diff options
13 files changed, 5759 insertions, 5052 deletions
diff --git a/packages/linux/linux-omap/0003-DSS-Documentation-for-OMAP2-3-display-subsystem.patch b/packages/linux/linux-omap/0003-DSS-Documentation-for-OMAP2-3-display-subsystem.patch index 59c15cee7d..4946bda5e7 100644 --- a/packages/linux/linux-omap/0003-DSS-Documentation-for-OMAP2-3-display-subsystem.patch +++ b/packages/linux/linux-omap/0003-DSS-Documentation-for-OMAP2-3-display-subsystem.patch @@ -1,4 +1,4 @@ -From 7a7fe8f7530bf5c7f3714acbe9a5ec8cf80c3d0c Mon Sep 17 00:00:00 2001 +From 58be9dfad433036ff46ed883c3bc77fca88079f7 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen <tomi.valkeinen@nokia.com> Date: Tue, 4 Nov 2008 15:08:07 +0200 Subject: [PATCH] DSS: Documentation for OMAP2/3 display subsystem @@ -11,7 +11,7 @@ Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com> diff --git a/Documentation/arm/OMAP/DSS b/Documentation/arm/OMAP/DSS new file mode 100644 -index 0000000..b0cc980 +index 0000000..387bb73 --- /dev/null +++ b/Documentation/arm/OMAP/DSS @@ -0,0 +1,239 @@ @@ -41,7 +41,7 @@ index 0000000..b0cc980 +- Use CPU to update RFBI or DSI output +- OMAP DISPC planes +- RGB16, RGB24 packed, RGB24 unpacked -+- YUV2, UYVY ++- YUV2, UYVY +- Scaling +- Adjusting DSS FCK to find a good pixel clock +- Use DSI DPLL to create DSS FCK diff --git a/packages/linux/linux-omap/0004-DSS-New-display-subsystem-driver-for-OMAP2-3.patch b/packages/linux/linux-omap/0004-DSS-New-display-subsystem-driver-for-OMAP2-3.patch index febfc48c4d..509f34697a 100644 --- a/packages/linux/linux-omap/0004-DSS-New-display-subsystem-driver-for-OMAP2-3.patch +++ b/packages/linux/linux-omap/0004-DSS-New-display-subsystem-driver-for-OMAP2-3.patch @@ -1,32 +1,36 @@ -From 0cd726d12358cfe8d80fc0a309bb0c0732c716f0 Mon Sep 17 00:00:00 2001 +From 5a4331bf757fdec0ceb72bf40f7e46ce5c404e2d Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen <tomi.valkeinen@nokia.com> -Date: Tue, 4 Nov 2008 16:52:12 +0200 +Date: Tue, 11 Nov 2008 13:52:25 +0200 Subject: [PATCH] DSS: New display subsystem driver for OMAP2/3 -DSI, RFBI and VENC are separate patches - Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com> --- arch/arm/plat-omap/Kconfig | 2 + arch/arm/plat-omap/Makefile | 2 + - arch/arm/plat-omap/dss/Kconfig | 66 ++ + arch/arm/plat-omap/dss/Kconfig | 66 + arch/arm/plat-omap/dss/Makefile | 6 + - arch/arm/plat-omap/dss/dispc.c | 1667 +++++++++++++++++++++++++++++ - arch/arm/plat-omap/dss/display.c | 781 ++++++++++++++ - arch/arm/plat-omap/dss/dpi.c | 303 ++++++ - arch/arm/plat-omap/dss/dss.c | 547 ++++++++++ - arch/arm/plat-omap/dss/dss.h | 240 +++++ - arch/arm/plat-omap/dss/sdi.c | 154 +++ - arch/arm/plat-omap/include/mach/display.h | 458 ++++++++ - 11 files changed, 4226 insertions(+), 0 deletions(-) + arch/arm/plat-omap/dss/dispc.c | 1720 ++++++++++++++++ + arch/arm/plat-omap/dss/display.c | 775 ++++++++ + arch/arm/plat-omap/dss/dpi.c | 323 +++ + arch/arm/plat-omap/dss/dsi.c | 3022 +++++++++++++++++++++++++++++ + arch/arm/plat-omap/dss/dss.c | 547 ++++++ + arch/arm/plat-omap/dss/dss.h | 254 +++ + arch/arm/plat-omap/dss/rfbi.c | 1234 ++++++++++++ + arch/arm/plat-omap/dss/sdi.c | 157 ++ + arch/arm/plat-omap/dss/venc.c | 515 +++++ + arch/arm/plat-omap/include/mach/display.h | 458 +++++ + 14 files changed, 9081 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/dss/Kconfig create mode 100644 arch/arm/plat-omap/dss/Makefile create mode 100644 arch/arm/plat-omap/dss/dispc.c create mode 100644 arch/arm/plat-omap/dss/display.c create mode 100644 arch/arm/plat-omap/dss/dpi.c + create mode 100644 arch/arm/plat-omap/dss/dsi.c create mode 100644 arch/arm/plat-omap/dss/dss.c create mode 100644 arch/arm/plat-omap/dss/dss.h + create mode 100644 arch/arm/plat-omap/dss/rfbi.c create mode 100644 arch/arm/plat-omap/dss/sdi.c + create mode 100644 arch/arm/plat-omap/dss/venc.c create mode 100644 arch/arm/plat-omap/include/mach/display.h diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig @@ -54,7 +58,7 @@ index 1259846..2740497 100644 +obj-y += dss/ diff --git a/arch/arm/plat-omap/dss/Kconfig b/arch/arm/plat-omap/dss/Kconfig new file mode 100644 -index 0000000..150cd24 +index 0000000..ef0b5d9 --- /dev/null +++ b/arch/arm/plat-omap/dss/Kconfig @@ -0,0 +1,66 @@ @@ -107,8 +111,8 @@ index 0000000..150cd24 + +config OMAP2_DSS_MIN_FCK_PER_PCK + int "Minimum FCK/PCK ratio (for scaling)" -+ range 1 32 -+ default 4 ++ range 0 32 ++ default 0 + help + This can be used to adjust the minimum FCK/PCK ratio. + @@ -116,7 +120,7 @@ index 0000000..150cd24 + n x PCK. Video plane scaling requires higher FCK than + normally. + -+ If this is set to 1, there's no extra constraint on the ++ If this is set to 0, there's no extra constraint on the + DISPC FCK. However, the FCK will at minimum be + 2xPCK (if active matrix) or 3xPCK (if passive matrix). + @@ -138,10 +142,10 @@ index 0000000..e98c6c1 +omap-dss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o diff --git a/arch/arm/plat-omap/dss/dispc.c b/arch/arm/plat-omap/dss/dispc.c new file mode 100644 -index 0000000..8f5da2d +index 0000000..6d06082 --- /dev/null +++ b/arch/arm/plat-omap/dss/dispc.c -@@ -0,0 +1,1667 @@ +@@ -0,0 +1,1720 @@ +/* + * linux/arch/arm/plat-omap/dss/dispc.c + * @@ -255,8 +259,8 @@ index 0000000..8f5da2d + DISPC_IRQ_OCP_ERR | \ + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ -+ DISPC_IRQ_SYNC_LOST) -+/*DISPC_IRQ_SYNC_LOST_DIGIT*/ ++ DISPC_IRQ_SYNC_LOST | \ ++ DISPC_IRQ_SYNC_LOST_DIGIT) + +#define DISPC_MAX_NR_ISRS 8 + @@ -1240,91 +1244,135 @@ index 0000000..8f5da2d + panel->acbi, panel->acb); +} + -+unsigned long dispc_calc_clock_div(int is_tft, int pck, int *fck_div, ++void find_lck_pck_divs(int is_tft, unsigned long req_pck, unsigned long fck, + int *lck_div, int *pck_div) +{ -+ unsigned long prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); -+ unsigned long pcd_min = is_tft ? 2 : 3; -+ unsigned long best_pck = 0; -+ int best_fd = 9, best_ld = 1, best_pd = 2; -+ int fd, ld, pd; ++ int pcd_min = is_tft ? 2 : 3; ++ unsigned long best_pck; ++ int best_ld, cur_ld; ++ int best_pd, cur_pd; + -+ for (fd = 16; fd > 0; --fd) { -+ unsigned long fck = prate / fd * 2; -+ -+ if (fck > DISPC_MAX_FCK) -+ continue; ++ best_pck = 0; ++ best_ld = 0; ++ best_pd = 0; + -+#ifdef CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK -+ if (fck < pck * CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK) -+ continue; -+#endif -+ for (ld = 1; ld <= 255; ++ld) { -+ unsigned long lck = fck / ld; ++ for (cur_ld = 1; cur_ld <= 255; ++cur_ld) { ++ unsigned long lck = fck / cur_ld; + -+ for (pd = pcd_min; pd <= 255; ++pd) { -+ int p = lck / pd; ++ for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) { ++ unsigned long pck = lck / cur_pd; + -+ if (abs(p - pck) < abs(best_pck - pck)) { -+ best_pck = p; -+ best_fd = fd; -+ best_ld = ld; -+ best_pd = pd; -+ } ++ if (abs(pck - req_pck) < abs(best_pck - req_pck)) { ++ best_pck = pck; ++ best_ld = cur_ld; ++ best_pd = cur_pd; + -+ if (p == pck) ++ if (pck == req_pck) + goto found; -+ -+ if (p < pck) -+ break; + } + -+ if (lck / pcd_min < pck) ++ if (pck < req_pck) + break; + } ++ ++ if (lck / pcd_min < req_pck) ++ break; + } + +found: -+ *fck_div = best_fd; + *lck_div = best_ld; + *pck_div = best_pd; -+ -+ return prate / best_fd * 2; +} + -+void dispc_set_clock_div(int fck_div, int lck_div, int pck_div) ++int dispc_calc_clock_div(int is_tft, unsigned long req_pck, ++ struct dispc_clock_info *cinfo) +{ -+ unsigned long prate; ++ unsigned long prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); ++ struct dispc_clock_info cur, best; ++ int match = 0; ++ int min_fck_per_pck; ++ ++ min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; ++ ++ if (min_fck_per_pck && ++ req_pck * min_fck_per_pck > DISPC_MAX_FCK) { ++ DSSERR("Requested pixel clock not possible with the current " ++ "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " ++ "the constraint off.\n"); ++ min_fck_per_pck = 0; ++ } + -+ prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); ++retry: ++ memset(&cur, 0, sizeof(cur)); ++ memset(&best, 0, sizeof(best)); + -+ clk_set_rate(dispc.dpll4_m4_ck, prate / fck_div); -+ dispc_set_lcd_divisor(lck_div, pck_div); ++ for (cur.fck_div = 16; cur.fck_div > 0; --cur.fck_div) { ++ cur.fck = prate / cur.fck_div * 2; + -+#ifdef DEBUG -+ { -+ unsigned long fck, lck, pck; -+ fck = prate / fck_div * 2; -+ lck = fck / lck_div; -+ pck = lck / pck_div; ++ if (cur.fck > DISPC_MAX_FCK) ++ continue; ++ ++ if (min_fck_per_pck && ++ cur.fck < req_pck * min_fck_per_pck) ++ continue; ++ ++ match = 1; ++ ++ find_lck_pck_divs(is_tft, req_pck, cur.fck, ++ &cur.lck_div, &cur.pck_div); ++ ++ cur.lck = cur.fck / cur.lck_div; ++ cur.pck = cur.lck / cur.pck_div; + -+ DSSDBG("dpll4_m4 = %ld\n", prate); -+ DSSDBG("fck = %ld (%d)\n", fck, fck_div); -+ DSSDBG("lck = %ld (%d)\n", lck, lck_div); -+ DSSDBG("pck = %ld (%d)\n", pck, pck_div); ++ if (abs(cur.pck - req_pck) < abs(best.pck - req_pck)) { ++ best = cur; ++ ++ if (cur.pck == req_pck) ++ goto found; ++ } + } -+#endif ++ ++found: ++ if (!match) { ++ if (min_fck_per_pck) { ++ DSSERR("Could not find suitable clock settings.\n" ++ "Turning FCK/PCK constraint off and" ++ "trying again.\n"); ++ min_fck_per_pck = 0; ++ goto retry; ++ } ++ ++ DSSERR("Could not find suitable clock settings.\n"); ++ ++ return -EINVAL; ++ } ++ ++ if (cinfo) ++ *cinfo = best; ++ ++ return 0; +} + -+int dispc_pixel_clock_valid(int pixel_clock) ++int dispc_set_clock_div(struct dispc_clock_info *cinfo) +{ -+ int fck_div, lck_div, pck_div; -+ unsigned long fck; ++ unsigned long prate; ++ int r; ++ ++ prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); ++ ++ r = clk_set_rate(dispc.dpll4_m4_ck, prate / cinfo->fck_div); ++ ++ if (r) ++ return r; ++ ++ dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div); + -+ fck = dispc_calc_clock_div(1, pixel_clock * 1000, -+ &fck_div, &lck_div, &pck_div); ++ DSSDBG("dpll4_m4 = %ld\n", prate); ++ DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div); ++ DSSDBG("lck = %ld (%d)\n", cinfo->lck, cinfo->lck_div); ++ DSSDBG("pck = %ld (%d)\n", cinfo->pck, cinfo->pck_div); + -+ return fck > 0; ++ return 0; +} + +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) @@ -1431,8 +1479,23 @@ index 0000000..8f5da2d + int i; + u32 irqstatus = dispc_read_reg(DISPC_IRQSTATUS); + static int errors; ++ u32 handledirqs = 0; ++ ++#ifdef DEBUG ++ print_irq_status(irqstatus); ++#endif + -+ if (irqstatus & DISPC_IRQ_MASK_ERROR) { ++ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { ++ if (!registered_isr[i].isr) ++ continue; ++ if (registered_isr[i].mask & irqstatus) { ++ registered_isr[i].isr(registered_isr[i].arg, ++ irqstatus); ++ handledirqs |= registered_isr[i].mask; ++ } ++ } ++ ++ if (irqstatus & ~handledirqs & DISPC_IRQ_MASK_ERROR) { + if (printk_ratelimit()) { + DSSERR("dispc irq error status %04x\n", + irqstatus); @@ -1444,17 +1507,6 @@ index 0000000..8f5da2d + dispc_enable_digit_out(0); + } + } -+#ifdef DEBUG -+ print_irq_status(irqstatus); -+#endif -+ -+ for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { -+ if (!registered_isr[i].isr) -+ continue; -+ if (registered_isr[i].mask & irqstatus) -+ registered_isr[i].isr(registered_isr[i].arg, -+ irqstatus); -+ } + + /* ack the interrupt */ + dispc_write_reg(DISPC_IRQSTATUS, irqstatus); @@ -1480,6 +1532,11 @@ index 0000000..8f5da2d +{ + memset(registered_isr, 0, sizeof(registered_isr)); + ++ /* there's SYNC_LOST_DIGIT waiting after enabling the DSS, ++ * so clear it */ ++ dispc_write_reg(DISPC_IRQSTATUS, ++ dispc_read_reg(DISPC_IRQSTATUS)); ++ + /* We'll handle these always */ + dispc_write_reg(DISPC_IRQENABLE, DISPC_IRQ_MASK_ERROR); +} @@ -1811,10 +1868,10 @@ index 0000000..8f5da2d + diff --git a/arch/arm/plat-omap/dss/display.c b/arch/arm/plat-omap/dss/display.c new file mode 100644 -index 0000000..86f7d39 +index 0000000..4d7238f --- /dev/null +++ b/arch/arm/plat-omap/dss/display.c -@@ -0,0 +1,781 @@ +@@ -0,0 +1,775 @@ +/* + * linux/arch/arm/plat-omap/dss/display.c + * @@ -1843,7 +1900,6 @@ index 0000000..86f7d39 +#include <linux/io.h> +#include <linux/device.h> +#include <linux/err.h> -+#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/clk.h> + @@ -2230,11 +2286,6 @@ index 0000000..86f7d39 + dispc_enable_plane(ovl->id, 1); + } + -+ /* XXX if autoidle is enabled, we have to wait here a bit. -+ * Otherwise if we issue GOLCD too soon after lcd enable, -+ * we get sync lost. Why? */ -+ mdelay(100); -+ + dispc_go(mgr->id); + + return 0; @@ -2598,10 +2649,10 @@ index 0000000..86f7d39 +EXPORT_SYMBOL(omap_dss_unregister_panel); diff --git a/arch/arm/plat-omap/dss/dpi.c b/arch/arm/plat-omap/dss/dpi.c new file mode 100644 -index 0000000..d121b52 +index 0000000..2261288 --- /dev/null +++ b/arch/arm/plat-omap/dss/dpi.c -@@ -0,0 +1,303 @@ +@@ -0,0 +1,323 @@ +/* + * linux/arch/arm/plat-omap/dss/dpi.c + * @@ -2674,23 +2725,32 @@ index 0000000..d121b52 + } +#else + { -+ int fck_div; -+ fck = dispc_calc_clock_div(is_tft, -+ panel->timings.pixel_clock*1000, -+ &fck_div, &lck_div, &pck_div); ++ struct dispc_clock_info cinfo; ++ dispc_calc_clock_div(is_tft, panel->timings.pixel_clock*1000, ++ &cinfo); + -+ if (fck == 0) { -+ DSSERR("Requested pixel clock is not possible\n"); ++ if (dispc_set_clock_div(&cinfo)) { ++ DSSERR("Failed to set DSS clocks\n"); + return; + } + -+ dispc_set_clock_div(fck_div, lck_div, pck_div); ++ fck = cinfo.fck; ++ lck_div = cinfo.lck_div; ++ pck_div = cinfo.pck_div; + } +#endif + + pck = fck / lck_div / pck_div / 1000; + -+ panel->timings.pixel_clock = pck; ++ if (pck != panel->timings.pixel_clock) { ++ DSSWARN("Could not find exact pixel clock. Requested %d KHz, " ++ "got %lu KHz.\n", ++ panel->timings.pixel_clock, ++ pck); ++ ++ panel->timings.pixel_clock = pck; ++ } ++ + DSSDBG("fck %lu, lck_div %d, pck_div %d\n", fck, lck_div, pck_div); +#ifdef DEBUG + { @@ -2830,6 +2890,9 @@ index 0000000..d121b52 +static int dpi_check_timings(struct omap_display *display, + struct omap_video_timings *timings) +{ ++ int is_tft; ++ int r; ++ + if (timings->hsw < 1 || timings->hsw > 64 || + timings->hfp < 1 || timings->hfp > 256 || + timings->hbp < 1 || timings->hbp > 256) { @@ -2841,8 +2904,16 @@ index 0000000..d121b52 + return -EINVAL; + } + -+ if (!dispc_pixel_clock_valid(timings->pixel_clock)) -+ return -EINVAL; ++ ++ is_tft = (display->panel->config & OMAP_DSS_LCD_TFT) != 0; ++ ++#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL ++ r = dsi_pll_calc_pck(is_tft, timings->pixel_clock * 1000, 0); ++#else ++ r = dispc_calc_clock_div(is_tft, timings->pixel_clock * 1000, 0); ++#endif ++ if (r) ++ return r; + + return 0; +} @@ -2905,6 +2976,3034 @@ index 0000000..d121b52 +{ +} + +diff --git a/arch/arm/plat-omap/dss/dsi.c b/arch/arm/plat-omap/dss/dsi.c +new file mode 100644 +index 0000000..980be39 +--- /dev/null ++++ b/arch/arm/plat-omap/dss/dsi.c +@@ -0,0 +1,3022 @@ ++/* ++ * 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) ++{ |
