From: Tomi Valkeinen <tomi.valkeinen@nokia.com>
To: linux-fbdev-devel@lists.sourceforge.net
Cc: linux-omap@vger.kernel.org
Subject: [REVIEW PATCH 5/9] DSS: DSI support for OMAP2/3 DSS
Date: Tue, 04 Nov 2008 18:10:19 +0200 [thread overview]
Message-ID: <20081104161019.19071.8831.stgit@tubuntu> (raw)
In-Reply-To: <20081104160946.19071.44903.stgit@tubuntu>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
---
arch/arm/plat-omap/dss/dsi.c | 3027 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 3027 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-omap/dss/dsi.c
diff --git a/arch/arm/plat-omap/dss/dsi.c b/arch/arm/plat-omap/dss/dsi.c
new file mode 100644
index 0000000..47e5628
--- /dev/null
+++ b/arch/arm/plat-omap/dss/dsi.c
@@ -0,0 +1,3027 @@
+/*
+ * linux/arch/arm/plat-omap/dss/dsi.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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 "DSI"
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+
+#include <mach/board.h>
+#include <mach/display.h>
+
+#include "dss.h"
+
+/*#define VERBOSE*/
+/*#define VERBOSE_IRQ*/
+/*#define MEASURE_PERF*/
+
+#define DSI_BASE 0x4804FC00
+
+struct dsi_reg { u16 idx; };
+
+#define DSI_REG(idx) ((const struct dsi_reg) { idx })
+
+/* DSI Protocol Engine */
+
+#define DSI_REVISION DSI_REG(0x0000)
+#define DSI_SYSCONFIG DSI_REG(0x0010)
+#define DSI_SYSSTATUS DSI_REG(0x0014)
+#define DSI_IRQSTATUS DSI_REG(0x0018)
+#define DSI_IRQENABLE DSI_REG(0x001C)
+#define DSI_CTRL DSI_REG(0x0040)
+#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048)
+#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C)
+#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050)
+#define DSI_CLK_CTRL DSI_REG(0x0054)
+#define DSI_TIMING1 DSI_REG(0x0058)
+#define DSI_TIMING2 DSI_REG(0x005C)
+#define DSI_VM_TIMING1 DSI_REG(0x0060)
+#define DSI_VM_TIMING2 DSI_REG(0x0064)
+#define DSI_VM_TIMING3 DSI_REG(0x0068)
+#define DSI_CLK_TIMING DSI_REG(0x006C)
+#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070)
+#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074)
+#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078)
+#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C)
+#define DSI_VM_TIMING4 DSI_REG(0x0080)
+#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084)
+#define DSI_VM_TIMING5 DSI_REG(0x0088)
+#define DSI_VM_TIMING6 DSI_REG(0x008C)
+#define DSI_VM_TIMING7 DSI_REG(0x0090)
+#define DSI_STOPCLK_TIMING DSI_REG(0x0094)
+#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20))
+#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20))
+#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20))
+#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20))
+#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20))
+#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20))
+#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20))
+
+/* DSIPHY_SCP */
+
+#define DSIPHY_CFG0 DSI_REG(0x200 + 0x0000)
+#define DSIPHY_CFG1 DSI_REG(0x200 + 0x0004)
+#define DSIPHY_CFG2 DSI_REG(0x200 + 0x0008)
+#define DSIPHY_CFG5 DSI_REG(0x200 + 0x0014)
+
+/* DSI_PLL_CTRL_SCP */
+
+#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000)
+#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004)
+#define DSI_PLL_GO DSI_REG(0x300 + 0x0008)
+#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C)
+#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010)
+
+#define REG_GET(idx, start, end) \
+ FLD_GET(dsi_read_reg(idx), start, end)
+
+#define REG_FLD_MOD(idx, val, start, end) \
+ dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end))
+
+/* Global interrupts */
+#define DSI_IRQ_VC0 (1 << 0)
+#define DSI_IRQ_VC1 (1 << 1)
+#define DSI_IRQ_VC2 (1 << 2)
+#define DSI_IRQ_VC3 (1 << 3)
+#define DSI_IRQ_WAKEUP (1 << 4)
+#define DSI_IRQ_RESYNC (1 << 5)
+#define DSI_IRQ_PLL_LOCK (1 << 7)
+#define DSI_IRQ_PLL_UNLOCK (1 << 8)
+#define DSI_IRQ_PLL_RECALL (1 << 9)
+#define DSI_IRQ_COMPLEXIO_ERR (1 << 10)
+#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14)
+#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15)
+#define DSI_IRQ_TE_TRIGGER (1 << 16)
+#define DSI_IRQ_ACK_TRIGGER (1 << 17)
+#define DSI_IRQ_SYNC_LOST (1 << 18)
+#define DSI_IRQ_LDO_POWER_GOOD (1 << 19)
+#define DSI_IRQ_TA_TIMEOUT (1 << 20)
+#define DSI_IRQ_ERROR_MASK \
+ (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
+ DSI_IRQ_TA_TIMEOUT)
+#define DSI_IRQ_CHANNEL_MASK 0xf
+
+/* Virtual channel interrupts */
+#define DSI_VC_IRQ_CS (1 << 0)
+#define DSI_VC_IRQ_ECC_CORR (1 << 1)
+#define DSI_VC_IRQ_PACKET_SENT (1 << 2)
+#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3)
+#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4)
+#define DSI_VC_IRQ_BTA (1 << 5)
+#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6)
+#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7)
+#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8)
+#define DSI_VC_IRQ_ERROR_MASK \
+ (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \
+ DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \
+ DSI_VC_IRQ_FIFO_TX_UDF)
+
+/* ComplexIO interrupts */
+#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0)
+#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1)
+#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2)
+#define DSI_CIO_IRQ_ERRESC1 (1 << 5)
+#define DSI_CIO_IRQ_ERRESC2 (1 << 6)
+#define DSI_CIO_IRQ_ERRESC3 (1 << 7)
+#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10)
+#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11)
+#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12)
+#define DSI_CIO_IRQ_STATEULPS1 (1 << 15)
+#define DSI_CIO_IRQ_STATEULPS2 (1 << 16)
+#define DSI_CIO_IRQ_STATEULPS3 (1 << 17)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
+#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
+#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
+
+#define DSI_DT_DCS_SHORT_WRITE_0 0x05
+#define DSI_DT_DCS_SHORT_WRITE_1 0x15
+#define DSI_DT_DCS_READ 0x06
+#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37
+#define DSI_DT_NULL_PACKET 0x09
+#define DSI_DT_DCS_LONG_WRITE 0x39
+
+#define DSI_DT_RX_ACK_WITH_ERR 0x02
+#define DSI_DT_RX_DCS_LONG_READ 0x1c
+#define DSI_DT_RX_SHORT_READ_1 0x21
+#define DSI_DT_RX_SHORT_READ_2 0x22
+
+#define FINT_MAX 2100000
+#define FINT_MIN 750000
+#define REGN_MAX (1 << 7)
+#define REGM_MAX ((1 << 11) - 1)
+#define REGM3_MAX (1 << 4)
+#define REGM4_MAX (1 << 4)
+
+enum fifo_size {
+ DSI_FIFO_SIZE_0 = 0,
+ DSI_FIFO_SIZE_32 = 1,
+ DSI_FIFO_SIZE_64 = 2,
+ DSI_FIFO_SIZE_96 = 3,
+ DSI_FIFO_SIZE_128 = 4,
+};
+
+static struct
+{
+ void __iomem *base;
+
+ struct clk *dss_ick;
+ struct clk *dss1_fck;
+ struct clk *dss2_fck;
+
+ unsigned long dsi1_pll_fclk; /* Hz */
+ unsigned long dsi2_pll_fclk; /* Hz */
+ unsigned long dsiphy; /* Hz */
+ unsigned long ddr_clk; /* Hz */
+
+ struct {
+ enum fifo_size fifo_size;
+ int dest_per; /* destination peripheral 0-3 */
+ } vc[4];
+
+ struct mutex lock;
+
+ struct completion bta_completion;
+
+ spinlock_t update_lock;
+ int update_ongoing;
+ int update_syncers;
+ struct completion update_completion;
+ struct work_struct framedone_work;
+
+ enum omap_dss_update_mode update_mode;
+ int use_te;
+ int framedone_scheduled; /* helps to catch strange framedone bugs */
+
+ struct {
+ struct omap_display *display;
+ int x, y, w, h;
+ int bytespp;
+ } update_region;
+
+#ifdef MEASURE_PERF
+ ktime_t measure_time;
+ int measure_frames;
+#endif
+} dsi;
+
+
+static inline void dsi_write_reg(const struct dsi_reg idx, u32 val)
+{
+ __raw_writel(val, dsi.base + idx.idx);
+}
+
+static inline u32 dsi_read_reg(const struct dsi_reg idx)
+{
+ return __raw_readl(dsi.base + idx.idx);
+}
+
+static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum,
+ int value)
+{
+ int t = 1000;
+
+ while (REG_GET(idx, bitnum, bitnum) != value) {
+ if (--t == 0)
+ return !value;
+ }
+
+ return value;
+}
+
+
+#ifdef MEASURE_PERF
+static void start_measuring(void)
+{
+ dsi.measure_time = ktime_get();
+}
+
+static void end_measuring(const char *name)
+{
+ ktime_t t;
+ u32 total_bytes;
+ u32 us;
+ const int numframes = 100;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED)
+ return;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+ dsi.measure_frames++;
+ if (dsi.measure_frames < numframes)
+ return;
+ dsi.measure_frames = 0;
+ }
+
+ t = ktime_get();
+ t = ktime_sub(t, dsi.measure_time);
+ us = (u32)ktime_to_us(t);
+ if (us == 0)
+ us = 1;
+
+ total_bytes = dsi.update_region.w *
+ dsi.update_region.h *
+ dsi.update_region.bytespp;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+ DSSINFO("%s update: %d frames in %u us, %u frames/sec\n",
+ name, numframes,
+ us,
+ 1000*1000 / us);
+ } else {
+ DSSINFO("%s update in %u us (%u Hz), %u bytes, %u kbytes/sec\n",
+ name,
+ us,
+ 1000*1000 / us,
+ total_bytes,
+ total_bytes * 1000 / us);
+ }
+}
+#else
+#define start_measuring()
+#define end_measuring(x)
+#endif
+
+
+
+
+static void print_irq_status(u32 status)
+{
+#ifndef VERBOSE_IRQ
+ if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0)
+ return;
+#endif
+ printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status);
+
+#define PIS(x) \
+ if (status & DSI_IRQ_##x) \
+ printk(#x " ");
+#ifdef VERBOSE_IRQ
+ PIS(VC0);
+ PIS(VC1);
+ PIS(VC2);
+ PIS(VC3);
+#endif
+ PIS(WAKEUP);
+ PIS(RESYNC);
+ PIS(PLL_LOCK);
+ PIS(PLL_UNLOCK);
+ PIS(PLL_RECALL);
+ PIS(COMPLEXIO_ERR);
+ PIS(HS_TX_TIMEOUT);
+ PIS(LP_RX_TIMEOUT);
+ PIS(TE_TRIGGER);
+ PIS(ACK_TRIGGER);
+ PIS(SYNC_LOST);
+ PIS(LDO_POWER_GOOD);
+ PIS(TA_TIMEOUT);
+#undef PIS
+
+ printk("\n");
+}
+
+static void print_irq_status_vc(int channel, u32 status)
+{
+#ifndef VERBOSE_IRQ
+ if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
+ return;
+#endif
+ printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status);
+
+#define PIS(x) \
+ if (status & DSI_VC_IRQ_##x) \
+ printk(#x " ");
+ PIS(CS);
+ PIS(ECC_CORR);
+#ifdef VERBOSE_IRQ
+ PIS(PACKET_SENT);
+#endif
+ PIS(FIFO_TX_OVF);
+ PIS(FIFO_RX_OVF);
+ PIS(BTA);
+ PIS(ECC_NO_CORR);
+ PIS(FIFO_TX_UDF);
+ PIS(PP_BUSY_CHANGE);
+#undef PIS
+ printk("\n");
+}
+
+static void print_irq_status_cio(u32 status)
+{
+ printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status);
+
+#define PIS(x) \
+ if (status & DSI_CIO_IRQ_##x) \
+ printk(#x " ");
+ PIS(ERRSYNCESC1);
+ PIS(ERRSYNCESC2);
+ PIS(ERRSYNCESC3);
+ PIS(ERRESC1);
+ PIS(ERRESC2);
+ PIS(ERRESC3);
+ PIS(ERRCONTROL1);
+ PIS(ERRCONTROL2);
+ PIS(ERRCONTROL3);
+ PIS(STATEULPS1);
+ PIS(STATEULPS2);
+ PIS(STATEULPS3);
+ PIS(ERRCONTENTIONLP0_1);
+ PIS(ERRCONTENTIONLP1_1);
+ PIS(ERRCONTENTIONLP0_2);
+ PIS(ERRCONTENTIONLP1_2);
+ PIS(ERRCONTENTIONLP0_3);
+ PIS(ERRCONTENTIONLP1_3);
+ PIS(ULPSACTIVENOT_ALL0);
+ PIS(ULPSACTIVENOT_ALL1);
+#undef PIS
+
+ printk("\n");
+}
+
+static int debug_irq;
+
+/* called from dss */
+void dsi_irq_handler(void)
+{
+ u32 irqstatus, vcstatus, ciostatus;
+ int i;
+
+ irqstatus = dsi_read_reg(DSI_IRQSTATUS);
+
+ if (irqstatus & DSI_IRQ_ERROR_MASK) {
+ DSSERR("DSI error, irqstatus %x\n", irqstatus);
+ print_irq_status(irqstatus);
+ } else if (debug_irq) {
+ print_irq_status(irqstatus);
+ }
+
+ for (i = 0; i < 4; ++i) {
+ if ((irqstatus & (1<<i)) == 0)
+ continue;
+
+ vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i));
+
+ if (vcstatus & DSI_VC_IRQ_BTA)
+ complete(&dsi.bta_completion);
+
+ if (vcstatus & DSI_VC_IRQ_ERROR_MASK) {
+ DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
+ i, vcstatus);
+ print_irq_status_vc(i, vcstatus);
+ } else if (debug_irq) {
+ print_irq_status_vc(i, vcstatus);
+ }
+
+ dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus);
+ }
+
+ if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
+ ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+
+ dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
+
+ DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
+ print_irq_status_cio(ciostatus);
+ }
+
+ dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
+}
+
+
+static void _dsi_initialize_irq(void)
+{
+ u32 l;
+ 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);
+
+ /* clear interrupt status */
+ l = dsi_read_reg(DSI_IRQSTATUS);
+ dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK);
+
+ for (i = 0; i < 4; ++i) {
+ l = dsi_read_reg(DSI_VC_IRQSTATUS(i));
+ dsi_write_reg(DSI_VC_IRQSTATUS(i), l);
+ }
+
+ l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+ dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l);
+
+ /* enable error irqs */
+ l = DSI_IRQ_ERROR_MASK;
+ dsi_write_reg(DSI_IRQENABLE, l);
+
+ l = DSI_VC_IRQ_ERROR_MASK;
+ for (i = 0; i < 4; ++i)
+ dsi_write_reg(DSI_VC_IRQENABLE(i), l);
+
+ /* XXX zonda responds incorrectly, causing control error:
+ Exit from LP-ESC mode to LP11 uses wrong transition states on the
+ data lines LP0 and LN0. */
+ dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE,
+ -1 & (~DSI_CIO_IRQ_ERRCONTROL2));
+}
+
+static void dsi_vc_enable_bta_irq(int channel)
+{
+ u32 l;
+
+ l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
+ l |= DSI_VC_IRQ_BTA;
+ dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+}
+
+static void dsi_vc_disable_bta_irq(int channel)
+{
+ u32 l;
+
+ l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
+ l &= ~DSI_VC_IRQ_BTA;
+ dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+}
+
+/* DSI func clock. this could also be DSI2_PLL_FCLK */
+static inline void enable_clocks(int enable)
+{
+ if (enable) {
+ clk_enable(dsi.dss_ick);
+ clk_enable(dsi.dss1_fck);
+ } else {
+ clk_disable(dsi.dss1_fck);
+ clk_disable(dsi.dss_ick);
+ }
+}
+
+/* source clock for DSI PLL. this could also be PCLKFREE */
+static inline void dsi_enable_pll_clock(int enable)
+{
+ if (enable)
+ clk_enable(dsi.dss2_fck);
+ else
+ clk_disable(dsi.dss2_fck);
+}
+
+#if 1
+
+#ifdef DEBUG
+static void _dsi_print_reset_status(void)
+{
+ u32 l;
+
+ /* A dummy read using the SCP interface to any DSIPHY register is
+ * required after DSIPHY reset to complete the reset of the DSI complex
+ * I/O. */
+ l = dsi_read_reg(DSIPHY_CFG5);
+
+ printk(KERN_DEBUG "DSI resets: ");
+
+ l = dsi_read_reg(DSI_PLL_STATUS);
+ printk("PLL (%d) ", FLD_GET(l, 0, 0));
+
+ l = dsi_read_reg(DSI_COMPLEXIO_CFG1);
+ printk("CIO (%d) ", FLD_GET(l, 29, 29));
+
+ l = dsi_read_reg(DSIPHY_CFG5);
+ printk("PHY (%x, %d, %d, %d)\n",
+ FLD_GET(l, 28, 26),
+ FLD_GET(l, 29, 29),
+ FLD_GET(l, 30, 30),
+ FLD_GET(l, 31, 31));
+}
+#else
+#define _dsi_print_reset_status()
+#endif
+
+static int _dsi_reset(void)
+{
+ int r = 0;
+
+ /* Soft reset */
+ REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1);
+
+ if (wait_for_bit_change(DSI_SYSSTATUS, 0, 1) != 1) {
+ DSSERR("soft reset failed\n");
+ r = -ENODEV;
+ }
+
+ /* A dummy read using the SCP interface to any DSIPHY register is
+ * required after DSIPHY reset to complete the reset of the DSI complex
+ * I/O. */
+ dsi_read_reg(DSIPHY_CFG5);
+
+ _dsi_print_reset_status();
+
+ return r;
+}
+#endif
+
+static inline int dsi_if_enable(int enable)
+{
+ DSSDBG("dsi_if_enable(%d)\n", enable);
+
+ enable = enable ? 1 : 0;
+ REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */
+
+ if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) {
+ DSSERR("Failed to set dsi_if_enable to %d\n", enable);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static unsigned long dsi_fclk_rate(void)
+{
+ unsigned long r;
+
+ if (dss_get_dsi_clk_source() == 0) {
+ /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */
+ r = clk_get_rate(dsi.dss1_fck);
+ } else {
+ /* DSI FCLK source is DSI2_PLL_FCLK */
+ r = dsi.dsi2_pll_fclk;
+ }
+
+ return r;
+}
+
+static int dsi_set_lp_clk_divisor(void)
+{
+ int n;
+ unsigned long dsi_fclk;
+ unsigned long mhz;
+
+ /* LP_CLK_DIVISOR, DSI fclk/n, should be 20MHz - 32kHz */
+
+ dsi_fclk = dsi_fclk_rate();
+
+ for (n = 1; n < (1 << 13) - 1; ++n) {
+ mhz = dsi_fclk / n;
+ if (mhz <= 20*1000*1000)
+ break;
+ }
+
+ if (n == (1 << 13) - 1) {
+ DSSERR("DSI: Failed to find LP_CLK_DIVISOR\n");
+ return -EINVAL;
+ }
+
+ DSSDBG("LP_CLK_DIV %d, LP_CLK %ld\n", n, mhz);
+
+ REG_FLD_MOD(DSI_CLK_CTRL, n, 12, 0); /* LP_CLK_DIVISOR */
+ if (dsi_fclk > 30*1000*1000)
+ REG_FLD_MOD(DSI_CLK_CTRL, 1, 21, 21); /* LP_RX_SYNCHRO_ENABLE */
+
+ return 0;
+}
+
+
+enum dsi_pll_power_state {
+ DSI_PLL_POWER_OFF = 0x0,
+ DSI_PLL_POWER_ON_HSCLK = 0x1,
+ DSI_PLL_POWER_ON_ALL = 0x2,
+ DSI_PLL_POWER_ON_DIV = 0x3,
+};
+
+static int dsi_pll_power(enum dsi_pll_power_state state)
+{
+ int t = 0;
+
+ REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */
+
+ /* PLL_PWR_STATUS */
+ while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) {
+ udelay(1);
+ if (t++ > 1000) {
+ DSSERR("DSI: Failed to set DSI PLL power mode to %d\n",
+ state);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+/* return 1 for exact match */
+static int iterate_dispc_divs(int is_tft, unsigned long pck,
+ struct dsi_clock_info *cur, struct dsi_clock_info *best)
+{
+ int pcd_min = is_tft ? 2 : 3;
+
+ for (cur->lck_div = 1; cur->lck_div <= 255; ++cur->lck_div) {
+ unsigned long lck = cur->dispc_fck / cur->lck_div;
+
+ for (cur->pck_div = pcd_min; cur->pck_div <= 255;
+ ++cur->pck_div) {
+
+ cur->pck = lck / cur->pck_div;
+
+ if (abs(cur->pck - pck) < abs(best->pck - pck)) {
+ *best = *cur;
+ /*
+ DSSDBG("best match fck %ld, pck %ld, regn %d, "
+ "regm %d, regm3 %d, ld %d, pd %d\n",
+ best->dispc_fck,
+ best->pck,
+ best->regn, best->regm,
+ best->regm3,
+ best->lck_div, best->pck_div);
+ */
+ }
+
+ if (cur->pck == pck)
+ return 1;
+
+ if (cur->pck < pck)
+ break;
+ }
+
+ if (lck / pcd_min < cur->pck)
+ break;
+ }
+
+ return 0;
+}
+
+int dsi_pll_calc_pck(int is_tft, unsigned long pck,
+ struct dsi_clock_info *cinfo)
+{
+ struct dsi_clock_info cur, best;
+
+ DSSDBG("dsi_pll_calc\n");
+
+ memset(&best, 0, sizeof(best));
+
+ memset(&cur, 0, sizeof(cur));
+ cur.clkin = clk_get_rate(dsi.dss2_fck);
+ cur.use_dss2_fck = 1;
+ cur.highfreq = 0;
+
+ /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */
+ /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */
+ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */
+ for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) {
+ if (cur.highfreq == 0)
+ cur.fint = cur.clkin / cur.regn;
+ else
+ cur.fint = cur.clkin / (2 * cur.regn);
+
+ if (cur.fint > FINT_MAX || cur.fint < FINT_MIN)
+ continue;
+
+ /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */
+ for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) {
+ unsigned long a, b;
+
+ a = 2 * cur.regm * (cur.clkin/1000);
+ b = cur.regn * (cur.highfreq + 1);
+ cur.dsiphy = a / b * 1000;
+
+ if (cur.dsiphy > 1800 * 1000 * 1000)
+ break;
+
+ /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */
+ for (cur.regm3 = 1; cur.regm3 < REGM3_MAX;
+ ++cur.regm3) {
+ int r;
+
+ cur.dispc_fck = cur.dsiphy / cur.regm3;
+
+ /* this will narrow down the search a bit,
+ * but still give pixclocks below what was
+ * requested */
+ if (cur.dispc_fck < pck)
+ break;
+
+ if (cur.dispc_fck > DISPC_MAX_FCK)
+ continue;
+
+#ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK
+ if (cur.dispc_fck <
+ pck * CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK)
+ continue;
+#endif
+ r = iterate_dispc_divs(is_tft, pck,
+ &cur, &best);
+ if (r == 1)
+ goto found;
+
+ }
+ }
+ }
+found:
+
+ /* DSI2_PLL_FCLK(MHz) = DSIPHY(MHz) / regm4 < 173MHz */
+ /* hardcoded 48MHz for now. what should it be? */
+ best.regm4 = best.dsiphy / (48000000);
+ if (best.regm4 > REGM4_MAX)
+ best.regm4 = REGM4_MAX;
+ best.dsi_fck = best.dsiphy / best.regm4;
+
+ *cinfo = best;
+
+ return 0;
+}
+
+static int dsi_pll_calc_datafreq(unsigned long datafreq,
+ struct dsi_clock_info *cinfo)
+{
+ struct dsi_clock_info cur, best;
+ const int use_dss2_fck = 1;
+
+ DSSDBG("dsi_pll_calc_datarate\n");
+
+ memset(&best, 0, sizeof(best));
+
+ memset(&cur, 0, sizeof(cur));
+ cur.use_dss2_fck = use_dss2_fck;
+ if (use_dss2_fck) {
+ cur.clkin = clk_get_rate(dsi.dss2_fck);
+ cur.highfreq = 0;
+ } else {
+ cur.clkin = dispc_pclk_rate();
+ if (cur.clkin < 32000000)
+ cur.highfreq = 0;
+ else
+ cur.highfreq = 1;
+ }
+
+ /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */
+ /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */
+ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */
+ for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) {
+ if (cur.highfreq == 0)
+ cur.fint = cur.clkin / cur.regn;
+ else
+ cur.fint = cur.clkin / (2 * cur.regn);
+
+ if (cur.fint > FINT_MAX || cur.fint < FINT_MIN)
+ continue;
+
+ /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */
+ for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) {
+ unsigned long a, b;
+
+ a = 2 * cur.regm * (cur.clkin/1000);
+ b = cur.regn * (cur.highfreq + 1);
+ cur.dsiphy = a / b * 1000;
+
+ if (cur.dsiphy > 1800 * 1000 * 1000)
+ break;
+
+ if (abs(cur.dsiphy - datafreq) <
+ abs(best.dsiphy - datafreq)) {
+ best = cur;
+ /* DSSDBG("best %ld\n", best.dsiphy); */
+ }
+
+ if (cur.dsiphy == datafreq)
+ goto found;
+ }
+ }
+found:
+ /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */
+ /* hardcoded 48MHz for now. what should it be? */
+ best.regm3 = best.dsiphy / (48000000);
+ if (best.regm3 > REGM3_MAX)
+ best.regm3 = REGM3_MAX;
+ best.dispc_fck = best.dsiphy / best.regm3;
+
+ /* DSI2_PLL_FCLK(MHz) = DSIPHY(MHz) / regm4 < 173MHz */
+ /* hardcoded 48MHz for now. what should it be? */
+ best.regm4 = best.dsiphy / (48000000);
+ if (best.regm4 > REGM4_MAX)
+ best.regm4 = REGM4_MAX;
+ best.dsi_fck = best.dsiphy / best.regm4;
+
+ *cinfo = best;
+
+ return 0;
+}
+
+int dsi_pll_program(struct dsi_clock_info *cinfo)
+{
+ int r = 0;
+ u32 l;
+
+ DSSDBG("dsi_pll_program\n");
+
+ enable_clocks(1);
+
+ dsi.dsiphy = cinfo->dsiphy;
+ dsi.ddr_clk = dsi.dsiphy / 4;
+ dsi.dsi1_pll_fclk = cinfo->dispc_fck;
+ dsi.dsi2_pll_fclk = cinfo->dsi_fck;
+
+ DSSDBG("DSI Fint %ld\n", cinfo->fint);
+
+ DSSDBG("clkin (%s) rate %ld, highfreq %d\n",
+ cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree",
+ cinfo->clkin,
+ cinfo->highfreq);
+
+ /* DSIPHY == CLKIN4DDR */
+ DSSDBG("DSIPHY = 2 * %d / %d * %lu / %d = %lu\n",
+ cinfo->regm,
+ cinfo->regn,
+ cinfo->clkin,
+ cinfo->highfreq + 1,
+ cinfo->dsiphy);
+
+ DSSDBG("Data rate on 1 DSI lane %ld Mbps\n",
+ dsi.dsiphy / 1000 / 1000 / 2);
+
+ DSSDBG("Clock lane freq %ld Hz\n", dsi.ddr_clk);
+
+ DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n",
+ cinfo->regm3, cinfo->dispc_fck);
+ DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n",
+ cinfo->regm4, cinfo->dsi_fck);
+
+ REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */
+
+ l = dsi_read_reg(DSI_PLL_CONFIGURATION1);
+ l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */
+ l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */
+ l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */
+ l = FLD_MOD(l, cinfo->regm3 - 1, 22, 19); /* DSI_CLOCK_DIV */
+ l = FLD_MOD(l, cinfo->regm4 - 1, 26, 23); /* DSIPROTO_CLOCK_DIV */
+ dsi_write_reg(DSI_PLL_CONFIGURATION1, l);
+
+ l = dsi_read_reg(DSI_PLL_CONFIGURATION2);
+ l = FLD_MOD(l, 7, 4, 1); /* DSI_PLL_FREQSEL */
+ /* DSI_PLL_CLKSEL */
+ l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, 11, 11);
+ l = FLD_MOD(l, cinfo->highfreq, 12, 12); /* DSI_PLL_HIGHFREQ */
+ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
+ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */
+ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */
+ dsi_write_reg(DSI_PLL_CONFIGURATION2, l);
+
+ REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */
+
+ if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) {
+ DSSERR("dsi pll go bit not going down.\n");
+ r = -EIO;
+ goto err;
+ }
+
+ if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) {
+ DSSERR("DSI: cannot lock PLL\n");
+ r = -EIO;
+ goto err;
+ }
+
+ l = dsi_read_reg(DSI_PLL_CONFIGURATION2);
+ l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */
+ l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */
+ l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */
+ l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */
+ l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */
+ l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */
+ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
+ l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */
+ l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */
+ l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */
+ l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */
+ l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */
+ l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */
+ l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */
+ dsi_write_reg(DSI_PLL_CONFIGURATION2, l);
+
+ DSSDBG("PLL config done\n");
+err:
+ enable_clocks(0);
+
+ return r;
+}
+
+int dsi_pll_init(int enable_hsclk, int enable_hsdiv)
+{
+ int r = 0;
+ int fck_div, lck_div, pck_div;
+ unsigned long fck;
+ enum dsi_pll_power_state pwstate;
+
+ DSSDBG("PLL init\n");
+
+ enable_clocks(1);
+ dsi_enable_pll_clock(1);
+
+ /* configure dispc fck and pixel clock to something sane */
+ fck = dispc_calc_clock_div(1, 48 * 1000 * 1000,
+ &fck_div, &lck_div, &pck_div);
+ if (fck == 0)
+ return -EINVAL;
+
+ dispc_set_clock_div(fck_div, lck_div, pck_div);
+
+ /* PLL does not come out of reset without this... */
+ dispc_pck_free_enable(1);
+
+ if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) {
+ DSSERR("DSI: PLL not coming out of reset.\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ /* ... but if left on, we get problems when planes do not
+ * fill the whole display. No idea about this XXX */
+ dispc_pck_free_enable(0);
+
+ if (enable_hsclk && enable_hsdiv)
+ pwstate = DSI_PLL_POWER_ON_ALL;
+ else if (enable_hsclk)
+ pwstate = DSI_PLL_POWER_ON_HSCLK;
+ else if (enable_hsdiv)
+ pwstate = DSI_PLL_POWER_ON_DIV;
+ else
+ pwstate = DSI_PLL_POWER_OFF;
+
+ r = dsi_pll_power(pwstate);
+
+ if (r)
+ goto err;
+
+ enable_clocks(0);
+
+ DSSDBG("PLL init done\n");
+
+ return 0;
+err:
+ enable_clocks(0);
+ dsi_enable_pll_clock(0);
+ return r;
+}
+
+void dsi_pll_uninit(void)
+{
+ dsi_pll_power(DSI_PLL_POWER_OFF);
+ dsi_enable_pll_clock(0);
+ DSSDBG("PLL uninit done\n");
+}
+
+unsigned long dsi_get_dsi1_pll_rate(void)
+{
+ return dsi.dsi1_pll_fclk;
+}
+
+unsigned long dsi_get_dsi2_pll_rate(void)
+{
+ return dsi.dsi2_pll_fclk;
+}
+
+ssize_t dsi_print_clocks(char *buf, ssize_t size)
+{
+ ssize_t l = 0;
+ int clksel;
+
+ enable_clocks(1);
+
+ clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11);
+
+ l += snprintf(buf + l, size - l, "- dsi -\n");
+
+ l += snprintf(buf + l, size - l, "dsi fclk source = %s\n",
+ dss_get_dsi_clk_source() == 0 ?
+ "dss1_alwon_fclk" : "dsi2_pll_fclk");
+
+ l += snprintf(buf + l, size - l, "dsi pll source = %s\n",
+ clksel == 0 ?
+ "dss2_alwon_fclk" : "pclkfree");
+
+ l += snprintf(buf + l, size - l,
+ "DSIPHY\t\t%lu\nDDR_CLK\t\t%lu\n",
+ dsi.dsiphy, dsi.ddr_clk);
+
+ l += snprintf(buf + l, size - l,
+ "dsi1_pll_fck\t%lu (%s)\n"
+ "dsi2_pll_fck\t%lu (%s)\n",
+ dsi.dsi1_pll_fclk,
+ dss_get_dispc_clk_source() == 0 ? "off" : "on",
+ dsi.dsi2_pll_fclk,
+ dss_get_dsi_clk_source() == 0 ? "off" : "on");
+
+ enable_clocks(0);
+
+ return l;
+}
+
+
+enum dsi_complexio_power_state {
+ DSI_COMPLEXIO_POWER_OFF = 0x0,
+ DSI_COMPLEXIO_POWER_ON = 0x1,
+ DSI_COMPLEXIO_POWER_ULPS = 0x2,
+};
+
+static int dsi_complexio_power(enum dsi_complexio_power_state state)
+{
+ int t = 0;
+
+ /* PWR_CMD */
+ REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27);
+
+ /* PWR_STATUS */
+ while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) {
+ udelay(1);
+ if (t++ > 1000) {
+ DSSERR("DSI: failed to set complexio power state to "
+ "%d\n", state);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static void dsi_complexio_config(struct omap_display *display)
+{
+ u32 r;
+
+ int clk_lane = display->hw_config.u.dsi.clk_lane;
+ int data1_lane = display->hw_config.u.dsi.data1_lane;
+ int data2_lane = display->hw_config.u.dsi.data2_lane;
+ int clk_pol = display->hw_config.u.dsi.clk_pol;
+ int data1_pol = display->hw_config.u.dsi.data1_pol;
+ int data2_pol = display->hw_config.u.dsi.data2_pol;
+
+ r = dsi_read_reg(DSI_COMPLEXIO_CFG1);
+ r = FLD_MOD(r, clk_lane, 2, 0);
+ r = FLD_MOD(r, clk_pol, 3, 3);
+ r = FLD_MOD(r, data1_lane, 6, 4);
+ r = FLD_MOD(r, data1_pol, 7, 7);
+ r = FLD_MOD(r, data2_lane, 10, 8);
+ r = FLD_MOD(r, data2_pol, 11, 11);
+ dsi_write_reg(DSI_COMPLEXIO_CFG1, r);
+
+ /* The configuration of the DSI complex I/O (number of data lanes,
+ position, differential order) should not be changed while
+ DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for
+ the hardware to take into account a new configuration of the complex
+ I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to
+ follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1,
+ then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set
+ DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the
+ DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the
+ DSI complex I/O configuration is unknown. */
+
+ /*
+ REG_FLD_MOD(DSI_CTRL, 1, 0, 0);
+ REG_FLD_MOD(DSI_CTRL, 0, 0, 0);
+ REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20);
+ REG_FLD_MOD(DSI_CTRL, 1, 0, 0);
+ */
+}
+
+static inline int ns2ddr(int ns)
+{
+ /* convert time in ns to ddr ticks, rounding up */
+ return (ns * (dsi.ddr_clk/1000/1000) + 999) / 1000;
+}
+
+static void dsi_complexio_timings(void)
+{
+ u32 r;
+ u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
+ u32 tlpx_half, tclk_trail, tclk_zero;
+ u32 tclk_prepare;
+
+ /* calculate timings */
+
+ /* 1 * DDR_CLK = 2 * UI */
+
+ /* min 40ns + 4*UI max 85ns + 6*UI */
+ ths_prepare = ns2ddr(59) + 2;
+
+ /* min 145ns + 10*UI */
+ ths_prepare_ths_zero = ns2ddr(145) + 5;
+
+ /* min max(8*UI, 60ns+4*UI) */
+ ths_trail = max(4, ns2ddr(60) + 2);
+
+ /* min 100ns */
+ ths_exit = ns2ddr(100);
+
+ /* tlpx min 50n */
+ tlpx_half = ns2ddr(25);
+
+ /* min 60ns */
+ tclk_trail = ns2ddr(60);
+
+ /* min 38ns, max 95ns */
+ tclk_prepare = ns2ddr(38);
+
+ /* min tclk-prepare + tclk-zero = 300ns */
+ tclk_zero = ns2ddr(300 - 38);
+
+#ifdef VERBOSE
+ DSSDBG("ths_prepare %d, ths_prepare_ths_zero %d\n",
+ ths_prepare, ths_prepare_ths_zero);
+ DSSDBG("ths_trail %d, ths_exit %d\n", ths_trail, ths_exit);
+
+
+ DSSDBG("tlpx_half %d, tclk_trail %d, tclk_zero %d\n", tlpx_half,
+ tclk_trail, tclk_zero);
+ DSSDBG("tclk_prepare %d\n", tclk_prepare);
+#endif
+
+ /* program timings */
+
+ r = dsi_read_reg(DSIPHY_CFG0);
+ r = FLD_MOD(r, ths_prepare, 31, 24);
+ r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
+ r = FLD_MOD(r, ths_trail, 15, 8);
+ r = FLD_MOD(r, ths_exit, 7, 0);
+ dsi_write_reg(DSIPHY_CFG0, r);
+
+ r = dsi_read_reg(DSIPHY_CFG1);
+ r = FLD_MOD(r, tlpx_half, 22, 16);
+ r = FLD_MOD(r, tclk_trail, 15, 8);
+ r = FLD_MOD(r, tclk_zero, 7, 0);
+ dsi_write_reg(DSIPHY_CFG1, r);
+
+ r = dsi_read_reg(DSIPHY_CFG2);
+ r = FLD_MOD(r, tclk_prepare, 7, 0);
+ dsi_write_reg(DSIPHY_CFG2, r);
+}
+
+
+static int dsi_complexio_init(struct omap_display *display)
+{
+ int r = 0;
+
+ DSSDBG("dsi_complexio_init\n");
+
+ /* CIO_CLK_ICG, enable L3 clk to CIO */
+ REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14);
+
+ if (wait_for_bit_change(DSIPHY_CFG5, 30, 1) != 1) {
+ DSSERR("DSI: ComplexIO PHY not coming out of reset.\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ dsi_complexio_config(display);
+
+ r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON);
+
+ if (r)
+ goto err;
+
+ if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) {
+ DSSERR("DSI: ComplexIO not coming out of reset.\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) {
+ DSSERR("DSI: ComplexIO LDO power down.\n");
+ r = -ENODEV;
+ goto err;
+ }
+
+ dsi_complexio_timings();
+
+ /*
+ The configuration of the DSI complex I/O (number of data lanes,
+ position, differential order) should not be changed while
+ DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the
+ hardware to recognize a new configuration of the complex I/O (done
+ in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow
+ this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next
+ reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20]
+ LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN
+ bit to 1. If the sequence is not followed, the DSi complex I/O
+ configuration is undetermined.
+ */
+ dsi_if_enable(1);
+ dsi_if_enable(0);
+ REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
+ dsi_if_enable(1);
+ dsi_if_enable(0);
+
+ DSSDBG("CIO init done\n");
+err:
+ return r;
+}
+
+static void dsi_complexio_uninit(void)
+{
+ dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF);
+}
+
+
+
+static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2,
+ enum fifo_size size3, enum fifo_size size4)
+{
+ u32 r = 0;
+ int add = 0;
+ int i;
+
+ dsi.vc[0].fifo_size = size1;
+ dsi.vc[1].fifo_size = size2;
+ dsi.vc[2].fifo_size = size3;
+ dsi.vc[3].fifo_size = size4;
+
+ for (i = 0; i < 4; i++) {
+ u8 v;
+ int size = dsi.vc[i].fifo_size;
+
+ if (add + size > 4) {
+ DSSERR("DSI: Illegal FIFO configuration\n");
+ BUG();
+ }
+
+ v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
+ r |= v << (8 * i);
+ /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
+ add += size;
+ }
+
+ dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r);
+}
+
+static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2,
+ enum fifo_size size3, enum fifo_size size4)
+{
+ u32 r = 0;
+ int add = 0;
+ int i;
+
+ dsi.vc[0].fifo_size = size1;
+ dsi.vc[1].fifo_size = size2;
+ dsi.vc[2].fifo_size = size3;
+ dsi.vc[3].fifo_size = size4;
+
+ for (i = 0; i < 4; i++) {
+ u8 v;
+ int size = dsi.vc[i].fifo_size;
+
+ if (add + size > 4) {
+ DSSERR("DSI: Illegal FIFO configuration\n");
+ BUG();
+ }
+
+ v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
+ r |= v << (8 * i);
+ /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
+ add += size;
+ }
+
+ dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r);
+}
+
+static int dsi_force_tx_stop_mode_io(void)
+{
+ u32 r;
+
+ r = dsi_read_reg(DSI_TIMING1);
+ r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
+ dsi_write_reg(DSI_TIMING1, r);
+
+ if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) {
+ DSSERR("TX_STOP bit not going down\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void dsi_vc_print_status(int channel)
+{
+ u32 r;
+
+ r = dsi_read_reg(DSI_VC_CTRL(channel));
+ DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, "
+ "TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ",
+ channel,
+ FLD_GET(r, 5, 5),
+ FLD_GET(r, 6, 6),
+ FLD_GET(r, 15, 15),
+ FLD_GET(r, 16, 16),
+ FLD_GET(r, 20, 20));
+
+ r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS);
+ DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff);
+}
+
+static void dsi_vc_config(int channel)
+{
+ u32 r;
+
+ DSSDBG("dsi_vc_config %d\n", channel);
+
+ r = dsi_read_reg(DSI_VC_CTRL(channel));
+
+ r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
+ r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */
+ r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
+ r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
+ r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
+ r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
+ r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
+
+ r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
+ r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
+
+ dsi_write_reg(DSI_VC_CTRL(channel), r);
+}
+
+static void dsi_vc_config_vp(int channel)
+{
+ u32 r;
+
+ DSSDBG("dsi_vc_config_vp\n");
+
+ r = dsi_read_reg(DSI_VC_CTRL(channel));
+
+ r = FLD_MOD(r, 1, 1, 1); /* SOURCE, 1 = video port */
+ r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */
+ r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
+ r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
+ r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
+ r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
+ r = FLD_MOD(r, 1, 9, 9); /* MODE_SPEED, high speed on/off */
+
+ r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
+ r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
+
+ dsi_write_reg(DSI_VC_CTRL(channel), r);
+}
+
+
+static int dsi_vc_enable(int channel, int enable)
+{
+ DSSDBG("dsi_vc_enable channel %d, enable %d\n", channel, enable);
+
+ enable = enable ? 1 : 0;
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0);
+
+ if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) {
+ DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void dsi_vc_enable_hs(int channel, int enable)
+{
+ DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
+
+ dsi_vc_enable(channel, 0);
+ dsi_if_enable(0);
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9);
+
+ dsi_vc_enable(channel, 1);
+ dsi_if_enable(1);
+
+ dsi_force_tx_stop_mode_io();
+}
+
+static void dsi_vc_flush_long_data(int channel)
+{
+ while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+ u32 val;
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
+ (val >> 0) & 0xff,
+ (val >> 8) & 0xff,
+ (val >> 16) & 0xff,
+ (val >> 24) & 0xff);
+ }
+}
+
+static u16 dsi_vc_flush_receive_data(int channel)
+{
+ /* RX_FIFO_NOT_EMPTY */
+ while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+ u32 val;
+ u8 dt;
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ DSSDBG("\trawval %#08x\n", val);
+ dt = FLD_GET(val, 7, 0);
+ if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+ u16 err = FLD_GET(val, 23, 8);
+ DSSERR("\tACK with ERROR: %#x\n", err);
+ if (err & (1 << 9))
+ DSSERR("\t\tECC multibit\n");
+ if (err & (1 << 11))
+ DSSERR("\t\tData type not recognized\n");
+ if (err & (1 << 12))
+ DSSERR("\t\tInvalid VC ID\n");
+
+ } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+ DSSDBG("\tDCS short response, 1 byte: %#x\n",
+ FLD_GET(val, 23, 8));
+ return FLD_GET(val, 23, 8);
+ } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+ DSSDBG("\tDCS short response, 2 byte: %#x\n",
+ FLD_GET(val, 23, 8));
+ return FLD_GET(val, 23, 8);
+ } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+ DSSDBG("\tDCS long response, len %d\n",
+ FLD_GET(val, 23, 8));
+ dsi_vc_flush_long_data(channel);
+ } else {
+ DSSERR("\tunknown datatype\n");
+ }
+ }
+ return 0;
+}
+
+static int dsi_vc_send_bta(int channel)
+{
+ unsigned long tmo;
+
+ /*DSSDBG("dsi_vc_send_bta_sync %d\n", channel); */
+
+ if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */
+ DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
+ dsi_vc_flush_receive_data(channel);
+ }
+
+ REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
+
+ tmo = jiffies + msecs_to_jiffies(10);
+ while (REG_GET(DSI_VC_CTRL(channel), 6, 6) == 1) {
+ if (time_after(jiffies, tmo)) {
+ DSSERR("Failed to send BTA\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int dsi_vc_send_bta_sync(int channel)
+{
+ int r = 0;
+
+ init_completion(&dsi.bta_completion);
+
+ dsi_vc_enable_bta_irq(channel);
+
+ r = dsi_vc_send_bta(channel);
+ if (r)
+ goto err;
+
+ if (wait_for_completion_timeout(&dsi.bta_completion,
+ msecs_to_jiffies(500)) == 0) {
+ DSSERR("Failed to receive BTA\n");
+ r = -EIO;
+ goto err;
+ }
+err:
+ dsi_vc_disable_bta_irq(channel);
+
+ return r;
+}
+
+static inline void dsi_vc_write_long_header(int channel, u8 data_type,
+ u16 len, u8 ecc)
+{
+ u32 val;
+ u8 data_id;
+
+ /*data_id = data_type | channel << 6; */
+ data_id = data_type | dsi.vc[channel].dest_per << 6;
+
+ val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
+ FLD_VAL(ecc, 31, 24);
+
+ dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val);
+}
+
+static inline void dsi_vc_write_long_payload(int channel,
+ u8 b1, u8 b2, u8 b3, u8 b4)
+{
+ u32 val;
+
+ val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0;
+
+/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
+ b1, b2, b3, b4, val); */
+
+ dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val);
+}
+
+static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len,
+ u8 ecc)
+{
+ /*u32 val; */
+ int i;
+ u8 *p;
+ int r = 0;
+ u8 b1, b2, b3, b4;
+
+ /*DSSDBG("dsi_vc_send_long, %d bytes\n", len); */
+
+ /* len + header */
+ if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) {
+ DSSERR("DSI: unable to send long packet: packet too long.\n");
+ return -EINVAL;
+ }
+
+ dsi_vc_write_long_header(channel, data_type, len, ecc);
+
+ /*dsi_vc_print_status(0); */
+
+ p = data;
+ for (i = 0; i < len >> 2; i++) {
+ /*DSSDBG("\tsending full packet %d\n", i); */
+ /*dsi_vc_print_status(0); */
+
+ b1 = *p++;
+ b2 = *p++;
+ b3 = *p++;
+ b4 = *p++;
+
+ dsi_vc_write_long_payload(channel, b1, b2, b3, b4);
+ }
+
+ i = len % 4;
+ if (i) {
+ b1 = 0; b2 = 0; b3 = 0;
+
+ /*DSSDBG("\tsending remainder bytes %d\n", i); */
+
+ switch (i) {
+ case 3:
+ b1 = *p++;
+ b2 = *p++;
+ b3 = *p++;
+ break;
+ case 2:
+ b1 = *p++;
+ b2 = *p++;
+ break;
+ case 1:
+ b1 = *p++;
+ break;
+ }
+
+ dsi_vc_write_long_payload(channel, b1, b2, b3, 0);
+ }
+
+ return r;
+}
+
+static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc)
+{
+ u32 r;
+ u8 data_id;
+/*
+ DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
+ channel,
+ data_type, data & 0xff, (data >> 8) & 0xff);
+*/
+ if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) {
+ DSSERR("ERROR FIFO FULL, aborting transfer\n");
+ return -EINVAL;
+ }
+
+ data_id = data_type | channel << 6;
+
+ r = (data_id << 0) | (data << 8) | (ecc << 24);
+
+ dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r);
+
+ return 0;
+}
+
+int dsi_vc_send_null(int channel)
+{
+ u8 nullpkg[] = {0, 0, 0, 0};
+ return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0);
+}
+EXPORT_SYMBOL(dsi_vc_send_null);
+
+int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len)
+{
+ int r;
+
+ BUG_ON(len == 0);
+
+ if (len == 1) {
+ r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0,
+ data[0], 0);
+ } else if (len == 2) {
+ r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1,
+ data[0] | (data[1] << 8), 0);
+ } else {
+ /* 0x39 = DCS Long Write */
+ r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE,
+ data, len, 0);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(dsi_vc_dcs_write_nosync);
+
+int dsi_vc_dcs_write(int channel, u8 *data, int len)
+{
+ int r;
+
+ r = dsi_vc_dcs_write_nosync(channel, data, len);
+ if (r)
+ return r;
+
+ /* Some devices need time to process the msg in low power mode.
+ This also makes the write synchronous, and checks that
+ the peripheral is still alive */
+ r = dsi_vc_send_bta_sync(channel);
+
+ return r;
+}
+EXPORT_SYMBOL(dsi_vc_dcs_write);
+
+int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
+{
+ u32 val;
+ u8 dt;
+ int debug = 0;
+
+ if (debug)
+ DSSDBG("dsi_vc_dcs_read\n");
+
+ dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0);
+
+ dsi_vc_send_bta_sync(channel);
+
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ if (debug)
+ DSSDBG("\trawval %#08x\n", val);
+ dt = FLD_GET(val, 7, 0);
+ if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+ u16 err = FLD_GET(val, 23, 8);
+ DSSERR("\tACK with ERROR: %#x\n", err);
+ if (err & (1 << 9))
+ DSSERR("\t\tECC multibit\n");
+ if (err & (1 << 11))
+ DSSERR("\t\tData type not recognized\n");
+ if (err & (1 << 12))
+ DSSERR("\t\tInvalid VC ID\n");
+ return -1;
+
+ } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+ u8 data = FLD_GET(val, 15, 8);
+ if (debug)
+ DSSDBG("\tDCS short response, 1 byte: %#x\n", data);
+
+ if (buflen < 1)
+ return -1;
+
+ buf[0] = data;
+
+ return 1;
+ } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+ u16 data = FLD_GET(val, 23, 8);
+ if (debug)
+ DSSDBG("\tDCS short response, 2 byte: %#x\n", data);
+
+ if (buflen < 2)
+ return -1;
+
+ buf[0] = data & 0xff;
+ buf[1] = (data >> 8) & 0xff;
+
+ return 2;
+ } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+ int x;
+ int len = FLD_GET(val, 23, 8);
+ if (debug)
+ DSSDBG("\tDCS long response, len %d\n", len);
+
+ if (len > buflen)
+ return -1;
+
+ x = 0;
+ while (x < len) {
+ val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+ if (debug)
+ DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 "
+ "%#02x\n",
+ (val >> 0) & 0xff,
+ (val >> 8) & 0xff,
+ (val >> 16) & 0xff,
+ (val >> 24) & 0xff);
+
+ if (x < len)
+ buf[x++] = (val >> 0) & 0xff;
+ if (x < len)
+ buf[x++] = (val >> 8) & 0xff;
+ if (x < len)
+ buf[x++] = (val >> 16) & 0xff;
+ if (x < len)
+ buf[x++] = (val >> 24) & 0xff;
+ }
+
+ return len;
+ } else {
+ DSSERR("\tunknown datatype\n");
+ return -1;
+ }
+}
+EXPORT_SYMBOL(dsi_vc_dcs_read);
+
+
+int dsi_vc_set_max_rx_packet_size(int channel, u16 len)
+{
+ return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
+ len, 0);
+}
+EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
+
+
+static int dsi_set_lp_rx_timeout(int ns, int x4, int x16)
+{
+ u32 r;
+ unsigned long fck;
+ int ticks;
+
+ /* ticks in DSI_FCK */
+
+ fck = dsi_fclk_rate();
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+
+ if (ticks > 0x1fff) {
+ DSSERR("LP_TX_TO too high\n");
+ return -EINVAL;
+ }
+
+ r = dsi_read_reg(DSI_TIMING2);
+ r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
+ r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */
+ r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */
+ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
+ dsi_write_reg(DSI_TIMING2, r);
+
+ DSSDBG("LP_RX_TO %ld ns (%#x ticks)\n",
+ (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks);
+
+ return 0;
+}
+
+static int dsi_set_ta_timeout(int ns, int x8, int x16)
+{
+ u32 r;
+ unsigned long fck;
+ int ticks;
+
+ /* ticks in DSI_FCK */
+
+ fck = dsi_fclk_rate();
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+
+ if (ticks > 0x1fff) {
+ DSSERR("TA_TO too high\n");
+ return -EINVAL;
+ }
+
+ r = dsi_read_reg(DSI_TIMING1);
+ r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
+ r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */
+ r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */
+ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
+ dsi_write_reg(DSI_TIMING1, r);
+
+ DSSDBG("TA_TO %ld ns (%#x ticks)\n",
+ (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks);
+
+ return 0;
+}
+
+static int dsi_set_stop_state_counter(int ns, int x4, int x16)
+{
+ u32 r;
+ unsigned long fck;
+ int ticks;
+
+ /* ticks in DSI_FCK */
+
+ fck = dsi_fclk_rate();
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+
+ if (ticks > 0x1fff) {
+ DSSERR("STOP_STATE_COUNTER_IO too high\n");
+ return -EINVAL;
+ }
+
+ r = dsi_read_reg(DSI_TIMING1);
+ r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
+ r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */
+ r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */
+ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
+ dsi_write_reg(DSI_TIMING1, r);
+
+ DSSDBG("STOP_STATE_COUNTER %ld ns (%#x ticks)\n",
+ (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks);
+
+ return 0;
+}
+
+static int dsi_set_hs_tx_timeout(int ns, int x4, int x16)
+{
+ u32 r;
+ unsigned long fck;
+ int ticks;
+
+ /* ticks in TxByteClkHS */
+
+ fck = dsi.ddr_clk / 4;
+ ticks = (fck / 1000 / 1000) * ns / 1000;
+
+ if (ticks > 0x1fff) {
+ DSSERR("HS_TX_TO too high\n");
+ return -EINVAL;
+ }
+
+ r = dsi_read_reg(DSI_TIMING2);
+ r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
+ r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */
+ r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */
+ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
+ dsi_write_reg(DSI_TIMING2, r);
+
+ DSSDBG("HS_TX_TO %ld ns (%#x ticks)\n",
+ (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+ (fck / 1000 / 1000),
+ ticks);
+
+ return 0;
+}
+static int dsi_proto_config(struct omap_display *display)
+{
+ u32 r;
+ int buswidth = 0;
+
+ dsi_config_tx_fifo(DSI_FIFO_SIZE_128,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0);
+
+ dsi_config_rx_fifo(DSI_FIFO_SIZE_128,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0,
+ DSI_FIFO_SIZE_0);
+
+ /* XXX what values for the timeouts? */
+ dsi_set_stop_state_counter(1000, 0, 0);
+
+ dsi_set_ta_timeout(50000, 1, 1);
+
+ /* 3000ns * 16 */
+ dsi_set_lp_rx_timeout(3000, 0, 1);
+
+ /* 10000ns * 4 */
+ dsi_set_hs_tx_timeout(10000, 1, 0);
+
+ switch (display->bpp) {
+ case 16:
+ buswidth = 0;
+ break;
+ case 18:
+ buswidth = 1;
+ break;
+ case 24:
+ buswidth = 2;
+ break;
+ default:
+ BUG();
+ }
+
+ r = dsi_read_reg(DSI_CTRL);
+ r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */
+ r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */
+ r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */
+ /* XXX what should the ratio be */
+ r = FLD_MOD(r, 0, 4, 4); /* VP_CLK_RATIO, VP_PCLK = VP_CLK/2 */
+ r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
+ r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
+ r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */
+ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
+ r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
+ r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */
+ r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */
+
+ dsi_write_reg(DSI_CTRL, r);
+
+ /* we configure vc0 for L4 communication, and
+ * vc1 for dispc */
+ dsi_vc_config(0);
+ dsi_vc_config_vp(1);
+
+ /* set all vc targets to peripheral 0 */
+ dsi.vc[0].dest_per = 0;
+ dsi.vc[1].dest_per = 0;
+ dsi.vc[2].dest_per = 0;
+ dsi.vc[3].dest_per = 0;
+
+ return 0;
+}
+
+static void dsi_proto_timings(void)
+{
+ int tlpx_half, tclk_zero, tclk_prepare, tclk_trail;
+ int tclk_pre, tclk_post;
+ int ddr_clk_pre, ddr_clk_post;
+ u32 r;
+
+ r = dsi_read_reg(DSIPHY_CFG1);
+ tlpx_half = FLD_GET(r, 22, 16);
+ tclk_trail = FLD_GET(r, 15, 8);
+ tclk_zero = FLD_GET(r, 7, 0);
+
+ r = dsi_read_reg(DSIPHY_CFG2);
+ tclk_prepare = FLD_GET(r, 7, 0);
+
+ /* min 8*UI */
+ tclk_pre = 4;
+ /* min 60ns + 52*UI */
+ tclk_post = ns2ddr(60) + 26;
+
+ ddr_clk_pre = (tclk_pre + tlpx_half*2 + tclk_zero + tclk_prepare) / 4;
+ ddr_clk_post = (tclk_post + tclk_trail) / 4;
+
+ r = dsi_read_reg(DSI_CLK_TIMING);
+ r = FLD_MOD(r, ddr_clk_pre, 15, 8);
+ r = FLD_MOD(r, ddr_clk_post, 7, 0);
+ dsi_write_reg(DSI_CLK_TIMING, r);
+
+#ifdef VERBOSE
+ DSSDBG("ddr_clk_pre %d, ddr_clk_post %d\n",
+ ddr_clk_pre,
+ ddr_clk_post);
+#endif
+}
+
+
+#define DSI_DECL_VARS \
+ int __dsi_cb = 0; u32 __dsi_cv = 0;
+
+#define DSI_FLUSH(ch) \
+ if (__dsi_cb > 0) { \
+ /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \
+ dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \
+ __dsi_cb = __dsi_cv = 0; \
+ }
+
+#define DSI_PUSH(ch, data) \
+ do { \
+ __dsi_cv |= (data) << (__dsi_cb * 8); \
+ /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \
+ if (++__dsi_cb > 3) \
+ DSI_FLUSH(ch); \
+ } while (0)
+
+static int dsi_update_screen_l4(struct omap_display *display,
+ int x, int y, int w, int h)
+{
+ /* Note: supports only 24bit colors in 32bit container */
+ int first = 1;
+ int fifo_stalls = 0;
+ int max_dsi_packet_size;
+ int max_data_per_packet;
+ int max_pixels_per_packet;
+ int pixels_left;
+ int bytespp = 3;
+ int scr_width;
+ u32 *data;
+ int start_offset;
+ int horiz_inc;
+ int current_x;
+ struct omap_overlay *ovl;
+
+ debug_irq = 0;
+
+ DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n",
+ x, y, w, h);
+
+ ovl = &display->manager->overlays[0];
+
+ if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U)
+ return -EINVAL;
+
+ if (display->ctrl->bpp != 24)
+ return -EINVAL;
+
+ enable_clocks(1);
+
+ scr_width = ovl->info.screen_width;
+ data = ovl->info.vaddr;
+
+ start_offset = scr_width * y + x;
+ horiz_inc = scr_width - w;
+ current_x = x;
+
+ /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes
+ * in fifo */
+
+ /* When using CPU, max long packet size is TX buffer size */
+ max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4;
+
+ /* we seem to get better perf if we divide the tx fifo to half,
+ and while the other half is being sent, we fill the other half
+ max_dsi_packet_size /= 2; */
+
+ max_data_per_packet = max_dsi_packet_size - 4 - 1;
+
+ max_pixels_per_packet = max_data_per_packet / bytespp;
+
+ DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet);
+
+ display->ctrl->setup_update(display, x, y, w, h);
+
+ pixels_left = w * h;
+
+ DSSDBG("total pixels %d\n", pixels_left);
+
+ data += start_offset;
+
+ dsi.update_region.x = x;
+ dsi.update_region.y = y;
+ dsi.update_region.w = w;
+ dsi.update_region.h = h;
+ dsi.update_region.bytespp = bytespp;
+
+ start_measuring();
+
+ while (pixels_left > 0) {
+ /* 0x2c = write_memory_start */
+ /* 0x3c = write_memory_continue */
+ u8 dcs_cmd = first ? 0x2c : 0x3c;
+ int pixels;
+ DSI_DECL_VARS;
+ first = 0;
+
+#if 1
+ /* using fifo not empty */
+ /* TX_FIFO_NOT_EMPTY */
+ while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) {
+ udelay(1);
+ fifo_stalls++;
+ if (fifo_stalls > 0xfffff) {
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+ enable_clocks(0);
+ return -EIO;
+ }
+ }
+#elif 1
+ /* using fifo emptiness */
+ while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 <
+ max_dsi_packet_size) {
+ fifo_stalls++;
+ if (fifo_stalls > 0xfffff) {
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+ enable_clocks(0);
+ return -EIO;
+ }
+ }
+#else
+ while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) {
+ fifo_stalls++;
+ if (fifo_stalls > 0xfffff) {
+ DSSERR("fifo stalls overflow, pixels left %d\n",
+ pixels_left);
+ dsi_if_enable(0);
+ enable_clocks(0);
+ return -EIO;
+ }
+ }
+#endif
+ pixels = min(max_pixels_per_packet, pixels_left);
+
+ pixels_left -= pixels;
+
+ dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE,
+ 1 + pixels * bytespp, 0);
+
+ DSI_PUSH(0, dcs_cmd);
+
+ while (pixels-- > 0) {
+ u32 pix = *data++;
+
+ DSI_PUSH(0, (pix >> 16) & 0xff);
+ DSI_PUSH(0, (pix >> 8) & 0xff);
+ DSI_PUSH(0, (pix >> 0) & 0xff);
+
+ current_x++;
+ if (current_x == x+w) {
+ current_x = x;
+ data += horiz_inc;
+ }
+ }
+
+ DSI_FLUSH(0);
+ }
+
+ end_measuring("L4");
+
+ enable_clocks(0);
+
+ return 0;
+}
+
+#if 0
+static void dsi_clear_screen_l4(struct omap_display *display,
+ int x, int y, int w, int h)
+{
+ int first = 1;
+ int fifo_stalls = 0;
+ int max_dsi_packet_size;
+ int max_data_per_packet;
+ int max_pixels_per_packet;
+ int pixels_left;
+ int bytespp = 3;
+ int pixnum;
+
+ debug_irq = 0;
+
+ DSSDBG("dsi_clear_screen_l4 (%d,%d %dx%d)\n",
+ x, y, w, h);
+
+ if (display->ctrl->bpp != 24)
+ return -EINVAL;
+
+ /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp)
+ * bytes in fifo */
+
+ /* When using CPU, max long packet size is TX buffer size */
+ max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4;
+
+ max_data_per_packet = max_dsi_packet_size - 4 - 1;
+
+ max_pixels_per_packet = max_data_per_packet / bytespp;
+
+ enable_clocks(1);
+
+ display->ctrl->setup_update(display, x, y, w, h);
+
+ pixels_left = w * h;
+
+ dsi.update_region.x = x;
+ dsi.update_region.y = y;
+ dsi.update_region.w = w;
+ dsi.update_region.h = h;
+ dsi.update_region.bytespp = bytespp;
+
+ start_measuring();
+
+ pixnum = 0;
+
+ while (pixels_left > 0) {
+ /* 0x2c = write_memory_start */
+ /* 0x3c = write_memory_continue */
+ u8 dcs_cmd = first ? 0x2c : 0x3c;
+ int pixels;
+ DSI_DECL_VARS;
+ first = 0;
+
+ /* TX_FIFO_NOT_EMPTY */
+ while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) {
+ fifo_stalls++;
+ if (fifo_stalls > 0xfffff) {
+ DSSERR("fifo stalls overflow\n");
+ dsi_if_enable(0);
+ enable_clocks(0);
+ return;
+ }
+ }
+
+ pixels = min(max_pixels_per_packet, pixels_left);
+
+ pixels_left -= pixels;
+
+ dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE,
+ 1 + pixels * bytespp, 0);
+
+ DSI_PUSH(0, dcs_cmd);
+
+ while (pixels-- > 0) {
+ u32 pix;
+
+ pix = 0x000000;
+
+ DSI_PUSH(0, (pix >> 16) & 0xff);
+ DSI_PUSH(0, (pix >> 8) & 0xff);
+ DSI_PUSH(0, (pix >> 0) & 0xff);
+ }
+
+ DSI_FLUSH(0);
+ }
+
+ enable_clocks(0);
+
+ end_measuring("L4 CLEAR");
+}
+#endif
+
+static int dsi_wait_for_framedone(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsi.update_lock, flags);
+ if (dsi.update_ongoing) {
+ long wait = msecs_to_jiffies(1000);
+ dsi.update_syncers++;
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+ wait = wait_for_completion_timeout(&dsi.update_completion,
+ wait);
+ if (wait == 0) {
+ DSSERR("timeout waiting sync\n");
+ return -ETIME;
+ }
+ } else {
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+ }
+
+ return 0;
+}
+
+static void dsi_setup_update_dispc(struct omap_display *display,
+ int x, int y, int w, int h)
+{
+ int bytespp = 3;
+
+ DSSDBG("dsi_setup_update_dispc(%d,%d %dx%d)\n",
+ x, y, w, h);
+
+ dsi.update_region.display = display;
+ dsi.update_region.x = x;
+ dsi.update_region.y = y;
+ dsi.update_region.w = w;
+ dsi.update_region.h = h;
+ dsi.update_region.bytespp = bytespp;
+
+ enable_clocks(1);
+
+ dispc_setup_partial_planes(display, &x, &y, &w, &h);
+
+ dispc_set_lcd_size(w, h);
+
+ enable_clocks(0);
+}
+
+static void dsi_update_screen_dispc(struct omap_display *display)
+{
+ int bytespp = 3;
+ int total_len;
+ int line_packet_len;
+ int x, y, w, h;
+ u32 l;
+
+ x = dsi.update_region.x;
+ y = dsi.update_region.y;
+ w = dsi.update_region.w;
+ h = dsi.update_region.h;
+
+ DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
+ x, y, w, h);
+
+ enable_clocks(1);
+
+ /* TODO: one packet could be longer, I think? Max is the line buffer */
+ line_packet_len = w * bytespp + 1; /* 1 byte for DCS cmd */
+ total_len = line_packet_len * h;
+
+ display->ctrl->setup_update(display, x, y, w, h);
+
+ if (0)
+ dsi_vc_print_status(1);
+
+ start_measuring();
+
+ l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
+ dsi_write_reg(DSI_VC_TE(1), l);
+
+ dsi_vc_write_long_header(1, DSI_DT_DCS_LONG_WRITE, line_packet_len, 0);
+
+ if (dsi.use_te)
+ l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
+ else
+ l = FLD_MOD(l, 1, 31, 31); /* TE_START */
+ dsi_write_reg(DSI_VC_TE(1), l);
+
+ dispc_enable_lcd_out(1);
+
+ if (dsi.use_te)
+ dsi_vc_send_bta(1);
+}
+
+static void framedone_callback(void *data, u32 mask)
+{
+ if (dsi.framedone_scheduled) {
+ DSSERR("Framedone already scheduled. Bogus FRAMEDONE IRQ?\n");
+ return;
+ }
+
+ dsi.framedone_scheduled = 1;
+
+ /* We get FRAMEDONE when DISPC has finished sending pixels and turns
+ * itself off. However, DSI still has the pixels in its buffers, and
+ * is sending the data. Thus we have to wait until we can do a new
+ * transfer or turn the clocks off. We do that in a separate work
+ * func. */
+ schedule_work(&dsi.framedone_work);
+}
+
+static void framedone_worker(struct work_struct *work)
+{
+ unsigned long flags;
+ u32 l;
+ unsigned long tmo;
+ int i = 0;
+
+ l = REG_GET(DSI_VC_TE(1), 23, 0); /* TE_SIZE */
+
+ /* There shouldn't be much stuff in DSI buffers, if any, so we'll
+ * just busyloop */
+ if (l > 0) {
+ tmo = jiffies + msecs_to_jiffies(50);
+ while (REG_GET(DSI_VC_TE(1), 23, 0) > 0) { /* TE_SIZE */
+ i++;
+ if (time_after(jiffies, tmo)) {
+ DSSERR("timeout waiting TE_SIZE to zero\n");
+ break;
+ }
+ cpu_relax();
+ }
+ }
+
+ if (REG_GET(DSI_VC_TE(1), 30, 30))
+ DSSERR("TE_EN not zero\n");
+
+ if (REG_GET(DSI_VC_TE(1), 31, 31))
+ DSSERR("TE_START not zero\n");
+
+ spin_lock_irqsave(&dsi.update_lock, flags);
+ if (dsi.update_ongoing == 0) {
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+ DSSERR("framedone irq without update request\n");
+ return;
+ }
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+
+ end_measuring("DISPC");
+
+ DSSDBG("FRAMEDONE\n");
+
+#if 0
+ if (l)
+ DSSWARN("FRAMEDONE irq too early, %d bytes, %d loops\n", l, i);
+#else
+ if (l > 1024*3)
+ DSSWARN("FRAMEDONE irq too early, %d bytes, %d loops\n", l, i);
+#endif
+
+ spin_lock_irqsave(&dsi.update_lock, flags);
+ dsi.update_ongoing = 0;
+ while (dsi.update_syncers > 0) {
+ complete(&dsi.update_completion);
+ --dsi.update_syncers;
+ }
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+
+#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
+ dispc_fake_vsync_irq();
+#endif
+ enable_clocks(0);
+
+ dsi.framedone_scheduled = 0;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+ spin_lock_irqsave(&dsi.update_lock, flags);
+ dsi.update_ongoing = 1;
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+ dsi_update_screen_dispc(dsi.update_region.display);
+ }
+}
+
+static void dsi_start_auto_update(struct omap_display *display)
+{
+ unsigned long flags;
+ int bytespp = 3;
+
+ DSSDBG("starting auto update\n");
+
+ dsi.update_region.display = display;
+ dsi.update_region.x = 0;
+ dsi.update_region.y = 0;
+ dsi.update_region.w = display->x_res;
+ dsi.update_region.h = display->y_res;
+ dsi.update_region.bytespp = bytespp;
+
+ enable_clocks(1);
+
+ dispc_set_lcd_size(display->x_res, display->y_res);
+
+ spin_lock_irqsave(&dsi.update_lock, flags);
+ dsi.update_ongoing = 1;
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+ dsi_update_screen_dispc(display);
+}
+
+static void dsi_stop_auto_update(void)
+{
+ dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+
+ DSSDBG("waiting for display to finish.\n");
+ dsi_wait_for_framedone();
+ DSSDBG("done waiting\n");
+ enable_clocks(0);
+
+ dsi.update_mode = OMAP_DSS_UPDATE_MANUAL;
+}
+
+static int dsi_set_update_mode(struct omap_display *display,
+ enum omap_dss_update_mode mode)
+{
+ if (mode == dsi.update_mode)
+ return 0;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
+ dsi_stop_auto_update();
+ else if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+ dsi_wait_for_framedone();
+
+ dsi.update_mode = mode;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
+ dsi_start_auto_update(display);
+
+ return 0;
+}
+
+/* Display funcs */
+
+static int dsi_display_enable(struct omap_display *display)
+{
+ int r = 0;
+ struct dsi_clock_info cinfo;
+
+ DSSDBG("dsi_display_enable\n");
+
+ mutex_lock(&dsi.lock);
+
+ if (display->state != OMAP_DSS_DISPLAY_DISABLED) {
+ DSSERR("display already enabled\n");
+ r = -EINVAL;
+ goto err0;
+ }
+
+ enable_clocks(1);
+
+ r = omap_dispc_register_isr(framedone_callback, NULL,
+ DISPC_IRQ_FRAMEDONE);
+ if (r) {
+ DSSERR("can't get FRAMEDONE irq\n");
+ goto err1;
+ }
+
+ dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
+
+ dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI);
+ dispc_enable_fifohandcheck(1);
+ dispc_setup_plane_fifo(OMAP_DSS_GFX, 0);
+ dispc_setup_plane_fifo(OMAP_DSS_VIDEO1, 0);
+ dispc_setup_plane_fifo(OMAP_DSS_VIDEO2, 0);
+ dispc_set_tft_data_lines(display->bpp);
+
+ {
+ struct omap_video_timings timings = {
+ .hsw = 1,
+ .hfp = 1,
+ .hbp = 1,
+ .vsw = 1,
+ .vfp = 0,
+ .vbp = 0,
+ };
+
+ dispc_set_lcd_timings(&timings);
+ }
+
+ _dsi_print_reset_status();
+
+ r = dsi_pll_init(1, 0);
+ if (r)
+ goto err2;
+
+ /* XXX hardcoded for 300Mbp/lane for now */
+ r = dsi_pll_calc_datafreq(600 * 1000 * 1000, &cinfo);
+ if (r)
+ goto err3;
+
+ r = dsi_pll_program(&cinfo);
+ if (r)
+ goto err3;
+
+ DSSDBG("PLL OK\n");
+
+ r = dsi_complexio_init(display);
+ if (r)
+ goto err3;
+
+ _dsi_print_reset_status();
+
+ dsi_proto_timings();
+ dsi_set_lp_clk_divisor();
+
+ if (1)
+ _dsi_print_reset_status();
+
+ r = dsi_proto_config(display);
+ if (r)
+ goto err4;
+
+ /* enable interface */
+ dsi_vc_enable(0, 1);
+ dsi_vc_enable(1, 1);
+ dsi_if_enable(1);
+ dsi_force_tx_stop_mode_io();
+
+
+ if (display->ctrl && display->ctrl->enable) {
+ r = display->ctrl->enable(display);
+ if (r)
+ goto err5;
+ }
+
+ if (display->panel && display->panel->enable) {
+ r = display->panel->enable(display);
+ if (r)
+ goto err6;
+ }
+
+ if (dsi.use_te) {
+ r = display->ctrl->enable_te(display, 1);
+ if (r)
+ goto err7;
+ }
+
+ /* enable high-speed after initial config */
+ dsi_vc_enable_hs(0, 1);
+
+ display->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
+ dsi_start_auto_update(display);
+
+ enable_clocks(0);
+ mutex_unlock(&dsi.lock);
+
+ return 0;
+err7:
+ if (display->panel && display->panel->disable)
+ display->panel->disable(display);
+err6:
+ if (display->ctrl && display->ctrl->disable)
+ display->ctrl->disable(display);
+err5:
+ dsi_if_enable(0);
+err4:
+ dsi_complexio_uninit();
+err3:
+ dsi_pll_uninit();
+err2:
+ omap_dispc_unregister_isr(framedone_callback);
+err1:
+ enable_clocks(0);
+err0:
+ mutex_unlock(&dsi.lock);
+ DSSDBG("dsi_display_enable FAILED\n");
+ return r;
+}
+
+static void dsi_display_disable(struct omap_display *display)
+{
+ DSSDBG("dsi_display_disable\n");
+
+ mutex_lock(&dsi.lock);
+
+ if (display->state == OMAP_DSS_DISPLAY_DISABLED)
+ goto end;
+
+ enable_clocks(1);
+
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
+ dsi_stop_auto_update();
+ else if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+ dsi_wait_for_framedone();
+
+ display->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ omap_dispc_unregister_isr(framedone_callback);
+
+ if (display->panel && display->panel->disable)
+ display->panel->disable(display);
+ if (display->ctrl && display->ctrl->disable)
+ display->ctrl->disable(display);
+
+ /* XXX sleep a bit to make sure all DSI buffers are sent.
+ * We should check it from somewhere, fifo fullness I guess */
+ msleep(200);
+
+ dsi_complexio_uninit();
+ dsi_pll_uninit();
+
+ enable_clocks(0);
+
+end:
+ mutex_unlock(&dsi.lock);
+}
+
+static int dsi_display_suspend(struct omap_display *display)
+{
+ if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EINVAL;
+
+ if (display->panel->suspend)
+ display->panel->suspend(display);
+
+ if (display->ctrl->suspend)
+ display->ctrl->suspend(display);
+
+ display->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ return 0;
+}
+
+static int dsi_display_resume(struct omap_display *display)
+{
+ if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
+ return -EINVAL;
+
+ if (display->panel->resume)
+ display->panel->resume(display);
+
+ if (display->ctrl->resume)
+ display->ctrl->resume(display);
+
+ display->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ return 0;
+}
+
+static void dsi_display_set_mode(struct omap_display *display,
+ int x_res, int y_res, int bpp)
+{
+ DSSDBG("dsi_display_set_mode %dx%d, %dbpp\n", x_res, y_res, bpp);
+}
+
+static int dsi_display_update(struct omap_display *display,
+ int x, int y, int w, int h)
+{
+ unsigned long flags;
+ int r = 0;
+
+ DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h);
+
+ if (w == 0 || h == 0)
+ return 0;
+
+ mutex_lock(&dsi.lock);
+
+ if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL)
+ goto end; /* XXX return error? */
+
+ spin_lock_irqsave(&dsi.update_lock, flags);
+
+ if (dsi.update_ongoing) {
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+ DSSERR("DSI is busy\n");
+ r = -EBUSY;
+ goto end;
+ }
+
+ dsi.update_ongoing = 1;
+
+ if (dsi.update_syncers > 0)
+ DSSERR("someone waiting for sync, and no update ongoing\n");
+
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+
+ if (display->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+ dsi_setup_update_dispc(display, x, y, w, h);
+ dsi_update_screen_dispc(display);
+ } else {
+ r = dsi_update_screen_l4(display, x, y, w, h);
+ if (r)
+ goto end;
+
+ spin_lock_irqsave(&dsi.update_lock, flags);
+ dsi.update_ongoing = 0;
+ while (dsi.update_syncers > 0) {
+ complete(&dsi.update_completion);
+ --dsi.update_syncers;
+ }
+ spin_unlock_irqrestore(&dsi.update_lock, flags);
+ }
+
+end:
+ mutex_unlock(&dsi.lock);
+ return r;
+}
+
+static int dsi_display_sync(struct omap_display *display)
+{
+ int r = 0;
+
+ DSSDBG("dsi_display_sync\n");
+
+ mutex_lock(&dsi.lock);
+
+ if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL)
+ goto end;
+
+ r = dsi_wait_for_framedone();
+
+end:
+ mutex_unlock(&dsi.lock);
+ return r;
+}
+
+static int dsi_display_set_update_mode(struct omap_display *display,
+ enum omap_dss_update_mode mode)
+{
+ int r;
+
+ DSSDBG("dsi_display_set_update_mode\n");
+
+ mutex_lock(&dsi.lock);
+
+ r = dsi_set_update_mode(display, mode);
+
+ mutex_unlock(&dsi.lock);
+
+ return r;
+}
+
+static enum omap_dss_update_mode dsi_display_get_update_mode(
+ struct omap_display *display)
+{
+ return dsi.update_mode;
+}
+
+static int dsi_display_enable_te(struct omap_display *display, int enable)
+{
+ enum omap_dss_update_mode mode;
+
+ DSSDBG("dsi_display_enable_te\n");
+
+ mutex_lock(&dsi.lock);
+
+ enable_clocks(1);
+
+ mode = dsi.update_mode;
+
+ /* XXX perhaps suspend or something would be better here */
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
+ dsi_stop_auto_update();
+ else if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+ dsi_wait_for_framedone();
+
+ dsi.use_te = enable;
+ display->ctrl->enable_te(display, enable);
+ if (enable) {
+ /* disable LP_RX_TO, so that we can receive TE.
+ * Time to wait for TE is longer than the timer allows */
+ REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
+ } else {
+ REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
+ }
+
+ /* restore the old update mode */
+ dsi_set_update_mode(display, mode);
+
+ enable_clocks(0);
+
+ mutex_unlock(&dsi.lock);
+
+ return 0;
+}
+
+static int dsi_display_get_te(struct omap_display *display)
+{
+ return dsi.use_te;
+}
+
+static int dsi_display_run_test(struct omap_display *display, int test_num)
+{
+ enum omap_dss_update_mode mode;
+ int r = 0;
+
+ DSSDBG("dsi_display_run_test %d\n", test_num);
+
+ mutex_lock(&dsi.lock);
+
+ enable_clocks(1);
+
+ mode = dsi.update_mode;
+
+ /* XXX perhaps suspend or something would be better here */
+ if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO)
+ dsi_stop_auto_update();
+ else if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+ dsi_wait_for_framedone();
+
+ /* run test first in low speed mode */
+ dsi_vc_enable_hs(0, 0);
+
+ if (display->ctrl->run_test) {
+ r = display->ctrl->run_test(display, test_num);
+ if (r)
+ goto fail;
+ }
+
+ if (display->panel->run_test) {
+ r = display->panel->run_test(display, test_num);
+ if (r)
+ goto fail;
+ }
+
+ /* then in high speed */
+ dsi_vc_enable_hs(0, 1);
+
+ if (display->ctrl->run_test) {
+ r = display->ctrl->run_test(display, test_num);
+ if (r)
+ goto fail;
+ }
+
+ if (display->panel->run_test)
+ r = display->panel->run_test(display, test_num);
+
+fail:
+ dsi_vc_enable_hs(0, 1);
+
+ /* restore the old update mode */
+ dsi_set_update_mode(display, mode);
+
+ enable_clocks(0);
+
+ mutex_unlock(&dsi.lock);
+
+ return r;
+}
+
+void dsi_init_display(struct omap_display *display)
+{
+ DSSDBG("DSI init\n");
+
+ display->enable = dsi_display_enable;
+ display->disable = dsi_display_disable;
+ display->suspend = dsi_display_suspend;
+ display->resume = dsi_display_resume;
+ display->set_mode = dsi_display_set_mode;
+ display->update = dsi_display_update;
+ display->sync = dsi_display_sync;
+ display->set_update_mode = dsi_display_set_update_mode;
+ display->get_update_mode = dsi_display_get_update_mode;
+ display->enable_te = dsi_display_enable_te;
+ display->get_te = dsi_display_get_te;
+ display->run_test = dsi_display_run_test;
+
+ display->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+}
+
+int dsi_init(void)
+{
+ u32 rev;
+
+ init_completion(&dsi.bta_completion);
+ INIT_WORK(&dsi.framedone_work, framedone_worker);
+
+ init_completion(&dsi.update_completion);
+ spin_lock_init(&dsi.update_lock);
+ dsi.update_ongoing = 0;
+ dsi.update_syncers = 0;
+
+ mutex_init(&dsi.lock);
+
+ dsi.base = ioremap(DSI_BASE, SZ_1K);
+ if (!dsi.base) {
+ DSSERR("can't ioremap DSI\n");
+ return -ENOMEM;
+ }
+
+ dsi.dss_ick = get_dss_ick();
+ dsi.dss1_fck = get_dss1_fck();
+ dsi.dss2_fck = get_dss2_fck();
+
+ enable_clocks(1);
+
+ /* Autoidle */
+ REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0);
+
+ /* ENWAKEUP */
+ REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2);
+
+ /* SIDLEMODE smart-idle */
+ REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3);
+
+ if (0)
+ _dsi_reset();
+
+ _dsi_initialize_irq();
+
+ rev = dsi_read_reg(DSI_REVISION);
+ printk(KERN_INFO "OMAP DSI rev %d.%d\n",
+ FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+ enable_clocks(0);
+
+ return 0;
+}
+
+void dsi_exit(void)
+{
+ iounmap(dsi.base);
+
+ DSSDBG("omap_dsi_exit\n");
+}
+
next prev parent reply other threads:[~2008-11-04 16:10 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-11-04 16:09 [REVIEW PATCH 0/9] DSS: Series description Tomi Valkeinen
2008-11-04 16:09 ` [REVIEW PATCH 1/9] DSS: Documentation for OMAP2/3 display subsystem Tomi Valkeinen
2008-11-05 7:56 ` [Linux-fbdev-devel] " Geert Uytterhoeven
2008-11-05 10:12 ` Tomi Valkeinen
2008-11-04 16:09 ` [REVIEW PATCH 2/9] DSS: New display subsystem driver for OMAP2/3 Tomi Valkeinen
2008-11-04 16:10 ` [REVIEW PATCH 3/9] DSS: RFBI support for OMAP2/3 DSS Tomi Valkeinen
2008-11-04 16:10 ` [REVIEW PATCH 4/9] DSS: TV-out " Tomi Valkeinen
2008-11-05 10:27 ` Jarkko Nikula
2008-11-04 16:10 ` Tomi Valkeinen [this message]
2008-11-04 16:10 ` [REVIEW PATCH 6/9] DSS: OMAPFB: fb driver for new display subsystem Tomi Valkeinen
2008-11-04 16:10 ` [REVIEW PATCH 7/9] DSS: Add generic DVI panel Tomi Valkeinen
2008-11-04 16:10 ` [REVIEW PATCH 8/9] DSS: support for Beagle Board Tomi Valkeinen
2008-11-04 17:28 ` Koen Kooi
2008-11-05 10:05 ` Tomi Valkeinen
2008-11-05 21:15 ` Koen Kooi
2008-11-04 18:24 ` [Linux-fbdev-devel] " Tony Lindgren
2008-11-05 10:09 ` Tomi Valkeinen
2008-11-04 21:35 ` FSUSB Register access York, Jeffrey-P56387
2008-11-04 22:20 ` Felipe Balbi
2008-11-05 0:38 ` York, Jeffrey-P56387
2008-11-05 0:55 ` Felipe Balbi
2008-11-05 1:03 ` York, Jeffrey-P56387
2008-11-05 1:11 ` Felipe Balbi
2008-11-04 22:35 ` Woodruff, Richard
2008-11-05 10:27 ` [REVIEW PATCH 8/9] DSS: support for Beagle Board Jarkko Nikula
2008-11-05 23:21 ` David Brownell
2008-11-06 8:23 ` Tomi Valkeinen
2008-11-06 8:30 ` Koen Kooi
2008-11-04 16:10 ` [REVIEW PATCH 9/9] DSS: support for OMAP3 SDP board Tomi Valkeinen
2008-11-05 10:54 ` Jarkko Nikula
2008-11-10 4:03 ` [Linux-fbdev-devel] [REVIEW PATCH 0/9] DSS: Series description Shah, Hardik
2008-11-10 11:31 ` Tomi Valkeinen
2008-11-10 12:03 ` Shah, Hardik
2008-11-18 6:40 ` Shah, Hardik
2008-11-18 12:06 ` Tomi Valkeinen
2008-11-24 6:26 ` Arun KS
2008-11-24 6:30 ` Hiremath, Vaibhav
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20081104161019.19071.8831.stgit@tubuntu \
--to=tomi.valkeinen@nokia.com \
--cc=linux-fbdev-devel@lists.sourceforge.net \
--cc=linux-omap@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.