1f76ee892STomi Valkeinen /* 2f76ee892STomi Valkeinen * linux/drivers/video/omap2/dss/dsi.c 3f76ee892STomi Valkeinen * 4f76ee892STomi Valkeinen * Copyright (C) 2009 Nokia Corporation 5f76ee892STomi Valkeinen * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 6f76ee892STomi Valkeinen * 7f76ee892STomi Valkeinen * This program is free software; you can redistribute it and/or modify it 8f76ee892STomi Valkeinen * under the terms of the GNU General Public License version 2 as published by 9f76ee892STomi Valkeinen * the Free Software Foundation. 10f76ee892STomi Valkeinen * 11f76ee892STomi Valkeinen * This program is distributed in the hope that it will be useful, but WITHOUT 12f76ee892STomi Valkeinen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13f76ee892STomi Valkeinen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14f76ee892STomi Valkeinen * more details. 15f76ee892STomi Valkeinen * 16f76ee892STomi Valkeinen * You should have received a copy of the GNU General Public License along with 17f76ee892STomi Valkeinen * this program. If not, see <http://www.gnu.org/licenses/>. 18f76ee892STomi Valkeinen */ 19f76ee892STomi Valkeinen 20f76ee892STomi Valkeinen #define DSS_SUBSYS_NAME "DSI" 21f76ee892STomi Valkeinen 22f76ee892STomi Valkeinen #include <linux/kernel.h> 23f76ee892STomi Valkeinen #include <linux/io.h> 24f76ee892STomi Valkeinen #include <linux/clk.h> 25f76ee892STomi Valkeinen #include <linux/device.h> 26f76ee892STomi Valkeinen #include <linux/err.h> 27f76ee892STomi Valkeinen #include <linux/interrupt.h> 28f76ee892STomi Valkeinen #include <linux/delay.h> 29f76ee892STomi Valkeinen #include <linux/mutex.h> 30f76ee892STomi Valkeinen #include <linux/module.h> 31f76ee892STomi Valkeinen #include <linux/semaphore.h> 32f76ee892STomi Valkeinen #include <linux/seq_file.h> 33f76ee892STomi Valkeinen #include <linux/platform_device.h> 34f76ee892STomi Valkeinen #include <linux/regulator/consumer.h> 35f76ee892STomi Valkeinen #include <linux/wait.h> 36f76ee892STomi Valkeinen #include <linux/workqueue.h> 37f76ee892STomi Valkeinen #include <linux/sched.h> 38f76ee892STomi Valkeinen #include <linux/slab.h> 39f76ee892STomi Valkeinen #include <linux/debugfs.h> 40f76ee892STomi Valkeinen #include <linux/pm_runtime.h> 41f76ee892STomi Valkeinen #include <linux/of.h> 42f76ee892STomi Valkeinen #include <linux/of_platform.h> 43f76ee892STomi Valkeinen #include <linux/component.h> 44f76ee892STomi Valkeinen 4562d9e44eSPeter Ujfalusi #include <video/omapfb_dss.h> 46f76ee892STomi Valkeinen #include <video/mipi_display.h> 47f76ee892STomi Valkeinen 48f76ee892STomi Valkeinen #include "dss.h" 49f76ee892STomi Valkeinen #include "dss_features.h" 50f76ee892STomi Valkeinen 51f76ee892STomi Valkeinen #define DSI_CATCH_MISSING_TE 52f76ee892STomi Valkeinen 53f76ee892STomi Valkeinen struct dsi_reg { u16 module; u16 idx; }; 54f76ee892STomi Valkeinen 55f76ee892STomi Valkeinen #define DSI_REG(mod, idx) ((const struct dsi_reg) { mod, idx }) 56f76ee892STomi Valkeinen 57f76ee892STomi Valkeinen /* DSI Protocol Engine */ 58f76ee892STomi Valkeinen 59f76ee892STomi Valkeinen #define DSI_PROTO 0 60f76ee892STomi Valkeinen #define DSI_PROTO_SZ 0x200 61f76ee892STomi Valkeinen 62f76ee892STomi Valkeinen #define DSI_REVISION DSI_REG(DSI_PROTO, 0x0000) 63f76ee892STomi Valkeinen #define DSI_SYSCONFIG DSI_REG(DSI_PROTO, 0x0010) 64f76ee892STomi Valkeinen #define DSI_SYSSTATUS DSI_REG(DSI_PROTO, 0x0014) 65f76ee892STomi Valkeinen #define DSI_IRQSTATUS DSI_REG(DSI_PROTO, 0x0018) 66f76ee892STomi Valkeinen #define DSI_IRQENABLE DSI_REG(DSI_PROTO, 0x001C) 67f76ee892STomi Valkeinen #define DSI_CTRL DSI_REG(DSI_PROTO, 0x0040) 68f76ee892STomi Valkeinen #define DSI_GNQ DSI_REG(DSI_PROTO, 0x0044) 69f76ee892STomi Valkeinen #define DSI_COMPLEXIO_CFG1 DSI_REG(DSI_PROTO, 0x0048) 70f76ee892STomi Valkeinen #define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(DSI_PROTO, 0x004C) 71f76ee892STomi Valkeinen #define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(DSI_PROTO, 0x0050) 72f76ee892STomi Valkeinen #define DSI_CLK_CTRL DSI_REG(DSI_PROTO, 0x0054) 73f76ee892STomi Valkeinen #define DSI_TIMING1 DSI_REG(DSI_PROTO, 0x0058) 74f76ee892STomi Valkeinen #define DSI_TIMING2 DSI_REG(DSI_PROTO, 0x005C) 75f76ee892STomi Valkeinen #define DSI_VM_TIMING1 DSI_REG(DSI_PROTO, 0x0060) 76f76ee892STomi Valkeinen #define DSI_VM_TIMING2 DSI_REG(DSI_PROTO, 0x0064) 77f76ee892STomi Valkeinen #define DSI_VM_TIMING3 DSI_REG(DSI_PROTO, 0x0068) 78f76ee892STomi Valkeinen #define DSI_CLK_TIMING DSI_REG(DSI_PROTO, 0x006C) 79f76ee892STomi Valkeinen #define DSI_TX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0070) 80f76ee892STomi Valkeinen #define DSI_RX_FIFO_VC_SIZE DSI_REG(DSI_PROTO, 0x0074) 81f76ee892STomi Valkeinen #define DSI_COMPLEXIO_CFG2 DSI_REG(DSI_PROTO, 0x0078) 82f76ee892STomi Valkeinen #define DSI_RX_FIFO_VC_FULLNESS DSI_REG(DSI_PROTO, 0x007C) 83f76ee892STomi Valkeinen #define DSI_VM_TIMING4 DSI_REG(DSI_PROTO, 0x0080) 84f76ee892STomi Valkeinen #define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(DSI_PROTO, 0x0084) 85f76ee892STomi Valkeinen #define DSI_VM_TIMING5 DSI_REG(DSI_PROTO, 0x0088) 86f76ee892STomi Valkeinen #define DSI_VM_TIMING6 DSI_REG(DSI_PROTO, 0x008C) 87f76ee892STomi Valkeinen #define DSI_VM_TIMING7 DSI_REG(DSI_PROTO, 0x0090) 88f76ee892STomi Valkeinen #define DSI_STOPCLK_TIMING DSI_REG(DSI_PROTO, 0x0094) 89f76ee892STomi Valkeinen #define DSI_VC_CTRL(n) DSI_REG(DSI_PROTO, 0x0100 + (n * 0x20)) 90f76ee892STomi Valkeinen #define DSI_VC_TE(n) DSI_REG(DSI_PROTO, 0x0104 + (n * 0x20)) 91f76ee892STomi Valkeinen #define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0108 + (n * 0x20)) 92f76ee892STomi Valkeinen #define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(DSI_PROTO, 0x010C + (n * 0x20)) 93f76ee892STomi Valkeinen #define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(DSI_PROTO, 0x0110 + (n * 0x20)) 94f76ee892STomi Valkeinen #define DSI_VC_IRQSTATUS(n) DSI_REG(DSI_PROTO, 0x0118 + (n * 0x20)) 95f76ee892STomi Valkeinen #define DSI_VC_IRQENABLE(n) DSI_REG(DSI_PROTO, 0x011C + (n * 0x20)) 96f76ee892STomi Valkeinen 97f76ee892STomi Valkeinen /* DSIPHY_SCP */ 98f76ee892STomi Valkeinen 99f76ee892STomi Valkeinen #define DSI_PHY 1 100f76ee892STomi Valkeinen #define DSI_PHY_OFFSET 0x200 101f76ee892STomi Valkeinen #define DSI_PHY_SZ 0x40 102f76ee892STomi Valkeinen 103f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG0 DSI_REG(DSI_PHY, 0x0000) 104f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG1 DSI_REG(DSI_PHY, 0x0004) 105f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG2 DSI_REG(DSI_PHY, 0x0008) 106f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG5 DSI_REG(DSI_PHY, 0x0014) 107f76ee892STomi Valkeinen #define DSI_DSIPHY_CFG10 DSI_REG(DSI_PHY, 0x0028) 108f76ee892STomi Valkeinen 109f76ee892STomi Valkeinen /* DSI_PLL_CTRL_SCP */ 110f76ee892STomi Valkeinen 111f76ee892STomi Valkeinen #define DSI_PLL 2 112f76ee892STomi Valkeinen #define DSI_PLL_OFFSET 0x300 113f76ee892STomi Valkeinen #define DSI_PLL_SZ 0x20 114f76ee892STomi Valkeinen 115f76ee892STomi Valkeinen #define DSI_PLL_CONTROL DSI_REG(DSI_PLL, 0x0000) 116f76ee892STomi Valkeinen #define DSI_PLL_STATUS DSI_REG(DSI_PLL, 0x0004) 117f76ee892STomi Valkeinen #define DSI_PLL_GO DSI_REG(DSI_PLL, 0x0008) 118f76ee892STomi Valkeinen #define DSI_PLL_CONFIGURATION1 DSI_REG(DSI_PLL, 0x000C) 119f76ee892STomi Valkeinen #define DSI_PLL_CONFIGURATION2 DSI_REG(DSI_PLL, 0x0010) 120f76ee892STomi Valkeinen 121f76ee892STomi Valkeinen #define REG_GET(dsidev, idx, start, end) \ 122f76ee892STomi Valkeinen FLD_GET(dsi_read_reg(dsidev, idx), start, end) 123f76ee892STomi Valkeinen 124f76ee892STomi Valkeinen #define REG_FLD_MOD(dsidev, idx, val, start, end) \ 125f76ee892STomi Valkeinen dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end)) 126f76ee892STomi Valkeinen 127f76ee892STomi Valkeinen /* Global interrupts */ 128f76ee892STomi Valkeinen #define DSI_IRQ_VC0 (1 << 0) 129f76ee892STomi Valkeinen #define DSI_IRQ_VC1 (1 << 1) 130f76ee892STomi Valkeinen #define DSI_IRQ_VC2 (1 << 2) 131f76ee892STomi Valkeinen #define DSI_IRQ_VC3 (1 << 3) 132f76ee892STomi Valkeinen #define DSI_IRQ_WAKEUP (1 << 4) 133f76ee892STomi Valkeinen #define DSI_IRQ_RESYNC (1 << 5) 134f76ee892STomi Valkeinen #define DSI_IRQ_PLL_LOCK (1 << 7) 135f76ee892STomi Valkeinen #define DSI_IRQ_PLL_UNLOCK (1 << 8) 136f76ee892STomi Valkeinen #define DSI_IRQ_PLL_RECALL (1 << 9) 137f76ee892STomi Valkeinen #define DSI_IRQ_COMPLEXIO_ERR (1 << 10) 138f76ee892STomi Valkeinen #define DSI_IRQ_HS_TX_TIMEOUT (1 << 14) 139f76ee892STomi Valkeinen #define DSI_IRQ_LP_RX_TIMEOUT (1 << 15) 140f76ee892STomi Valkeinen #define DSI_IRQ_TE_TRIGGER (1 << 16) 141f76ee892STomi Valkeinen #define DSI_IRQ_ACK_TRIGGER (1 << 17) 142f76ee892STomi Valkeinen #define DSI_IRQ_SYNC_LOST (1 << 18) 143f76ee892STomi Valkeinen #define DSI_IRQ_LDO_POWER_GOOD (1 << 19) 144f76ee892STomi Valkeinen #define DSI_IRQ_TA_TIMEOUT (1 << 20) 145f76ee892STomi Valkeinen #define DSI_IRQ_ERROR_MASK \ 146f76ee892STomi Valkeinen (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ 147f76ee892STomi Valkeinen DSI_IRQ_TA_TIMEOUT) 148f76ee892STomi Valkeinen #define DSI_IRQ_CHANNEL_MASK 0xf 149f76ee892STomi Valkeinen 150f76ee892STomi Valkeinen /* Virtual channel interrupts */ 151f76ee892STomi Valkeinen #define DSI_VC_IRQ_CS (1 << 0) 152f76ee892STomi Valkeinen #define DSI_VC_IRQ_ECC_CORR (1 << 1) 153f76ee892STomi Valkeinen #define DSI_VC_IRQ_PACKET_SENT (1 << 2) 154f76ee892STomi Valkeinen #define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3) 155f76ee892STomi Valkeinen #define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4) 156f76ee892STomi Valkeinen #define DSI_VC_IRQ_BTA (1 << 5) 157f76ee892STomi Valkeinen #define DSI_VC_IRQ_ECC_NO_CORR (1 << 6) 158f76ee892STomi Valkeinen #define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7) 159f76ee892STomi Valkeinen #define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8) 160f76ee892STomi Valkeinen #define DSI_VC_IRQ_ERROR_MASK \ 161f76ee892STomi Valkeinen (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \ 162f76ee892STomi Valkeinen DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \ 163f76ee892STomi Valkeinen DSI_VC_IRQ_FIFO_TX_UDF) 164f76ee892STomi Valkeinen 165f76ee892STomi Valkeinen /* ComplexIO interrupts */ 166f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) 167f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) 168f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) 169f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3) 170f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4) 171f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC1 (1 << 5) 172f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC2 (1 << 6) 173f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC3 (1 << 7) 174f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC4 (1 << 8) 175f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRESC5 (1 << 9) 176f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) 177f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) 178f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) 179f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13) 180f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14) 181f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS1 (1 << 15) 182f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS2 (1 << 16) 183f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS3 (1 << 17) 184f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS4 (1 << 18) 185f76ee892STomi Valkeinen #define DSI_CIO_IRQ_STATEULPS5 (1 << 19) 186f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) 187f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) 188f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) 189f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) 190f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) 191f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) 192f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26) 193f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27) 194f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28) 195f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29) 196f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) 197f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) 198f76ee892STomi Valkeinen #define DSI_CIO_IRQ_ERROR_MASK \ 199f76ee892STomi Valkeinen (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \ 200f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \ 201f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRSYNCESC5 | \ 202f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ 203f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \ 204f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRESC5 | \ 205f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \ 206f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \ 207f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTROL5 | \ 208f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \ 209f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \ 210f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \ 211f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \ 212f76ee892STomi Valkeinen DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5) 213f76ee892STomi Valkeinen 214f76ee892STomi Valkeinen typedef void (*omap_dsi_isr_t) (void *arg, u32 mask); 215f76ee892STomi Valkeinen 216f76ee892STomi Valkeinen static int dsi_display_init_dispc(struct platform_device *dsidev, 217f76ee892STomi Valkeinen struct omap_overlay_manager *mgr); 218f76ee892STomi Valkeinen static void dsi_display_uninit_dispc(struct platform_device *dsidev, 219f76ee892STomi Valkeinen struct omap_overlay_manager *mgr); 220f76ee892STomi Valkeinen 221f76ee892STomi Valkeinen static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel); 222f76ee892STomi Valkeinen 223f76ee892STomi Valkeinen /* DSI PLL HSDIV indices */ 224f76ee892STomi Valkeinen #define HSDIV_DISPC 0 225f76ee892STomi Valkeinen #define HSDIV_DSI 1 226f76ee892STomi Valkeinen 227f76ee892STomi Valkeinen #define DSI_MAX_NR_ISRS 2 228f76ee892STomi Valkeinen #define DSI_MAX_NR_LANES 5 229f76ee892STomi Valkeinen 230f76ee892STomi Valkeinen enum dsi_lane_function { 231f76ee892STomi Valkeinen DSI_LANE_UNUSED = 0, 232f76ee892STomi Valkeinen DSI_LANE_CLK, 233f76ee892STomi Valkeinen DSI_LANE_DATA1, 234f76ee892STomi Valkeinen DSI_LANE_DATA2, 235f76ee892STomi Valkeinen DSI_LANE_DATA3, 236f76ee892STomi Valkeinen DSI_LANE_DATA4, 237f76ee892STomi Valkeinen }; 238f76ee892STomi Valkeinen 239f76ee892STomi Valkeinen struct dsi_lane_config { 240f76ee892STomi Valkeinen enum dsi_lane_function function; 241f76ee892STomi Valkeinen u8 polarity; 242f76ee892STomi Valkeinen }; 243f76ee892STomi Valkeinen 244f76ee892STomi Valkeinen struct dsi_isr_data { 245f76ee892STomi Valkeinen omap_dsi_isr_t isr; 246f76ee892STomi Valkeinen void *arg; 247f76ee892STomi Valkeinen u32 mask; 248f76ee892STomi Valkeinen }; 249f76ee892STomi Valkeinen 250f76ee892STomi Valkeinen enum fifo_size { 251f76ee892STomi Valkeinen DSI_FIFO_SIZE_0 = 0, 252f76ee892STomi Valkeinen DSI_FIFO_SIZE_32 = 1, 253f76ee892STomi Valkeinen DSI_FIFO_SIZE_64 = 2, 254f76ee892STomi Valkeinen DSI_FIFO_SIZE_96 = 3, 255f76ee892STomi Valkeinen DSI_FIFO_SIZE_128 = 4, 256f76ee892STomi Valkeinen }; 257f76ee892STomi Valkeinen 258f76ee892STomi Valkeinen enum dsi_vc_source { 259f76ee892STomi Valkeinen DSI_VC_SOURCE_L4 = 0, 260f76ee892STomi Valkeinen DSI_VC_SOURCE_VP, 261f76ee892STomi Valkeinen }; 262f76ee892STomi Valkeinen 263f76ee892STomi Valkeinen struct dsi_irq_stats { 264f76ee892STomi Valkeinen unsigned long last_reset; 265f76ee892STomi Valkeinen unsigned irq_count; 266f76ee892STomi Valkeinen unsigned dsi_irqs[32]; 267f76ee892STomi Valkeinen unsigned vc_irqs[4][32]; 268f76ee892STomi Valkeinen unsigned cio_irqs[32]; 269f76ee892STomi Valkeinen }; 270f76ee892STomi Valkeinen 271f76ee892STomi Valkeinen struct dsi_isr_tables { 272f76ee892STomi Valkeinen struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS]; 273f76ee892STomi Valkeinen struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS]; 274f76ee892STomi Valkeinen struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS]; 275f76ee892STomi Valkeinen }; 276f76ee892STomi Valkeinen 277f76ee892STomi Valkeinen struct dsi_clk_calc_ctx { 278f76ee892STomi Valkeinen struct platform_device *dsidev; 279f76ee892STomi Valkeinen struct dss_pll *pll; 280f76ee892STomi Valkeinen 281f76ee892STomi Valkeinen /* inputs */ 282f76ee892STomi Valkeinen 283f76ee892STomi Valkeinen const struct omap_dss_dsi_config *config; 284f76ee892STomi Valkeinen 285f76ee892STomi Valkeinen unsigned long req_pck_min, req_pck_nom, req_pck_max; 286f76ee892STomi Valkeinen 287f76ee892STomi Valkeinen /* outputs */ 288f76ee892STomi Valkeinen 289f76ee892STomi Valkeinen struct dss_pll_clock_info dsi_cinfo; 290f76ee892STomi Valkeinen struct dispc_clock_info dispc_cinfo; 291f76ee892STomi Valkeinen 292f76ee892STomi Valkeinen struct omap_video_timings dispc_vm; 293f76ee892STomi Valkeinen struct omap_dss_dsi_videomode_timings dsi_vm; 294f76ee892STomi Valkeinen }; 295f76ee892STomi Valkeinen 296f76ee892STomi Valkeinen struct dsi_lp_clock_info { 297f76ee892STomi Valkeinen unsigned long lp_clk; 298f76ee892STomi Valkeinen u16 lp_clk_div; 299f76ee892STomi Valkeinen }; 300f76ee892STomi Valkeinen 301f76ee892STomi Valkeinen struct dsi_data { 302f76ee892STomi Valkeinen struct platform_device *pdev; 303f76ee892STomi Valkeinen void __iomem *proto_base; 304f76ee892STomi Valkeinen void __iomem *phy_base; 305f76ee892STomi Valkeinen void __iomem *pll_base; 306f76ee892STomi Valkeinen 307f76ee892STomi Valkeinen int module_id; 308f76ee892STomi Valkeinen 309f76ee892STomi Valkeinen int irq; 310f76ee892STomi Valkeinen 311f76ee892STomi Valkeinen bool is_enabled; 312f76ee892STomi Valkeinen 313f76ee892STomi Valkeinen struct clk *dss_clk; 314f76ee892STomi Valkeinen 315f76ee892STomi Valkeinen struct dispc_clock_info user_dispc_cinfo; 316f76ee892STomi Valkeinen struct dss_pll_clock_info user_dsi_cinfo; 317f76ee892STomi Valkeinen 318f76ee892STomi Valkeinen struct dsi_lp_clock_info user_lp_cinfo; 319f76ee892STomi Valkeinen struct dsi_lp_clock_info current_lp_cinfo; 320f76ee892STomi Valkeinen 321f76ee892STomi Valkeinen struct dss_pll pll; 322f76ee892STomi Valkeinen 323f76ee892STomi Valkeinen bool vdds_dsi_enabled; 324f76ee892STomi Valkeinen struct regulator *vdds_dsi_reg; 325f76ee892STomi Valkeinen 326f76ee892STomi Valkeinen struct { 327f76ee892STomi Valkeinen enum dsi_vc_source source; 328f76ee892STomi Valkeinen struct omap_dss_device *dssdev; 329f76ee892STomi Valkeinen enum fifo_size tx_fifo_size; 330f76ee892STomi Valkeinen enum fifo_size rx_fifo_size; 331f76ee892STomi Valkeinen int vc_id; 332f76ee892STomi Valkeinen } vc[4]; 333f76ee892STomi Valkeinen 334f76ee892STomi Valkeinen struct mutex lock; 335f76ee892STomi Valkeinen struct semaphore bus_lock; 336f76ee892STomi Valkeinen 337f76ee892STomi Valkeinen spinlock_t irq_lock; 338f76ee892STomi Valkeinen struct dsi_isr_tables isr_tables; 339f76ee892STomi Valkeinen /* space for a copy used by the interrupt handler */ 340f76ee892STomi Valkeinen struct dsi_isr_tables isr_tables_copy; 341f76ee892STomi Valkeinen 342f76ee892STomi Valkeinen int update_channel; 343f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE 344f76ee892STomi Valkeinen unsigned update_bytes; 345f76ee892STomi Valkeinen #endif 346f76ee892STomi Valkeinen 347f76ee892STomi Valkeinen bool te_enabled; 348f76ee892STomi Valkeinen bool ulps_enabled; 349f76ee892STomi Valkeinen 350f76ee892STomi Valkeinen void (*framedone_callback)(int, void *); 351f76ee892STomi Valkeinen void *framedone_data; 352f76ee892STomi Valkeinen 353f76ee892STomi Valkeinen struct delayed_work framedone_timeout_work; 354f76ee892STomi Valkeinen 355f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE 356f76ee892STomi Valkeinen struct timer_list te_timer; 357f76ee892STomi Valkeinen #endif 358f76ee892STomi Valkeinen 359f76ee892STomi Valkeinen unsigned long cache_req_pck; 360f76ee892STomi Valkeinen unsigned long cache_clk_freq; 361f76ee892STomi Valkeinen struct dss_pll_clock_info cache_cinfo; 362f76ee892STomi Valkeinen 363f76ee892STomi Valkeinen u32 errors; 364f76ee892STomi Valkeinen spinlock_t errors_lock; 365f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE 366f76ee892STomi Valkeinen ktime_t perf_setup_time; 367f76ee892STomi Valkeinen ktime_t perf_start_time; 368f76ee892STomi Valkeinen #endif 369f76ee892STomi Valkeinen int debug_read; 370f76ee892STomi Valkeinen int debug_write; 371f76ee892STomi Valkeinen 37235b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 373f76ee892STomi Valkeinen spinlock_t irq_stats_lock; 374f76ee892STomi Valkeinen struct dsi_irq_stats irq_stats; 375f76ee892STomi Valkeinen #endif 376f76ee892STomi Valkeinen 377f76ee892STomi Valkeinen unsigned num_lanes_supported; 378f76ee892STomi Valkeinen unsigned line_buffer_size; 379f76ee892STomi Valkeinen 380f76ee892STomi Valkeinen struct dsi_lane_config lanes[DSI_MAX_NR_LANES]; 381f76ee892STomi Valkeinen unsigned num_lanes_used; 382f76ee892STomi Valkeinen 383f76ee892STomi Valkeinen unsigned scp_clk_refcount; 384f76ee892STomi Valkeinen 385f76ee892STomi Valkeinen struct dss_lcd_mgr_config mgr_config; 386f76ee892STomi Valkeinen struct omap_video_timings timings; 387f76ee892STomi Valkeinen enum omap_dss_dsi_pixel_format pix_fmt; 388f76ee892STomi Valkeinen enum omap_dss_dsi_mode mode; 389f76ee892STomi Valkeinen struct omap_dss_dsi_videomode_timings vm_timings; 390f76ee892STomi Valkeinen 391f76ee892STomi Valkeinen struct omap_dss_device output; 392f76ee892STomi Valkeinen }; 393f76ee892STomi Valkeinen 394f76ee892STomi Valkeinen struct dsi_packet_sent_handler_data { 395f76ee892STomi Valkeinen struct platform_device *dsidev; 396f76ee892STomi Valkeinen struct completion *completion; 397f76ee892STomi Valkeinen }; 398f76ee892STomi Valkeinen 399f76ee892STomi Valkeinen struct dsi_module_id_data { 400f76ee892STomi Valkeinen u32 address; 401f76ee892STomi Valkeinen int id; 402f76ee892STomi Valkeinen }; 403f76ee892STomi Valkeinen 404f76ee892STomi Valkeinen static const struct of_device_id dsi_of_match[]; 405f76ee892STomi Valkeinen 406f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE 407f76ee892STomi Valkeinen static bool dsi_perf; 408f76ee892STomi Valkeinen module_param(dsi_perf, bool, 0644); 409f76ee892STomi Valkeinen #endif 410f76ee892STomi Valkeinen 411f76ee892STomi Valkeinen static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev) 412f76ee892STomi Valkeinen { 413f76ee892STomi Valkeinen return dev_get_drvdata(&dsidev->dev); 414f76ee892STomi Valkeinen } 415f76ee892STomi Valkeinen 416f76ee892STomi Valkeinen static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev) 417f76ee892STomi Valkeinen { 418f76ee892STomi Valkeinen return to_platform_device(dssdev->dev); 419f76ee892STomi Valkeinen } 420f76ee892STomi Valkeinen 421f76ee892STomi Valkeinen static struct platform_device *dsi_get_dsidev_from_id(int module) 422f76ee892STomi Valkeinen { 423f76ee892STomi Valkeinen struct omap_dss_device *out; 424f76ee892STomi Valkeinen enum omap_dss_output_id id; 425f76ee892STomi Valkeinen 426f76ee892STomi Valkeinen switch (module) { 427f76ee892STomi Valkeinen case 0: 428f76ee892STomi Valkeinen id = OMAP_DSS_OUTPUT_DSI1; 429f76ee892STomi Valkeinen break; 430f76ee892STomi Valkeinen case 1: 431f76ee892STomi Valkeinen id = OMAP_DSS_OUTPUT_DSI2; 432f76ee892STomi Valkeinen break; 433f76ee892STomi Valkeinen default: 434f76ee892STomi Valkeinen return NULL; 435f76ee892STomi Valkeinen } 436f76ee892STomi Valkeinen 437f76ee892STomi Valkeinen out = omap_dss_get_output(id); 438f76ee892STomi Valkeinen 439f76ee892STomi Valkeinen return out ? to_platform_device(out->dev) : NULL; 440f76ee892STomi Valkeinen } 441f76ee892STomi Valkeinen 442f76ee892STomi Valkeinen static inline void dsi_write_reg(struct platform_device *dsidev, 443f76ee892STomi Valkeinen const struct dsi_reg idx, u32 val) 444f76ee892STomi Valkeinen { 445f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 446f76ee892STomi Valkeinen void __iomem *base; 447f76ee892STomi Valkeinen 448f76ee892STomi Valkeinen switch(idx.module) { 449f76ee892STomi Valkeinen case DSI_PROTO: base = dsi->proto_base; break; 450f76ee892STomi Valkeinen case DSI_PHY: base = dsi->phy_base; break; 451f76ee892STomi Valkeinen case DSI_PLL: base = dsi->pll_base; break; 452f76ee892STomi Valkeinen default: return; 453f76ee892STomi Valkeinen } 454f76ee892STomi Valkeinen 455f76ee892STomi Valkeinen __raw_writel(val, base + idx.idx); 456f76ee892STomi Valkeinen } 457f76ee892STomi Valkeinen 458f76ee892STomi Valkeinen static inline u32 dsi_read_reg(struct platform_device *dsidev, 459f76ee892STomi Valkeinen const struct dsi_reg idx) 460f76ee892STomi Valkeinen { 461f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 462f76ee892STomi Valkeinen void __iomem *base; 463f76ee892STomi Valkeinen 464f76ee892STomi Valkeinen switch(idx.module) { 465f76ee892STomi Valkeinen case DSI_PROTO: base = dsi->proto_base; break; 466f76ee892STomi Valkeinen case DSI_PHY: base = dsi->phy_base; break; 467f76ee892STomi Valkeinen case DSI_PLL: base = dsi->pll_base; break; 468f76ee892STomi Valkeinen default: return 0; 469f76ee892STomi Valkeinen } 470f76ee892STomi Valkeinen 471f76ee892STomi Valkeinen return __raw_readl(base + idx.idx); 472f76ee892STomi Valkeinen } 473f76ee892STomi Valkeinen 474f76ee892STomi Valkeinen static void dsi_bus_lock(struct omap_dss_device *dssdev) 475f76ee892STomi Valkeinen { 476f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 477f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 478f76ee892STomi Valkeinen 479f76ee892STomi Valkeinen down(&dsi->bus_lock); 480f76ee892STomi Valkeinen } 481f76ee892STomi Valkeinen 482f76ee892STomi Valkeinen static void dsi_bus_unlock(struct omap_dss_device *dssdev) 483f76ee892STomi Valkeinen { 484f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 485f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 486f76ee892STomi Valkeinen 487f76ee892STomi Valkeinen up(&dsi->bus_lock); 488f76ee892STomi Valkeinen } 489f76ee892STomi Valkeinen 490f76ee892STomi Valkeinen static bool dsi_bus_is_locked(struct platform_device *dsidev) 491f76ee892STomi Valkeinen { 492f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 493f76ee892STomi Valkeinen 494f76ee892STomi Valkeinen return dsi->bus_lock.count == 0; 495f76ee892STomi Valkeinen } 496f76ee892STomi Valkeinen 497f76ee892STomi Valkeinen static void dsi_completion_handler(void *data, u32 mask) 498f76ee892STomi Valkeinen { 499f76ee892STomi Valkeinen complete((struct completion *)data); 500f76ee892STomi Valkeinen } 501f76ee892STomi Valkeinen 502f76ee892STomi Valkeinen static inline int wait_for_bit_change(struct platform_device *dsidev, 503f76ee892STomi Valkeinen const struct dsi_reg idx, int bitnum, int value) 504f76ee892STomi Valkeinen { 505f76ee892STomi Valkeinen unsigned long timeout; 506f76ee892STomi Valkeinen ktime_t wait; 507f76ee892STomi Valkeinen int t; 508f76ee892STomi Valkeinen 509f76ee892STomi Valkeinen /* first busyloop to see if the bit changes right away */ 510f76ee892STomi Valkeinen t = 100; 511f76ee892STomi Valkeinen while (t-- > 0) { 512f76ee892STomi Valkeinen if (REG_GET(dsidev, idx, bitnum, bitnum) == value) 513f76ee892STomi Valkeinen return value; 514f76ee892STomi Valkeinen } 515f76ee892STomi Valkeinen 516f76ee892STomi Valkeinen /* then loop for 500ms, sleeping for 1ms in between */ 517f76ee892STomi Valkeinen timeout = jiffies + msecs_to_jiffies(500); 518f76ee892STomi Valkeinen while (time_before(jiffies, timeout)) { 519f76ee892STomi Valkeinen if (REG_GET(dsidev, idx, bitnum, bitnum) == value) 520f76ee892STomi Valkeinen return value; 521f76ee892STomi Valkeinen 522f76ee892STomi Valkeinen wait = ns_to_ktime(1000 * 1000); 523f76ee892STomi Valkeinen set_current_state(TASK_UNINTERRUPTIBLE); 524f76ee892STomi Valkeinen schedule_hrtimeout(&wait, HRTIMER_MODE_REL); 525f76ee892STomi Valkeinen } 526f76ee892STomi Valkeinen 527f76ee892STomi Valkeinen return !value; 528f76ee892STomi Valkeinen } 529f76ee892STomi Valkeinen 530f76ee892STomi Valkeinen u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt) 531f76ee892STomi Valkeinen { 532f76ee892STomi Valkeinen switch (fmt) { 533f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB888: 534f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB666: 535f76ee892STomi Valkeinen return 24; 536f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB666_PACKED: 537f76ee892STomi Valkeinen return 18; 538f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB565: 539f76ee892STomi Valkeinen return 16; 540f76ee892STomi Valkeinen default: 541f76ee892STomi Valkeinen BUG(); 542f76ee892STomi Valkeinen return 0; 543f76ee892STomi Valkeinen } 544f76ee892STomi Valkeinen } 545f76ee892STomi Valkeinen 546f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE 547f76ee892STomi Valkeinen static void dsi_perf_mark_setup(struct platform_device *dsidev) 548f76ee892STomi Valkeinen { 549f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 550f76ee892STomi Valkeinen dsi->perf_setup_time = ktime_get(); 551f76ee892STomi Valkeinen } 552f76ee892STomi Valkeinen 553f76ee892STomi Valkeinen static void dsi_perf_mark_start(struct platform_device *dsidev) 554f76ee892STomi Valkeinen { 555f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 556f76ee892STomi Valkeinen dsi->perf_start_time = ktime_get(); 557f76ee892STomi Valkeinen } 558f76ee892STomi Valkeinen 559f76ee892STomi Valkeinen static void dsi_perf_show(struct platform_device *dsidev, const char *name) 560f76ee892STomi Valkeinen { 561f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 562f76ee892STomi Valkeinen ktime_t t, setup_time, trans_time; 563f76ee892STomi Valkeinen u32 total_bytes; 564f76ee892STomi Valkeinen u32 setup_us, trans_us, total_us; 565f76ee892STomi Valkeinen 566f76ee892STomi Valkeinen if (!dsi_perf) 567f76ee892STomi Valkeinen return; 568f76ee892STomi Valkeinen 569f76ee892STomi Valkeinen t = ktime_get(); 570f76ee892STomi Valkeinen 571f76ee892STomi Valkeinen setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time); 572f76ee892STomi Valkeinen setup_us = (u32)ktime_to_us(setup_time); 573f76ee892STomi Valkeinen if (setup_us == 0) 574f76ee892STomi Valkeinen setup_us = 1; 575f76ee892STomi Valkeinen 576f76ee892STomi Valkeinen trans_time = ktime_sub(t, dsi->perf_start_time); 577f76ee892STomi Valkeinen trans_us = (u32)ktime_to_us(trans_time); 578f76ee892STomi Valkeinen if (trans_us == 0) 579f76ee892STomi Valkeinen trans_us = 1; 580f76ee892STomi Valkeinen 581f76ee892STomi Valkeinen total_us = setup_us + trans_us; 582f76ee892STomi Valkeinen 583f76ee892STomi Valkeinen total_bytes = dsi->update_bytes; 584f76ee892STomi Valkeinen 585f76ee892STomi Valkeinen printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " 586f76ee892STomi Valkeinen "%u bytes, %u kbytes/sec\n", 587f76ee892STomi Valkeinen name, 588f76ee892STomi Valkeinen setup_us, 589f76ee892STomi Valkeinen trans_us, 590f76ee892STomi Valkeinen total_us, 591f76ee892STomi Valkeinen 1000*1000 / total_us, 592f76ee892STomi Valkeinen total_bytes, 593f76ee892STomi Valkeinen total_bytes * 1000 / total_us); 594f76ee892STomi Valkeinen } 595f76ee892STomi Valkeinen #else 596f76ee892STomi Valkeinen static inline void dsi_perf_mark_setup(struct platform_device *dsidev) 597f76ee892STomi Valkeinen { 598f76ee892STomi Valkeinen } 599f76ee892STomi Valkeinen 600f76ee892STomi Valkeinen static inline void dsi_perf_mark_start(struct platform_device *dsidev) 601f76ee892STomi Valkeinen { 602f76ee892STomi Valkeinen } 603f76ee892STomi Valkeinen 604f76ee892STomi Valkeinen static inline void dsi_perf_show(struct platform_device *dsidev, 605f76ee892STomi Valkeinen const char *name) 606f76ee892STomi Valkeinen { 607f76ee892STomi Valkeinen } 608f76ee892STomi Valkeinen #endif 609f76ee892STomi Valkeinen 610f76ee892STomi Valkeinen static int verbose_irq; 611f76ee892STomi Valkeinen 612f76ee892STomi Valkeinen static void print_irq_status(u32 status) 613f76ee892STomi Valkeinen { 614f76ee892STomi Valkeinen if (status == 0) 615f76ee892STomi Valkeinen return; 616f76ee892STomi Valkeinen 617f76ee892STomi Valkeinen if (!verbose_irq && (status & ~DSI_IRQ_CHANNEL_MASK) == 0) 618f76ee892STomi Valkeinen return; 619f76ee892STomi Valkeinen 620f76ee892STomi Valkeinen #define PIS(x) (status & DSI_IRQ_##x) ? (#x " ") : "" 621f76ee892STomi Valkeinen 622f76ee892STomi Valkeinen pr_debug("DSI IRQ: 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", 623f76ee892STomi Valkeinen status, 624f76ee892STomi Valkeinen verbose_irq ? PIS(VC0) : "", 625f76ee892STomi Valkeinen verbose_irq ? PIS(VC1) : "", 626f76ee892STomi Valkeinen verbose_irq ? PIS(VC2) : "", 627f76ee892STomi Valkeinen verbose_irq ? PIS(VC3) : "", 628f76ee892STomi Valkeinen PIS(WAKEUP), 629f76ee892STomi Valkeinen PIS(RESYNC), 630f76ee892STomi Valkeinen PIS(PLL_LOCK), 631f76ee892STomi Valkeinen PIS(PLL_UNLOCK), 632f76ee892STomi Valkeinen PIS(PLL_RECALL), 633f76ee892STomi Valkeinen PIS(COMPLEXIO_ERR), 634f76ee892STomi Valkeinen PIS(HS_TX_TIMEOUT), 635f76ee892STomi Valkeinen PIS(LP_RX_TIMEOUT), 636f76ee892STomi Valkeinen PIS(TE_TRIGGER), 637f76ee892STomi Valkeinen PIS(ACK_TRIGGER), 638f76ee892STomi Valkeinen PIS(SYNC_LOST), 639f76ee892STomi Valkeinen PIS(LDO_POWER_GOOD), 640f76ee892STomi Valkeinen PIS(TA_TIMEOUT)); 641f76ee892STomi Valkeinen #undef PIS 642f76ee892STomi Valkeinen } 643f76ee892STomi Valkeinen 644f76ee892STomi Valkeinen static void print_irq_status_vc(int channel, u32 status) 645f76ee892STomi Valkeinen { 646f76ee892STomi Valkeinen if (status == 0) 647f76ee892STomi Valkeinen return; 648f76ee892STomi Valkeinen 649f76ee892STomi Valkeinen if (!verbose_irq && (status & ~DSI_VC_IRQ_PACKET_SENT) == 0) 650f76ee892STomi Valkeinen return; 651f76ee892STomi Valkeinen 652f76ee892STomi Valkeinen #define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : "" 653f76ee892STomi Valkeinen 654f76ee892STomi Valkeinen pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n", 655f76ee892STomi Valkeinen channel, 656f76ee892STomi Valkeinen status, 657f76ee892STomi Valkeinen PIS(CS), 658f76ee892STomi Valkeinen PIS(ECC_CORR), 659f76ee892STomi Valkeinen PIS(ECC_NO_CORR), 660f76ee892STomi Valkeinen verbose_irq ? PIS(PACKET_SENT) : "", 661f76ee892STomi Valkeinen PIS(BTA), 662f76ee892STomi Valkeinen PIS(FIFO_TX_OVF), 663f76ee892STomi Valkeinen PIS(FIFO_RX_OVF), 664f76ee892STomi Valkeinen PIS(FIFO_TX_UDF), 665f76ee892STomi Valkeinen PIS(PP_BUSY_CHANGE)); 666f76ee892STomi Valkeinen #undef PIS 667f76ee892STomi Valkeinen } 668f76ee892STomi Valkeinen 669f76ee892STomi Valkeinen static void print_irq_status_cio(u32 status) 670f76ee892STomi Valkeinen { 671f76ee892STomi Valkeinen if (status == 0) 672f76ee892STomi Valkeinen return; 673f76ee892STomi Valkeinen 674f76ee892STomi Valkeinen #define PIS(x) (status & DSI_CIO_IRQ_##x) ? (#x " ") : "" 675f76ee892STomi Valkeinen 676f76ee892STomi Valkeinen pr_debug("DSI CIO IRQ 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", 677f76ee892STomi Valkeinen status, 678f76ee892STomi Valkeinen PIS(ERRSYNCESC1), 679f76ee892STomi Valkeinen PIS(ERRSYNCESC2), 680f76ee892STomi Valkeinen PIS(ERRSYNCESC3), 681f76ee892STomi Valkeinen PIS(ERRESC1), 682f76ee892STomi Valkeinen PIS(ERRESC2), 683f76ee892STomi Valkeinen PIS(ERRESC3), 684f76ee892STomi Valkeinen PIS(ERRCONTROL1), 685f76ee892STomi Valkeinen PIS(ERRCONTROL2), 686f76ee892STomi Valkeinen PIS(ERRCONTROL3), 687f76ee892STomi Valkeinen PIS(STATEULPS1), 688f76ee892STomi Valkeinen PIS(STATEULPS2), 689f76ee892STomi Valkeinen PIS(STATEULPS3), 690f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP0_1), 691f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP1_1), 692f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP0_2), 693f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP1_2), 694f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP0_3), 695f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP1_3), 696f76ee892STomi Valkeinen PIS(ULPSACTIVENOT_ALL0), 697f76ee892STomi Valkeinen PIS(ULPSACTIVENOT_ALL1)); 698f76ee892STomi Valkeinen #undef PIS 699f76ee892STomi Valkeinen } 700f76ee892STomi Valkeinen 70135b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 702f76ee892STomi Valkeinen static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus, 703f76ee892STomi Valkeinen u32 *vcstatus, u32 ciostatus) 704f76ee892STomi Valkeinen { 705f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 706f76ee892STomi Valkeinen int i; 707f76ee892STomi Valkeinen 708f76ee892STomi Valkeinen spin_lock(&dsi->irq_stats_lock); 709f76ee892STomi Valkeinen 710f76ee892STomi Valkeinen dsi->irq_stats.irq_count++; 711f76ee892STomi Valkeinen dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs); 712f76ee892STomi Valkeinen 713f76ee892STomi Valkeinen for (i = 0; i < 4; ++i) 714f76ee892STomi Valkeinen dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]); 715f76ee892STomi Valkeinen 716f76ee892STomi Valkeinen dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs); 717f76ee892STomi Valkeinen 718f76ee892STomi Valkeinen spin_unlock(&dsi->irq_stats_lock); 719f76ee892STomi Valkeinen } 720f76ee892STomi Valkeinen #else 721f76ee892STomi Valkeinen #define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus) 722f76ee892STomi Valkeinen #endif 723f76ee892STomi Valkeinen 724f76ee892STomi Valkeinen static int debug_irq; 725f76ee892STomi Valkeinen 726f76ee892STomi Valkeinen static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus, 727f76ee892STomi Valkeinen u32 *vcstatus, u32 ciostatus) 728f76ee892STomi Valkeinen { 729f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 730f76ee892STomi Valkeinen int i; 731f76ee892STomi Valkeinen 732f76ee892STomi Valkeinen if (irqstatus & DSI_IRQ_ERROR_MASK) { 733f76ee892STomi Valkeinen DSSERR("DSI error, irqstatus %x\n", irqstatus); 734f76ee892STomi Valkeinen print_irq_status(irqstatus); 735f76ee892STomi Valkeinen spin_lock(&dsi->errors_lock); 736f76ee892STomi Valkeinen dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK; 737f76ee892STomi Valkeinen spin_unlock(&dsi->errors_lock); 738f76ee892STomi Valkeinen } else if (debug_irq) { 739f76ee892STomi Valkeinen print_irq_status(irqstatus); 740f76ee892STomi Valkeinen } 741f76ee892STomi Valkeinen 742f76ee892STomi Valkeinen for (i = 0; i < 4; ++i) { 743f76ee892STomi Valkeinen if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) { 744f76ee892STomi Valkeinen DSSERR("DSI VC(%d) error, vc irqstatus %x\n", 745f76ee892STomi Valkeinen i, vcstatus[i]); 746f76ee892STomi Valkeinen print_irq_status_vc(i, vcstatus[i]); 747f76ee892STomi Valkeinen } else if (debug_irq) { 748f76ee892STomi Valkeinen print_irq_status_vc(i, vcstatus[i]); 749f76ee892STomi Valkeinen } 750f76ee892STomi Valkeinen } 751f76ee892STomi Valkeinen 752f76ee892STomi Valkeinen if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) { 753f76ee892STomi Valkeinen DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); 754f76ee892STomi Valkeinen print_irq_status_cio(ciostatus); 755f76ee892STomi Valkeinen } else if (debug_irq) { 756f76ee892STomi Valkeinen print_irq_status_cio(ciostatus); 757f76ee892STomi Valkeinen } 758f76ee892STomi Valkeinen } 759f76ee892STomi Valkeinen 760f76ee892STomi Valkeinen static void dsi_call_isrs(struct dsi_isr_data *isr_array, 761f76ee892STomi Valkeinen unsigned isr_array_size, u32 irqstatus) 762f76ee892STomi Valkeinen { 763f76ee892STomi Valkeinen struct dsi_isr_data *isr_data; 764f76ee892STomi Valkeinen int i; 765f76ee892STomi Valkeinen 766f76ee892STomi Valkeinen for (i = 0; i < isr_array_size; i++) { 767f76ee892STomi Valkeinen isr_data = &isr_array[i]; 768f76ee892STomi Valkeinen if (isr_data->isr && isr_data->mask & irqstatus) 769f76ee892STomi Valkeinen isr_data->isr(isr_data->arg, irqstatus); 770f76ee892STomi Valkeinen } 771f76ee892STomi Valkeinen } 772f76ee892STomi Valkeinen 773f76ee892STomi Valkeinen static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables, 774f76ee892STomi Valkeinen u32 irqstatus, u32 *vcstatus, u32 ciostatus) 775f76ee892STomi Valkeinen { 776f76ee892STomi Valkeinen int i; 777f76ee892STomi Valkeinen 778f76ee892STomi Valkeinen dsi_call_isrs(isr_tables->isr_table, 779f76ee892STomi Valkeinen ARRAY_SIZE(isr_tables->isr_table), 780f76ee892STomi Valkeinen irqstatus); 781f76ee892STomi Valkeinen 782f76ee892STomi Valkeinen for (i = 0; i < 4; ++i) { 783f76ee892STomi Valkeinen if (vcstatus[i] == 0) 784f76ee892STomi Valkeinen continue; 785f76ee892STomi Valkeinen dsi_call_isrs(isr_tables->isr_table_vc[i], 786f76ee892STomi Valkeinen ARRAY_SIZE(isr_tables->isr_table_vc[i]), 787f76ee892STomi Valkeinen vcstatus[i]); 788f76ee892STomi Valkeinen } 789f76ee892STomi Valkeinen 790f76ee892STomi Valkeinen if (ciostatus != 0) 791f76ee892STomi Valkeinen dsi_call_isrs(isr_tables->isr_table_cio, 792f76ee892STomi Valkeinen ARRAY_SIZE(isr_tables->isr_table_cio), 793f76ee892STomi Valkeinen ciostatus); 794f76ee892STomi Valkeinen } 795f76ee892STomi Valkeinen 796f76ee892STomi Valkeinen static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) 797f76ee892STomi Valkeinen { 798f76ee892STomi Valkeinen struct platform_device *dsidev; 799f76ee892STomi Valkeinen struct dsi_data *dsi; 800f76ee892STomi Valkeinen u32 irqstatus, vcstatus[4], ciostatus; 801f76ee892STomi Valkeinen int i; 802f76ee892STomi Valkeinen 803f76ee892STomi Valkeinen dsidev = (struct platform_device *) arg; 804f76ee892STomi Valkeinen dsi = dsi_get_dsidrv_data(dsidev); 805f76ee892STomi Valkeinen 806f76ee892STomi Valkeinen if (!dsi->is_enabled) 807f76ee892STomi Valkeinen return IRQ_NONE; 808f76ee892STomi Valkeinen 809f76ee892STomi Valkeinen spin_lock(&dsi->irq_lock); 810f76ee892STomi Valkeinen 811f76ee892STomi Valkeinen irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS); 812f76ee892STomi Valkeinen 813f76ee892STomi Valkeinen /* IRQ is not for us */ 814f76ee892STomi Valkeinen if (!irqstatus) { 815f76ee892STomi Valkeinen spin_unlock(&dsi->irq_lock); 816f76ee892STomi Valkeinen return IRQ_NONE; 817f76ee892STomi Valkeinen } 818f76ee892STomi Valkeinen 819f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); 820f76ee892STomi Valkeinen /* flush posted write */ 821f76ee892STomi Valkeinen dsi_read_reg(dsidev, DSI_IRQSTATUS); 822f76ee892STomi Valkeinen 823f76ee892STomi Valkeinen for (i = 0; i < 4; ++i) { 824f76ee892STomi Valkeinen if ((irqstatus & (1 << i)) == 0) { 825f76ee892STomi Valkeinen vcstatus[i] = 0; 826f76ee892STomi Valkeinen continue; 827f76ee892STomi Valkeinen } 828f76ee892STomi Valkeinen 829f76ee892STomi Valkeinen vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); 830f76ee892STomi Valkeinen 831f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]); 832f76ee892STomi Valkeinen /* flush posted write */ 833f76ee892STomi Valkeinen dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); 834f76ee892STomi Valkeinen } 835f76ee892STomi Valkeinen 836f76ee892STomi Valkeinen if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { 837f76ee892STomi Valkeinen ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); 838f76ee892STomi Valkeinen 839f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus); 840f76ee892STomi Valkeinen /* flush posted write */ 841f76ee892STomi Valkeinen dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); 842f76ee892STomi Valkeinen } else { 843f76ee892STomi Valkeinen ciostatus = 0; 844f76ee892STomi Valkeinen } 845f76ee892STomi Valkeinen 846f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE 847f76ee892STomi Valkeinen if (irqstatus & DSI_IRQ_TE_TRIGGER) 848f76ee892STomi Valkeinen del_timer(&dsi->te_timer); 849f76ee892STomi Valkeinen #endif 850f76ee892STomi Valkeinen 851f76ee892STomi Valkeinen /* make a copy and unlock, so that isrs can unregister 852f76ee892STomi Valkeinen * themselves */ 853f76ee892STomi Valkeinen memcpy(&dsi->isr_tables_copy, &dsi->isr_tables, 854f76ee892STomi Valkeinen sizeof(dsi->isr_tables)); 855f76ee892STomi Valkeinen 856f76ee892STomi Valkeinen spin_unlock(&dsi->irq_lock); 857f76ee892STomi Valkeinen 858f76ee892STomi Valkeinen dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus); 859f76ee892STomi Valkeinen 860f76ee892STomi Valkeinen dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus); 861f76ee892STomi Valkeinen 862f76ee892STomi Valkeinen dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus); 863f76ee892STomi Valkeinen 864f76ee892STomi Valkeinen return IRQ_HANDLED; 865f76ee892STomi Valkeinen } 866f76ee892STomi Valkeinen 867f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */ 868f76ee892STomi Valkeinen static void _omap_dsi_configure_irqs(struct platform_device *dsidev, 869f76ee892STomi Valkeinen struct dsi_isr_data *isr_array, 870f76ee892STomi Valkeinen unsigned isr_array_size, u32 default_mask, 871f76ee892STomi Valkeinen const struct dsi_reg enable_reg, 872f76ee892STomi Valkeinen const struct dsi_reg status_reg) 873f76ee892STomi Valkeinen { 874f76ee892STomi Valkeinen struct dsi_isr_data *isr_data; 875f76ee892STomi Valkeinen u32 mask; 876f76ee892STomi Valkeinen u32 old_mask; 877f76ee892STomi Valkeinen int i; 878f76ee892STomi Valkeinen 879f76ee892STomi Valkeinen mask = default_mask; 880f76ee892STomi Valkeinen 881f76ee892STomi Valkeinen for (i = 0; i < isr_array_size; i++) { 882f76ee892STomi Valkeinen isr_data = &isr_array[i]; 883f76ee892STomi Valkeinen 884f76ee892STomi Valkeinen if (isr_data->isr == NULL) 885f76ee892STomi Valkeinen continue; 886f76ee892STomi Valkeinen 887f76ee892STomi Valkeinen mask |= isr_data->mask; 888f76ee892STomi Valkeinen } 889f76ee892STomi Valkeinen 890f76ee892STomi Valkeinen old_mask = dsi_read_reg(dsidev, enable_reg); 891f76ee892STomi Valkeinen /* clear the irqstatus for newly enabled irqs */ 892f76ee892STomi Valkeinen dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask); 893f76ee892STomi Valkeinen dsi_write_reg(dsidev, enable_reg, mask); 894f76ee892STomi Valkeinen 895f76ee892STomi Valkeinen /* flush posted writes */ 896f76ee892STomi Valkeinen dsi_read_reg(dsidev, enable_reg); 897f76ee892STomi Valkeinen dsi_read_reg(dsidev, status_reg); 898f76ee892STomi Valkeinen } 899f76ee892STomi Valkeinen 900f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */ 901f76ee892STomi Valkeinen static void _omap_dsi_set_irqs(struct platform_device *dsidev) 902f76ee892STomi Valkeinen { 903f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 904f76ee892STomi Valkeinen u32 mask = DSI_IRQ_ERROR_MASK; 905f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE 906f76ee892STomi Valkeinen mask |= DSI_IRQ_TE_TRIGGER; 907f76ee892STomi Valkeinen #endif 908f76ee892STomi Valkeinen _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table, 909f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table), mask, 910f76ee892STomi Valkeinen DSI_IRQENABLE, DSI_IRQSTATUS); 911f76ee892STomi Valkeinen } 912f76ee892STomi Valkeinen 913f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */ 914f76ee892STomi Valkeinen static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc) 915f76ee892STomi Valkeinen { 916f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 917f76ee892STomi Valkeinen 918f76ee892STomi Valkeinen _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc], 919f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]), 920f76ee892STomi Valkeinen DSI_VC_IRQ_ERROR_MASK, 921f76ee892STomi Valkeinen DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc)); 922f76ee892STomi Valkeinen } 923f76ee892STomi Valkeinen 924f76ee892STomi Valkeinen /* dsi->irq_lock has to be locked by the caller */ 925f76ee892STomi Valkeinen static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev) 926f76ee892STomi Valkeinen { 927f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 928f76ee892STomi Valkeinen 929f76ee892STomi Valkeinen _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio, 930f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table_cio), 931f76ee892STomi Valkeinen DSI_CIO_IRQ_ERROR_MASK, 932f76ee892STomi Valkeinen DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS); 933f76ee892STomi Valkeinen } 934f76ee892STomi Valkeinen 935f76ee892STomi Valkeinen static void _dsi_initialize_irq(struct platform_device *dsidev) 936f76ee892STomi Valkeinen { 937f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 938f76ee892STomi Valkeinen unsigned long flags; 939f76ee892STomi Valkeinen int vc; 940f76ee892STomi Valkeinen 941f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_lock, flags); 942f76ee892STomi Valkeinen 943f76ee892STomi Valkeinen memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables)); 944f76ee892STomi Valkeinen 945f76ee892STomi Valkeinen _omap_dsi_set_irqs(dsidev); 946f76ee892STomi Valkeinen for (vc = 0; vc < 4; ++vc) 947f76ee892STomi Valkeinen _omap_dsi_set_irqs_vc(dsidev, vc); 948f76ee892STomi Valkeinen _omap_dsi_set_irqs_cio(dsidev); 949f76ee892STomi Valkeinen 950f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_lock, flags); 951f76ee892STomi Valkeinen } 952f76ee892STomi Valkeinen 953f76ee892STomi Valkeinen static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask, 954f76ee892STomi Valkeinen struct dsi_isr_data *isr_array, unsigned isr_array_size) 955f76ee892STomi Valkeinen { 956f76ee892STomi Valkeinen struct dsi_isr_data *isr_data; 957f76ee892STomi Valkeinen int free_idx; 958f76ee892STomi Valkeinen int i; 959f76ee892STomi Valkeinen 960f76ee892STomi Valkeinen BUG_ON(isr == NULL); 961f76ee892STomi Valkeinen 962f76ee892STomi Valkeinen /* check for duplicate entry and find a free slot */ 963f76ee892STomi Valkeinen free_idx = -1; 964f76ee892STomi Valkeinen for (i = 0; i < isr_array_size; i++) { 965f76ee892STomi Valkeinen isr_data = &isr_array[i]; 966f76ee892STomi Valkeinen 967f76ee892STomi Valkeinen if (isr_data->isr == isr && isr_data->arg == arg && 968f76ee892STomi Valkeinen isr_data->mask == mask) { 969f76ee892STomi Valkeinen return -EINVAL; 970f76ee892STomi Valkeinen } 971f76ee892STomi Valkeinen 972f76ee892STomi Valkeinen if (isr_data->isr == NULL && free_idx == -1) 973f76ee892STomi Valkeinen free_idx = i; 974f76ee892STomi Valkeinen } 975f76ee892STomi Valkeinen 976f76ee892STomi Valkeinen if (free_idx == -1) 977f76ee892STomi Valkeinen return -EBUSY; 978f76ee892STomi Valkeinen 979f76ee892STomi Valkeinen isr_data = &isr_array[free_idx]; 980f76ee892STomi Valkeinen isr_data->isr = isr; 981f76ee892STomi Valkeinen isr_data->arg = arg; 982f76ee892STomi Valkeinen isr_data->mask = mask; 983f76ee892STomi Valkeinen 984f76ee892STomi Valkeinen return 0; 985f76ee892STomi Valkeinen } 986f76ee892STomi Valkeinen 987f76ee892STomi Valkeinen static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask, 988f76ee892STomi Valkeinen struct dsi_isr_data *isr_array, unsigned isr_array_size) 989f76ee892STomi Valkeinen { 990f76ee892STomi Valkeinen struct dsi_isr_data *isr_data; 991f76ee892STomi Valkeinen int i; 992f76ee892STomi Valkeinen 993f76ee892STomi Valkeinen for (i = 0; i < isr_array_size; i++) { 994f76ee892STomi Valkeinen isr_data = &isr_array[i]; 995f76ee892STomi Valkeinen if (isr_data->isr != isr || isr_data->arg != arg || 996f76ee892STomi Valkeinen isr_data->mask != mask) 997f76ee892STomi Valkeinen continue; 998f76ee892STomi Valkeinen 999f76ee892STomi Valkeinen isr_data->isr = NULL; 1000f76ee892STomi Valkeinen isr_data->arg = NULL; 1001f76ee892STomi Valkeinen isr_data->mask = 0; 1002f76ee892STomi Valkeinen 1003f76ee892STomi Valkeinen return 0; 1004f76ee892STomi Valkeinen } 1005f76ee892STomi Valkeinen 1006f76ee892STomi Valkeinen return -EINVAL; 1007f76ee892STomi Valkeinen } 1008f76ee892STomi Valkeinen 1009f76ee892STomi Valkeinen static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr, 1010f76ee892STomi Valkeinen void *arg, u32 mask) 1011f76ee892STomi Valkeinen { 1012f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1013f76ee892STomi Valkeinen unsigned long flags; 1014f76ee892STomi Valkeinen int r; 1015f76ee892STomi Valkeinen 1016f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_lock, flags); 1017f76ee892STomi Valkeinen 1018f76ee892STomi Valkeinen r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table, 1019f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table)); 1020f76ee892STomi Valkeinen 1021f76ee892STomi Valkeinen if (r == 0) 1022f76ee892STomi Valkeinen _omap_dsi_set_irqs(dsidev); 1023f76ee892STomi Valkeinen 1024f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_lock, flags); 1025f76ee892STomi Valkeinen 1026f76ee892STomi Valkeinen return r; 1027f76ee892STomi Valkeinen } 1028f76ee892STomi Valkeinen 1029f76ee892STomi Valkeinen static int dsi_unregister_isr(struct platform_device *dsidev, 1030f76ee892STomi Valkeinen omap_dsi_isr_t isr, void *arg, u32 mask) 1031f76ee892STomi Valkeinen { 1032f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1033f76ee892STomi Valkeinen unsigned long flags; 1034f76ee892STomi Valkeinen int r; 1035f76ee892STomi Valkeinen 1036f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_lock, flags); 1037f76ee892STomi Valkeinen 1038f76ee892STomi Valkeinen r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table, 1039f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table)); 1040f76ee892STomi Valkeinen 1041f76ee892STomi Valkeinen if (r == 0) 1042f76ee892STomi Valkeinen _omap_dsi_set_irqs(dsidev); 1043f76ee892STomi Valkeinen 1044f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_lock, flags); 1045f76ee892STomi Valkeinen 1046f76ee892STomi Valkeinen return r; 1047f76ee892STomi Valkeinen } 1048f76ee892STomi Valkeinen 1049f76ee892STomi Valkeinen static int dsi_register_isr_vc(struct platform_device *dsidev, int channel, 1050f76ee892STomi Valkeinen omap_dsi_isr_t isr, void *arg, u32 mask) 1051f76ee892STomi Valkeinen { 1052f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1053f76ee892STomi Valkeinen unsigned long flags; 1054f76ee892STomi Valkeinen int r; 1055f76ee892STomi Valkeinen 1056f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_lock, flags); 1057f76ee892STomi Valkeinen 1058f76ee892STomi Valkeinen r = _dsi_register_isr(isr, arg, mask, 1059f76ee892STomi Valkeinen dsi->isr_tables.isr_table_vc[channel], 1060f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); 1061f76ee892STomi Valkeinen 1062f76ee892STomi Valkeinen if (r == 0) 1063f76ee892STomi Valkeinen _omap_dsi_set_irqs_vc(dsidev, channel); 1064f76ee892STomi Valkeinen 1065f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_lock, flags); 1066f76ee892STomi Valkeinen 1067f76ee892STomi Valkeinen return r; 1068f76ee892STomi Valkeinen } 1069f76ee892STomi Valkeinen 1070f76ee892STomi Valkeinen static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel, 1071f76ee892STomi Valkeinen omap_dsi_isr_t isr, void *arg, u32 mask) 1072f76ee892STomi Valkeinen { 1073f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1074f76ee892STomi Valkeinen unsigned long flags; 1075f76ee892STomi Valkeinen int r; 1076f76ee892STomi Valkeinen 1077f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_lock, flags); 1078f76ee892STomi Valkeinen 1079f76ee892STomi Valkeinen r = _dsi_unregister_isr(isr, arg, mask, 1080f76ee892STomi Valkeinen dsi->isr_tables.isr_table_vc[channel], 1081f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); 1082f76ee892STomi Valkeinen 1083f76ee892STomi Valkeinen if (r == 0) 1084f76ee892STomi Valkeinen _omap_dsi_set_irqs_vc(dsidev, channel); 1085f76ee892STomi Valkeinen 1086f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_lock, flags); 1087f76ee892STomi Valkeinen 1088f76ee892STomi Valkeinen return r; 1089f76ee892STomi Valkeinen } 1090f76ee892STomi Valkeinen 1091f76ee892STomi Valkeinen static int dsi_register_isr_cio(struct platform_device *dsidev, 1092f76ee892STomi Valkeinen omap_dsi_isr_t isr, void *arg, u32 mask) 1093f76ee892STomi Valkeinen { 1094f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1095f76ee892STomi Valkeinen unsigned long flags; 1096f76ee892STomi Valkeinen int r; 1097f76ee892STomi Valkeinen 1098f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_lock, flags); 1099f76ee892STomi Valkeinen 1100f76ee892STomi Valkeinen r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, 1101f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); 1102f76ee892STomi Valkeinen 1103f76ee892STomi Valkeinen if (r == 0) 1104f76ee892STomi Valkeinen _omap_dsi_set_irqs_cio(dsidev); 1105f76ee892STomi Valkeinen 1106f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_lock, flags); 1107f76ee892STomi Valkeinen 1108f76ee892STomi Valkeinen return r; 1109f76ee892STomi Valkeinen } 1110f76ee892STomi Valkeinen 1111f76ee892STomi Valkeinen static int dsi_unregister_isr_cio(struct platform_device *dsidev, 1112f76ee892STomi Valkeinen omap_dsi_isr_t isr, void *arg, u32 mask) 1113f76ee892STomi Valkeinen { 1114f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1115f76ee892STomi Valkeinen unsigned long flags; 1116f76ee892STomi Valkeinen int r; 1117f76ee892STomi Valkeinen 1118f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_lock, flags); 1119f76ee892STomi Valkeinen 1120f76ee892STomi Valkeinen r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, 1121f76ee892STomi Valkeinen ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); 1122f76ee892STomi Valkeinen 1123f76ee892STomi Valkeinen if (r == 0) 1124f76ee892STomi Valkeinen _omap_dsi_set_irqs_cio(dsidev); 1125f76ee892STomi Valkeinen 1126f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_lock, flags); 1127f76ee892STomi Valkeinen 1128f76ee892STomi Valkeinen return r; 1129f76ee892STomi Valkeinen } 1130f76ee892STomi Valkeinen 1131f76ee892STomi Valkeinen static u32 dsi_get_errors(struct platform_device *dsidev) 1132f76ee892STomi Valkeinen { 1133f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1134f76ee892STomi Valkeinen unsigned long flags; 1135f76ee892STomi Valkeinen u32 e; 1136f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->errors_lock, flags); 1137f76ee892STomi Valkeinen e = dsi->errors; 1138f76ee892STomi Valkeinen dsi->errors = 0; 1139f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->errors_lock, flags); 1140f76ee892STomi Valkeinen return e; 1141f76ee892STomi Valkeinen } 1142f76ee892STomi Valkeinen 1143f76ee892STomi Valkeinen static int dsi_runtime_get(struct platform_device *dsidev) 1144f76ee892STomi Valkeinen { 1145f76ee892STomi Valkeinen int r; 1146f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1147f76ee892STomi Valkeinen 1148f76ee892STomi Valkeinen DSSDBG("dsi_runtime_get\n"); 1149f76ee892STomi Valkeinen 1150f76ee892STomi Valkeinen r = pm_runtime_get_sync(&dsi->pdev->dev); 1151f76ee892STomi Valkeinen WARN_ON(r < 0); 1152f76ee892STomi Valkeinen return r < 0 ? r : 0; 1153f76ee892STomi Valkeinen } 1154f76ee892STomi Valkeinen 1155f76ee892STomi Valkeinen static void dsi_runtime_put(struct platform_device *dsidev) 1156f76ee892STomi Valkeinen { 1157f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1158f76ee892STomi Valkeinen int r; 1159f76ee892STomi Valkeinen 1160f76ee892STomi Valkeinen DSSDBG("dsi_runtime_put\n"); 1161f76ee892STomi Valkeinen 1162f76ee892STomi Valkeinen r = pm_runtime_put_sync(&dsi->pdev->dev); 1163f76ee892STomi Valkeinen WARN_ON(r < 0 && r != -ENOSYS); 1164f76ee892STomi Valkeinen } 1165f76ee892STomi Valkeinen 1166f76ee892STomi Valkeinen static int dsi_regulator_init(struct platform_device *dsidev) 1167f76ee892STomi Valkeinen { 1168f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1169f76ee892STomi Valkeinen struct regulator *vdds_dsi; 1170f76ee892STomi Valkeinen 1171f76ee892STomi Valkeinen if (dsi->vdds_dsi_reg != NULL) 1172f76ee892STomi Valkeinen return 0; 1173f76ee892STomi Valkeinen 1174f76ee892STomi Valkeinen vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdd"); 1175f76ee892STomi Valkeinen 1176f76ee892STomi Valkeinen if (IS_ERR(vdds_dsi)) { 1177f76ee892STomi Valkeinen if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER) 1178f76ee892STomi Valkeinen DSSERR("can't get DSI VDD regulator\n"); 1179f76ee892STomi Valkeinen return PTR_ERR(vdds_dsi); 1180f76ee892STomi Valkeinen } 1181f76ee892STomi Valkeinen 1182f76ee892STomi Valkeinen dsi->vdds_dsi_reg = vdds_dsi; 1183f76ee892STomi Valkeinen 1184f76ee892STomi Valkeinen return 0; 1185f76ee892STomi Valkeinen } 1186f76ee892STomi Valkeinen 1187f76ee892STomi Valkeinen static void _dsi_print_reset_status(struct platform_device *dsidev) 1188f76ee892STomi Valkeinen { 1189f76ee892STomi Valkeinen u32 l; 1190f76ee892STomi Valkeinen int b0, b1, b2; 1191f76ee892STomi Valkeinen 1192f76ee892STomi Valkeinen /* A dummy read using the SCP interface to any DSIPHY register is 1193f76ee892STomi Valkeinen * required after DSIPHY reset to complete the reset of the DSI complex 1194f76ee892STomi Valkeinen * I/O. */ 1195f76ee892STomi Valkeinen l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); 1196f76ee892STomi Valkeinen 1197f76ee892STomi Valkeinen if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) { 1198f76ee892STomi Valkeinen b0 = 28; 1199f76ee892STomi Valkeinen b1 = 27; 1200f76ee892STomi Valkeinen b2 = 26; 1201f76ee892STomi Valkeinen } else { 1202f76ee892STomi Valkeinen b0 = 24; 1203f76ee892STomi Valkeinen b1 = 25; 1204f76ee892STomi Valkeinen b2 = 26; 1205f76ee892STomi Valkeinen } 1206f76ee892STomi Valkeinen 1207f76ee892STomi Valkeinen #define DSI_FLD_GET(fld, start, end)\ 1208f76ee892STomi Valkeinen FLD_GET(dsi_read_reg(dsidev, DSI_##fld), start, end) 1209f76ee892STomi Valkeinen 1210f76ee892STomi Valkeinen pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n", 1211f76ee892STomi Valkeinen DSI_FLD_GET(PLL_STATUS, 0, 0), 1212f76ee892STomi Valkeinen DSI_FLD_GET(COMPLEXIO_CFG1, 29, 29), 1213f76ee892STomi Valkeinen DSI_FLD_GET(DSIPHY_CFG5, b0, b0), 1214f76ee892STomi Valkeinen DSI_FLD_GET(DSIPHY_CFG5, b1, b1), 1215f76ee892STomi Valkeinen DSI_FLD_GET(DSIPHY_CFG5, b2, b2), 1216f76ee892STomi Valkeinen DSI_FLD_GET(DSIPHY_CFG5, 29, 29), 1217f76ee892STomi Valkeinen DSI_FLD_GET(DSIPHY_CFG5, 30, 30), 1218f76ee892STomi Valkeinen DSI_FLD_GET(DSIPHY_CFG5, 31, 31)); 1219f76ee892STomi Valkeinen 1220f76ee892STomi Valkeinen #undef DSI_FLD_GET 1221f76ee892STomi Valkeinen } 1222f76ee892STomi Valkeinen 1223f76ee892STomi Valkeinen static inline int dsi_if_enable(struct platform_device *dsidev, bool enable) 1224f76ee892STomi Valkeinen { 1225f76ee892STomi Valkeinen DSSDBG("dsi_if_enable(%d)\n", enable); 1226f76ee892STomi Valkeinen 1227f76ee892STomi Valkeinen enable = enable ? 1 : 0; 1228f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */ 1229f76ee892STomi Valkeinen 1230f76ee892STomi Valkeinen if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) { 1231f76ee892STomi Valkeinen DSSERR("Failed to set dsi_if_enable to %d\n", enable); 1232f76ee892STomi Valkeinen return -EIO; 1233f76ee892STomi Valkeinen } 1234f76ee892STomi Valkeinen 1235f76ee892STomi Valkeinen return 0; 1236f76ee892STomi Valkeinen } 1237f76ee892STomi Valkeinen 1238f76ee892STomi Valkeinen static unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) 1239f76ee892STomi Valkeinen { 1240f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1241f76ee892STomi Valkeinen 1242f76ee892STomi Valkeinen return dsi->pll.cinfo.clkout[HSDIV_DISPC]; 1243f76ee892STomi Valkeinen } 1244f76ee892STomi Valkeinen 1245f76ee892STomi Valkeinen static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev) 1246f76ee892STomi Valkeinen { 1247f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1248f76ee892STomi Valkeinen 1249f76ee892STomi Valkeinen return dsi->pll.cinfo.clkout[HSDIV_DSI]; 1250f76ee892STomi Valkeinen } 1251f76ee892STomi Valkeinen 1252f76ee892STomi Valkeinen static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) 1253f76ee892STomi Valkeinen { 1254f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1255f76ee892STomi Valkeinen 1256f76ee892STomi Valkeinen return dsi->pll.cinfo.clkdco / 16; 1257f76ee892STomi Valkeinen } 1258f76ee892STomi Valkeinen 1259f76ee892STomi Valkeinen static unsigned long dsi_fclk_rate(struct platform_device *dsidev) 1260f76ee892STomi Valkeinen { 1261f76ee892STomi Valkeinen unsigned long r; 1262f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1263f76ee892STomi Valkeinen 1264f76ee892STomi Valkeinen if (dss_get_dsi_clk_source(dsi->module_id) == OMAP_DSS_CLK_SRC_FCK) { 1265f76ee892STomi Valkeinen /* DSI FCLK source is DSS_CLK_FCK */ 1266f76ee892STomi Valkeinen r = clk_get_rate(dsi->dss_clk); 1267f76ee892STomi Valkeinen } else { 1268f76ee892STomi Valkeinen /* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */ 1269f76ee892STomi Valkeinen r = dsi_get_pll_hsdiv_dsi_rate(dsidev); 1270f76ee892STomi Valkeinen } 1271f76ee892STomi Valkeinen 1272f76ee892STomi Valkeinen return r; 1273f76ee892STomi Valkeinen } 1274f76ee892STomi Valkeinen 1275f76ee892STomi Valkeinen static int dsi_lp_clock_calc(unsigned long dsi_fclk, 1276f76ee892STomi Valkeinen unsigned long lp_clk_min, unsigned long lp_clk_max, 1277f76ee892STomi Valkeinen struct dsi_lp_clock_info *lp_cinfo) 1278f76ee892STomi Valkeinen { 1279f76ee892STomi Valkeinen unsigned lp_clk_div; 1280f76ee892STomi Valkeinen unsigned long lp_clk; 1281f76ee892STomi Valkeinen 1282f76ee892STomi Valkeinen lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk_max * 2); 1283f76ee892STomi Valkeinen lp_clk = dsi_fclk / 2 / lp_clk_div; 1284f76ee892STomi Valkeinen 1285f76ee892STomi Valkeinen if (lp_clk < lp_clk_min || lp_clk > lp_clk_max) 1286f76ee892STomi Valkeinen return -EINVAL; 1287f76ee892STomi Valkeinen 1288f76ee892STomi Valkeinen lp_cinfo->lp_clk_div = lp_clk_div; 1289f76ee892STomi Valkeinen lp_cinfo->lp_clk = lp_clk; 1290f76ee892STomi Valkeinen 1291f76ee892STomi Valkeinen return 0; 1292f76ee892STomi Valkeinen } 1293f76ee892STomi Valkeinen 1294f76ee892STomi Valkeinen static int dsi_set_lp_clk_divisor(struct platform_device *dsidev) 1295f76ee892STomi Valkeinen { 1296f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1297f76ee892STomi Valkeinen unsigned long dsi_fclk; 1298f76ee892STomi Valkeinen unsigned lp_clk_div; 1299f76ee892STomi Valkeinen unsigned long lp_clk; 1300f76ee892STomi Valkeinen unsigned lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV); 1301f76ee892STomi Valkeinen 1302f76ee892STomi Valkeinen 1303f76ee892STomi Valkeinen lp_clk_div = dsi->user_lp_cinfo.lp_clk_div; 1304f76ee892STomi Valkeinen 1305f76ee892STomi Valkeinen if (lp_clk_div == 0 || lp_clk_div > lpdiv_max) 1306f76ee892STomi Valkeinen return -EINVAL; 1307f76ee892STomi Valkeinen 1308f76ee892STomi Valkeinen dsi_fclk = dsi_fclk_rate(dsidev); 1309f76ee892STomi Valkeinen 1310f76ee892STomi Valkeinen lp_clk = dsi_fclk / 2 / lp_clk_div; 1311f76ee892STomi Valkeinen 1312f76ee892STomi Valkeinen DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); 1313f76ee892STomi Valkeinen dsi->current_lp_cinfo.lp_clk = lp_clk; 1314f76ee892STomi Valkeinen dsi->current_lp_cinfo.lp_clk_div = lp_clk_div; 1315f76ee892STomi Valkeinen 1316f76ee892STomi Valkeinen /* LP_CLK_DIVISOR */ 1317f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0); 1318f76ee892STomi Valkeinen 1319f76ee892STomi Valkeinen /* LP_RX_SYNCHRO_ENABLE */ 1320f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21); 1321f76ee892STomi Valkeinen 1322f76ee892STomi Valkeinen return 0; 1323f76ee892STomi Valkeinen } 1324f76ee892STomi Valkeinen 1325f76ee892STomi Valkeinen static void dsi_enable_scp_clk(struct platform_device *dsidev) 1326f76ee892STomi Valkeinen { 1327f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1328f76ee892STomi Valkeinen 1329f76ee892STomi Valkeinen if (dsi->scp_clk_refcount++ == 0) 1330f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */ 1331f76ee892STomi Valkeinen } 1332f76ee892STomi Valkeinen 1333f76ee892STomi Valkeinen static void dsi_disable_scp_clk(struct platform_device *dsidev) 1334f76ee892STomi Valkeinen { 1335f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1336f76ee892STomi Valkeinen 1337f76ee892STomi Valkeinen WARN_ON(dsi->scp_clk_refcount == 0); 1338f76ee892STomi Valkeinen if (--dsi->scp_clk_refcount == 0) 1339f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */ 1340f76ee892STomi Valkeinen } 1341f76ee892STomi Valkeinen 1342f76ee892STomi Valkeinen enum dsi_pll_power_state { 1343f76ee892STomi Valkeinen DSI_PLL_POWER_OFF = 0x0, 1344f76ee892STomi Valkeinen DSI_PLL_POWER_ON_HSCLK = 0x1, 1345f76ee892STomi Valkeinen DSI_PLL_POWER_ON_ALL = 0x2, 1346f76ee892STomi Valkeinen DSI_PLL_POWER_ON_DIV = 0x3, 1347f76ee892STomi Valkeinen }; 1348f76ee892STomi Valkeinen 1349f76ee892STomi Valkeinen static int dsi_pll_power(struct platform_device *dsidev, 1350f76ee892STomi Valkeinen enum dsi_pll_power_state state) 1351f76ee892STomi Valkeinen { 1352f76ee892STomi Valkeinen int t = 0; 1353f76ee892STomi Valkeinen 1354f76ee892STomi Valkeinen /* DSI-PLL power command 0x3 is not working */ 1355f76ee892STomi Valkeinen if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) && 1356f76ee892STomi Valkeinen state == DSI_PLL_POWER_ON_DIV) 1357f76ee892STomi Valkeinen state = DSI_PLL_POWER_ON_ALL; 1358f76ee892STomi Valkeinen 1359f76ee892STomi Valkeinen /* PLL_PWR_CMD */ 1360f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30); 1361f76ee892STomi Valkeinen 1362f76ee892STomi Valkeinen /* PLL_PWR_STATUS */ 1363f76ee892STomi Valkeinen while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) { 1364f76ee892STomi Valkeinen if (++t > 1000) { 1365f76ee892STomi Valkeinen DSSERR("Failed to set DSI PLL power mode to %d\n", 1366f76ee892STomi Valkeinen state); 1367f76ee892STomi Valkeinen return -ENODEV; 1368f76ee892STomi Valkeinen } 1369f76ee892STomi Valkeinen udelay(1); 1370f76ee892STomi Valkeinen } 1371f76ee892STomi Valkeinen 1372f76ee892STomi Valkeinen return 0; 1373f76ee892STomi Valkeinen } 1374f76ee892STomi Valkeinen 1375f76ee892STomi Valkeinen 1376f76ee892STomi Valkeinen static void dsi_pll_calc_dsi_fck(struct dss_pll_clock_info *cinfo) 1377f76ee892STomi Valkeinen { 1378f76ee892STomi Valkeinen unsigned long max_dsi_fck; 1379f76ee892STomi Valkeinen 1380f76ee892STomi Valkeinen max_dsi_fck = dss_feat_get_param_max(FEAT_PARAM_DSI_FCK); 1381f76ee892STomi Valkeinen 1382f76ee892STomi Valkeinen cinfo->mX[HSDIV_DSI] = DIV_ROUND_UP(cinfo->clkdco, max_dsi_fck); 1383f76ee892STomi Valkeinen cinfo->clkout[HSDIV_DSI] = cinfo->clkdco / cinfo->mX[HSDIV_DSI]; 1384f76ee892STomi Valkeinen } 1385f76ee892STomi Valkeinen 1386f76ee892STomi Valkeinen static int dsi_pll_enable(struct dss_pll *pll) 1387f76ee892STomi Valkeinen { 1388f76ee892STomi Valkeinen struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); 1389f76ee892STomi Valkeinen struct platform_device *dsidev = dsi->pdev; 1390f76ee892STomi Valkeinen int r = 0; 1391f76ee892STomi Valkeinen 1392f76ee892STomi Valkeinen DSSDBG("PLL init\n"); 1393f76ee892STomi Valkeinen 1394f76ee892STomi Valkeinen r = dsi_regulator_init(dsidev); 1395f76ee892STomi Valkeinen if (r) 1396f76ee892STomi Valkeinen return r; 1397f76ee892STomi Valkeinen 1398f76ee892STomi Valkeinen r = dsi_runtime_get(dsidev); 1399f76ee892STomi Valkeinen if (r) 1400f76ee892STomi Valkeinen return r; 1401f76ee892STomi Valkeinen 1402f76ee892STomi Valkeinen /* 1403f76ee892STomi Valkeinen * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4. 1404f76ee892STomi Valkeinen */ 1405f76ee892STomi Valkeinen dsi_enable_scp_clk(dsidev); 1406f76ee892STomi Valkeinen 1407f76ee892STomi Valkeinen if (!dsi->vdds_dsi_enabled) { 1408f76ee892STomi Valkeinen r = regulator_enable(dsi->vdds_dsi_reg); 1409f76ee892STomi Valkeinen if (r) 1410f76ee892STomi Valkeinen goto err0; 1411f76ee892STomi Valkeinen dsi->vdds_dsi_enabled = true; 1412f76ee892STomi Valkeinen } 1413f76ee892STomi Valkeinen 1414f76ee892STomi Valkeinen /* XXX PLL does not come out of reset without this... */ 1415f76ee892STomi Valkeinen dispc_pck_free_enable(1); 1416f76ee892STomi Valkeinen 1417f76ee892STomi Valkeinen if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) { 1418f76ee892STomi Valkeinen DSSERR("PLL not coming out of reset.\n"); 1419f76ee892STomi Valkeinen r = -ENODEV; 1420f76ee892STomi Valkeinen dispc_pck_free_enable(0); 1421f76ee892STomi Valkeinen goto err1; 1422f76ee892STomi Valkeinen } 1423f76ee892STomi Valkeinen 1424f76ee892STomi Valkeinen /* XXX ... but if left on, we get problems when planes do not 1425f76ee892STomi Valkeinen * fill the whole display. No idea about this */ 1426f76ee892STomi Valkeinen dispc_pck_free_enable(0); 1427f76ee892STomi Valkeinen 1428f76ee892STomi Valkeinen r = dsi_pll_power(dsidev, DSI_PLL_POWER_ON_ALL); 1429f76ee892STomi Valkeinen 1430f76ee892STomi Valkeinen if (r) 1431f76ee892STomi Valkeinen goto err1; 1432f76ee892STomi Valkeinen 1433f76ee892STomi Valkeinen DSSDBG("PLL init done\n"); 1434f76ee892STomi Valkeinen 1435f76ee892STomi Valkeinen return 0; 1436f76ee892STomi Valkeinen err1: 1437f76ee892STomi Valkeinen if (dsi->vdds_dsi_enabled) { 1438f76ee892STomi Valkeinen regulator_disable(dsi->vdds_dsi_reg); 1439f76ee892STomi Valkeinen dsi->vdds_dsi_enabled = false; 1440f76ee892STomi Valkeinen } 1441f76ee892STomi Valkeinen err0: 1442f76ee892STomi Valkeinen dsi_disable_scp_clk(dsidev); 1443f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 1444f76ee892STomi Valkeinen return r; 1445f76ee892STomi Valkeinen } 1446f76ee892STomi Valkeinen 1447f76ee892STomi Valkeinen static void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes) 1448f76ee892STomi Valkeinen { 1449f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1450f76ee892STomi Valkeinen 1451f76ee892STomi Valkeinen dsi_pll_power(dsidev, DSI_PLL_POWER_OFF); 1452f76ee892STomi Valkeinen if (disconnect_lanes) { 1453f76ee892STomi Valkeinen WARN_ON(!dsi->vdds_dsi_enabled); 1454f76ee892STomi Valkeinen regulator_disable(dsi->vdds_dsi_reg); 1455f76ee892STomi Valkeinen dsi->vdds_dsi_enabled = false; 1456f76ee892STomi Valkeinen } 1457f76ee892STomi Valkeinen 1458f76ee892STomi Valkeinen dsi_disable_scp_clk(dsidev); 1459f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 1460f76ee892STomi Valkeinen 1461f76ee892STomi Valkeinen DSSDBG("PLL uninit done\n"); 1462f76ee892STomi Valkeinen } 1463f76ee892STomi Valkeinen 1464f76ee892STomi Valkeinen static void dsi_pll_disable(struct dss_pll *pll) 1465f76ee892STomi Valkeinen { 1466f76ee892STomi Valkeinen struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); 1467f76ee892STomi Valkeinen struct platform_device *dsidev = dsi->pdev; 1468f76ee892STomi Valkeinen 1469f76ee892STomi Valkeinen dsi_pll_uninit(dsidev, true); 1470f76ee892STomi Valkeinen } 1471f76ee892STomi Valkeinen 1472f76ee892STomi Valkeinen static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, 1473f76ee892STomi Valkeinen struct seq_file *s) 1474f76ee892STomi Valkeinen { 1475f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1476f76ee892STomi Valkeinen struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo; 1477f76ee892STomi Valkeinen enum omap_dss_clk_source dispc_clk_src, dsi_clk_src; 1478f76ee892STomi Valkeinen int dsi_module = dsi->module_id; 1479f76ee892STomi Valkeinen struct dss_pll *pll = &dsi->pll; 1480f76ee892STomi Valkeinen 1481f76ee892STomi Valkeinen dispc_clk_src = dss_get_dispc_clk_source(); 1482f76ee892STomi Valkeinen dsi_clk_src = dss_get_dsi_clk_source(dsi_module); 1483f76ee892STomi Valkeinen 1484f76ee892STomi Valkeinen if (dsi_runtime_get(dsidev)) 1485f76ee892STomi Valkeinen return; 1486f76ee892STomi Valkeinen 1487f76ee892STomi Valkeinen seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); 1488f76ee892STomi Valkeinen 1489f76ee892STomi Valkeinen seq_printf(s, "dsi pll clkin\t%lu\n", clk_get_rate(pll->clkin)); 1490f76ee892STomi Valkeinen 1491f76ee892STomi Valkeinen seq_printf(s, "Fint\t\t%-16lun %u\n", cinfo->fint, cinfo->n); 1492f76ee892STomi Valkeinen 1493f76ee892STomi Valkeinen seq_printf(s, "CLKIN4DDR\t%-16lum %u\n", 1494f76ee892STomi Valkeinen cinfo->clkdco, cinfo->m); 1495f76ee892STomi Valkeinen 1496f76ee892STomi Valkeinen seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16lum_dispc %u\t(%s)\n", 1497f76ee892STomi Valkeinen dss_feat_get_clk_source_name(dsi_module == 0 ? 1498f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC : 1499f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC), 1500f76ee892STomi Valkeinen cinfo->clkout[HSDIV_DISPC], 1501f76ee892STomi Valkeinen cinfo->mX[HSDIV_DISPC], 1502f76ee892STomi Valkeinen dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ? 1503f76ee892STomi Valkeinen "off" : "on"); 1504f76ee892STomi Valkeinen 1505f76ee892STomi Valkeinen seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16lum_dsi %u\t(%s)\n", 1506f76ee892STomi Valkeinen dss_feat_get_clk_source_name(dsi_module == 0 ? 1507f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI : 1508f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI), 1509f76ee892STomi Valkeinen cinfo->clkout[HSDIV_DSI], 1510f76ee892STomi Valkeinen cinfo->mX[HSDIV_DSI], 1511f76ee892STomi Valkeinen dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ? 1512f76ee892STomi Valkeinen "off" : "on"); 1513f76ee892STomi Valkeinen 1514f76ee892STomi Valkeinen seq_printf(s, "- DSI%d -\n", dsi_module + 1); 1515f76ee892STomi Valkeinen 1516f76ee892STomi Valkeinen seq_printf(s, "dsi fclk source = %s (%s)\n", 1517f76ee892STomi Valkeinen dss_get_generic_clk_source_name(dsi_clk_src), 1518f76ee892STomi Valkeinen dss_feat_get_clk_source_name(dsi_clk_src)); 1519f76ee892STomi Valkeinen 1520f76ee892STomi Valkeinen seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev)); 1521f76ee892STomi Valkeinen 1522f76ee892STomi Valkeinen seq_printf(s, "DDR_CLK\t\t%lu\n", 1523f76ee892STomi Valkeinen cinfo->clkdco / 4); 1524f76ee892STomi Valkeinen 1525f76ee892STomi Valkeinen seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev)); 1526f76ee892STomi Valkeinen 1527f76ee892STomi Valkeinen seq_printf(s, "LP_CLK\t\t%lu\n", dsi->current_lp_cinfo.lp_clk); 1528f76ee892STomi Valkeinen 1529f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 1530f76ee892STomi Valkeinen } 1531f76ee892STomi Valkeinen 1532f76ee892STomi Valkeinen void dsi_dump_clocks(struct seq_file *s) 1533f76ee892STomi Valkeinen { 1534f76ee892STomi Valkeinen struct platform_device *dsidev; 1535f76ee892STomi Valkeinen int i; 1536f76ee892STomi Valkeinen 1537f76ee892STomi Valkeinen for (i = 0; i < MAX_NUM_DSI; i++) { 1538f76ee892STomi Valkeinen dsidev = dsi_get_dsidev_from_id(i); 1539f76ee892STomi Valkeinen if (dsidev) 1540f76ee892STomi Valkeinen dsi_dump_dsidev_clocks(dsidev, s); 1541f76ee892STomi Valkeinen } 1542f76ee892STomi Valkeinen } 1543f76ee892STomi Valkeinen 154435b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 1545f76ee892STomi Valkeinen static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, 1546f76ee892STomi Valkeinen struct seq_file *s) 1547f76ee892STomi Valkeinen { 1548f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1549f76ee892STomi Valkeinen unsigned long flags; 1550f76ee892STomi Valkeinen struct dsi_irq_stats stats; 1551f76ee892STomi Valkeinen 1552f76ee892STomi Valkeinen spin_lock_irqsave(&dsi->irq_stats_lock, flags); 1553f76ee892STomi Valkeinen 1554f76ee892STomi Valkeinen stats = dsi->irq_stats; 1555f76ee892STomi Valkeinen memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats)); 1556f76ee892STomi Valkeinen dsi->irq_stats.last_reset = jiffies; 1557f76ee892STomi Valkeinen 1558f76ee892STomi Valkeinen spin_unlock_irqrestore(&dsi->irq_stats_lock, flags); 1559f76ee892STomi Valkeinen 1560f76ee892STomi Valkeinen seq_printf(s, "period %u ms\n", 1561f76ee892STomi Valkeinen jiffies_to_msecs(jiffies - stats.last_reset)); 1562f76ee892STomi Valkeinen 1563f76ee892STomi Valkeinen seq_printf(s, "irqs %d\n", stats.irq_count); 1564f76ee892STomi Valkeinen #define PIS(x) \ 1565f76ee892STomi Valkeinen seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); 1566f76ee892STomi Valkeinen 1567f76ee892STomi Valkeinen seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1); 1568f76ee892STomi Valkeinen PIS(VC0); 1569f76ee892STomi Valkeinen PIS(VC1); 1570f76ee892STomi Valkeinen PIS(VC2); 1571f76ee892STomi Valkeinen PIS(VC3); 1572f76ee892STomi Valkeinen PIS(WAKEUP); 1573f76ee892STomi Valkeinen PIS(RESYNC); 1574f76ee892STomi Valkeinen PIS(PLL_LOCK); 1575f76ee892STomi Valkeinen PIS(PLL_UNLOCK); 1576f76ee892STomi Valkeinen PIS(PLL_RECALL); 1577f76ee892STomi Valkeinen PIS(COMPLEXIO_ERR); 1578f76ee892STomi Valkeinen PIS(HS_TX_TIMEOUT); 1579f76ee892STomi Valkeinen PIS(LP_RX_TIMEOUT); 1580f76ee892STomi Valkeinen PIS(TE_TRIGGER); 1581f76ee892STomi Valkeinen PIS(ACK_TRIGGER); 1582f76ee892STomi Valkeinen PIS(SYNC_LOST); 1583f76ee892STomi Valkeinen PIS(LDO_POWER_GOOD); 1584f76ee892STomi Valkeinen PIS(TA_TIMEOUT); 1585f76ee892STomi Valkeinen #undef PIS 1586f76ee892STomi Valkeinen 1587f76ee892STomi Valkeinen #define PIS(x) \ 1588f76ee892STomi Valkeinen seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \ 1589f76ee892STomi Valkeinen stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \ 1590f76ee892STomi Valkeinen stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \ 1591f76ee892STomi Valkeinen stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \ 1592f76ee892STomi Valkeinen stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]); 1593f76ee892STomi Valkeinen 1594f76ee892STomi Valkeinen seq_printf(s, "-- VC interrupts --\n"); 1595f76ee892STomi Valkeinen PIS(CS); 1596f76ee892STomi Valkeinen PIS(ECC_CORR); 1597f76ee892STomi Valkeinen PIS(PACKET_SENT); 1598f76ee892STomi Valkeinen PIS(FIFO_TX_OVF); 1599f76ee892STomi Valkeinen PIS(FIFO_RX_OVF); 1600f76ee892STomi Valkeinen PIS(BTA); 1601f76ee892STomi Valkeinen PIS(ECC_NO_CORR); 1602f76ee892STomi Valkeinen PIS(FIFO_TX_UDF); 1603f76ee892STomi Valkeinen PIS(PP_BUSY_CHANGE); 1604f76ee892STomi Valkeinen #undef PIS 1605f76ee892STomi Valkeinen 1606f76ee892STomi Valkeinen #define PIS(x) \ 1607f76ee892STomi Valkeinen seq_printf(s, "%-20s %10d\n", #x, \ 1608f76ee892STomi Valkeinen stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]); 1609f76ee892STomi Valkeinen 1610f76ee892STomi Valkeinen seq_printf(s, "-- CIO interrupts --\n"); 1611f76ee892STomi Valkeinen PIS(ERRSYNCESC1); 1612f76ee892STomi Valkeinen PIS(ERRSYNCESC2); 1613f76ee892STomi Valkeinen PIS(ERRSYNCESC3); 1614f76ee892STomi Valkeinen PIS(ERRESC1); 1615f76ee892STomi Valkeinen PIS(ERRESC2); 1616f76ee892STomi Valkeinen PIS(ERRESC3); 1617f76ee892STomi Valkeinen PIS(ERRCONTROL1); 1618f76ee892STomi Valkeinen PIS(ERRCONTROL2); 1619f76ee892STomi Valkeinen PIS(ERRCONTROL3); 1620f76ee892STomi Valkeinen PIS(STATEULPS1); 1621f76ee892STomi Valkeinen PIS(STATEULPS2); 1622f76ee892STomi Valkeinen PIS(STATEULPS3); 1623f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP0_1); 1624f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP1_1); 1625f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP0_2); 1626f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP1_2); 1627f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP0_3); 1628f76ee892STomi Valkeinen PIS(ERRCONTENTIONLP1_3); 1629f76ee892STomi Valkeinen PIS(ULPSACTIVENOT_ALL0); 1630f76ee892STomi Valkeinen PIS(ULPSACTIVENOT_ALL1); 1631f76ee892STomi Valkeinen #undef PIS 1632f76ee892STomi Valkeinen } 1633f76ee892STomi Valkeinen 1634f76ee892STomi Valkeinen static void dsi1_dump_irqs(struct seq_file *s) 1635f76ee892STomi Valkeinen { 1636f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_id(0); 1637f76ee892STomi Valkeinen 1638f76ee892STomi Valkeinen dsi_dump_dsidev_irqs(dsidev, s); 1639f76ee892STomi Valkeinen } 1640f76ee892STomi Valkeinen 1641f76ee892STomi Valkeinen static void dsi2_dump_irqs(struct seq_file *s) 1642f76ee892STomi Valkeinen { 1643f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_id(1); 1644f76ee892STomi Valkeinen 1645f76ee892STomi Valkeinen dsi_dump_dsidev_irqs(dsidev, s); 1646f76ee892STomi Valkeinen } 1647f76ee892STomi Valkeinen #endif 1648f76ee892STomi Valkeinen 1649f76ee892STomi Valkeinen static void dsi_dump_dsidev_regs(struct platform_device *dsidev, 1650f76ee892STomi Valkeinen struct seq_file *s) 1651f76ee892STomi Valkeinen { 1652f76ee892STomi Valkeinen #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r)) 1653f76ee892STomi Valkeinen 1654f76ee892STomi Valkeinen if (dsi_runtime_get(dsidev)) 1655f76ee892STomi Valkeinen return; 1656f76ee892STomi Valkeinen dsi_enable_scp_clk(dsidev); 1657f76ee892STomi Valkeinen 1658f76ee892STomi Valkeinen DUMPREG(DSI_REVISION); 1659f76ee892STomi Valkeinen DUMPREG(DSI_SYSCONFIG); 1660f76ee892STomi Valkeinen DUMPREG(DSI_SYSSTATUS); 1661f76ee892STomi Valkeinen DUMPREG(DSI_IRQSTATUS); 1662f76ee892STomi Valkeinen DUMPREG(DSI_IRQENABLE); 1663f76ee892STomi Valkeinen DUMPREG(DSI_CTRL); 1664f76ee892STomi Valkeinen DUMPREG(DSI_COMPLEXIO_CFG1); 1665f76ee892STomi Valkeinen DUMPREG(DSI_COMPLEXIO_IRQ_STATUS); 1666f76ee892STomi Valkeinen DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE); 1667f76ee892STomi Valkeinen DUMPREG(DSI_CLK_CTRL); 1668f76ee892STomi Valkeinen DUMPREG(DSI_TIMING1); 1669f76ee892STomi Valkeinen DUMPREG(DSI_TIMING2); 1670f76ee892STomi Valkeinen DUMPREG(DSI_VM_TIMING1); 1671f76ee892STomi Valkeinen DUMPREG(DSI_VM_TIMING2); 1672f76ee892STomi Valkeinen DUMPREG(DSI_VM_TIMING3); 1673f76ee892STomi Valkeinen DUMPREG(DSI_CLK_TIMING); 1674f76ee892STomi Valkeinen DUMPREG(DSI_TX_FIFO_VC_SIZE); 1675f76ee892STomi Valkeinen DUMPREG(DSI_RX_FIFO_VC_SIZE); 1676f76ee892STomi Valkeinen DUMPREG(DSI_COMPLEXIO_CFG2); 1677f76ee892STomi Valkeinen DUMPREG(DSI_RX_FIFO_VC_FULLNESS); 1678f76ee892STomi Valkeinen DUMPREG(DSI_VM_TIMING4); 1679f76ee892STomi Valkeinen DUMPREG(DSI_TX_FIFO_VC_EMPTINESS); 1680f76ee892STomi Valkeinen DUMPREG(DSI_VM_TIMING5); 1681f76ee892STomi Valkeinen DUMPREG(DSI_VM_TIMING6); 1682f76ee892STomi Valkeinen DUMPREG(DSI_VM_TIMING7); 1683f76ee892STomi Valkeinen DUMPREG(DSI_STOPCLK_TIMING); 1684f76ee892STomi Valkeinen 1685f76ee892STomi Valkeinen DUMPREG(DSI_VC_CTRL(0)); 1686f76ee892STomi Valkeinen DUMPREG(DSI_VC_TE(0)); 1687f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_HEADER(0)); 1688f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0)); 1689f76ee892STomi Valkeinen DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0)); 1690f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQSTATUS(0)); 1691f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQENABLE(0)); 1692f76ee892STomi Valkeinen 1693f76ee892STomi Valkeinen DUMPREG(DSI_VC_CTRL(1)); 1694f76ee892STomi Valkeinen DUMPREG(DSI_VC_TE(1)); 1695f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_HEADER(1)); 1696f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1)); 1697f76ee892STomi Valkeinen DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1)); 1698f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQSTATUS(1)); 1699f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQENABLE(1)); 1700f76ee892STomi Valkeinen 1701f76ee892STomi Valkeinen DUMPREG(DSI_VC_CTRL(2)); 1702f76ee892STomi Valkeinen DUMPREG(DSI_VC_TE(2)); 1703f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_HEADER(2)); 1704f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2)); 1705f76ee892STomi Valkeinen DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2)); 1706f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQSTATUS(2)); 1707f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQENABLE(2)); 1708f76ee892STomi Valkeinen 1709f76ee892STomi Valkeinen DUMPREG(DSI_VC_CTRL(3)); 1710f76ee892STomi Valkeinen DUMPREG(DSI_VC_TE(3)); 1711f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_HEADER(3)); 1712f76ee892STomi Valkeinen DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3)); 1713f76ee892STomi Valkeinen DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3)); 1714f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQSTATUS(3)); 1715f76ee892STomi Valkeinen DUMPREG(DSI_VC_IRQENABLE(3)); 1716f76ee892STomi Valkeinen 1717f76ee892STomi Valkeinen DUMPREG(DSI_DSIPHY_CFG0); 1718f76ee892STomi Valkeinen DUMPREG(DSI_DSIPHY_CFG1); 1719f76ee892STomi Valkeinen DUMPREG(DSI_DSIPHY_CFG2); 1720f76ee892STomi Valkeinen DUMPREG(DSI_DSIPHY_CFG5); 1721f76ee892STomi Valkeinen 1722f76ee892STomi Valkeinen DUMPREG(DSI_PLL_CONTROL); 1723f76ee892STomi Valkeinen DUMPREG(DSI_PLL_STATUS); 1724f76ee892STomi Valkeinen DUMPREG(DSI_PLL_GO); 1725f76ee892STomi Valkeinen DUMPREG(DSI_PLL_CONFIGURATION1); 1726f76ee892STomi Valkeinen DUMPREG(DSI_PLL_CONFIGURATION2); 1727f76ee892STomi Valkeinen 1728f76ee892STomi Valkeinen dsi_disable_scp_clk(dsidev); 1729f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 1730f76ee892STomi Valkeinen #undef DUMPREG 1731f76ee892STomi Valkeinen } 1732f76ee892STomi Valkeinen 1733f76ee892STomi Valkeinen static void dsi1_dump_regs(struct seq_file *s) 1734f76ee892STomi Valkeinen { 1735f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_id(0); 1736f76ee892STomi Valkeinen 1737f76ee892STomi Valkeinen dsi_dump_dsidev_regs(dsidev, s); 1738f76ee892STomi Valkeinen } 1739f76ee892STomi Valkeinen 1740f76ee892STomi Valkeinen static void dsi2_dump_regs(struct seq_file *s) 1741f76ee892STomi Valkeinen { 1742f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_id(1); 1743f76ee892STomi Valkeinen 1744f76ee892STomi Valkeinen dsi_dump_dsidev_regs(dsidev, s); 1745f76ee892STomi Valkeinen } 1746f76ee892STomi Valkeinen 1747f76ee892STomi Valkeinen enum dsi_cio_power_state { 1748f76ee892STomi Valkeinen DSI_COMPLEXIO_POWER_OFF = 0x0, 1749f76ee892STomi Valkeinen DSI_COMPLEXIO_POWER_ON = 0x1, 1750f76ee892STomi Valkeinen DSI_COMPLEXIO_POWER_ULPS = 0x2, 1751f76ee892STomi Valkeinen }; 1752f76ee892STomi Valkeinen 1753f76ee892STomi Valkeinen static int dsi_cio_power(struct platform_device *dsidev, 1754f76ee892STomi Valkeinen enum dsi_cio_power_state state) 1755f76ee892STomi Valkeinen { 1756f76ee892STomi Valkeinen int t = 0; 1757f76ee892STomi Valkeinen 1758f76ee892STomi Valkeinen /* PWR_CMD */ 1759f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27); 1760f76ee892STomi Valkeinen 1761f76ee892STomi Valkeinen /* PWR_STATUS */ 1762f76ee892STomi Valkeinen while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1), 1763f76ee892STomi Valkeinen 26, 25) != state) { 1764f76ee892STomi Valkeinen if (++t > 1000) { 1765f76ee892STomi Valkeinen DSSERR("failed to set complexio power state to " 1766f76ee892STomi Valkeinen "%d\n", state); 1767f76ee892STomi Valkeinen return -ENODEV; 1768f76ee892STomi Valkeinen } 1769f76ee892STomi Valkeinen udelay(1); 1770f76ee892STomi Valkeinen } 1771f76ee892STomi Valkeinen 1772f76ee892STomi Valkeinen return 0; 1773f76ee892STomi Valkeinen } 1774f76ee892STomi Valkeinen 1775f76ee892STomi Valkeinen static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) 1776f76ee892STomi Valkeinen { 1777f76ee892STomi Valkeinen int val; 1778f76ee892STomi Valkeinen 1779f76ee892STomi Valkeinen /* line buffer on OMAP3 is 1024 x 24bits */ 1780f76ee892STomi Valkeinen /* XXX: for some reason using full buffer size causes 1781f76ee892STomi Valkeinen * considerable TX slowdown with update sizes that fill the 1782f76ee892STomi Valkeinen * whole buffer */ 1783f76ee892STomi Valkeinen if (!dss_has_feature(FEAT_DSI_GNQ)) 1784f76ee892STomi Valkeinen return 1023 * 3; 1785f76ee892STomi Valkeinen 1786f76ee892STomi Valkeinen val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */ 1787f76ee892STomi Valkeinen 1788f76ee892STomi Valkeinen switch (val) { 1789f76ee892STomi Valkeinen case 1: 1790f76ee892STomi Valkeinen return 512 * 3; /* 512x24 bits */ 1791f76ee892STomi Valkeinen case 2: 1792f76ee892STomi Valkeinen return 682 * 3; /* 682x24 bits */ 1793f76ee892STomi Valkeinen case 3: 1794f76ee892STomi Valkeinen return 853 * 3; /* 853x24 bits */ 1795f76ee892STomi Valkeinen case 4: 1796f76ee892STomi Valkeinen return 1024 * 3; /* 1024x24 bits */ 1797f76ee892STomi Valkeinen case 5: 1798f76ee892STomi Valkeinen return 1194 * 3; /* 1194x24 bits */ 1799f76ee892STomi Valkeinen case 6: 1800f76ee892STomi Valkeinen return 1365 * 3; /* 1365x24 bits */ 1801f76ee892STomi Valkeinen case 7: 1802f76ee892STomi Valkeinen return 1920 * 3; /* 1920x24 bits */ 1803f76ee892STomi Valkeinen default: 1804f76ee892STomi Valkeinen BUG(); 1805f76ee892STomi Valkeinen return 0; 1806f76ee892STomi Valkeinen } 1807f76ee892STomi Valkeinen } 1808f76ee892STomi Valkeinen 1809f76ee892STomi Valkeinen static int dsi_set_lane_config(struct platform_device *dsidev) 1810f76ee892STomi Valkeinen { 1811f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1812f76ee892STomi Valkeinen static const u8 offsets[] = { 0, 4, 8, 12, 16 }; 1813f76ee892STomi Valkeinen static const enum dsi_lane_function functions[] = { 1814f76ee892STomi Valkeinen DSI_LANE_CLK, 1815f76ee892STomi Valkeinen DSI_LANE_DATA1, 1816f76ee892STomi Valkeinen DSI_LANE_DATA2, 1817f76ee892STomi Valkeinen DSI_LANE_DATA3, 1818f76ee892STomi Valkeinen DSI_LANE_DATA4, 1819f76ee892STomi Valkeinen }; 1820f76ee892STomi Valkeinen u32 r; 1821f76ee892STomi Valkeinen int i; 1822f76ee892STomi Valkeinen 1823f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1); 1824f76ee892STomi Valkeinen 1825f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_used; ++i) { 1826f76ee892STomi Valkeinen unsigned offset = offsets[i]; 1827f76ee892STomi Valkeinen unsigned polarity, lane_number; 1828f76ee892STomi Valkeinen unsigned t; 1829f76ee892STomi Valkeinen 1830f76ee892STomi Valkeinen for (t = 0; t < dsi->num_lanes_supported; ++t) 1831f76ee892STomi Valkeinen if (dsi->lanes[t].function == functions[i]) 1832f76ee892STomi Valkeinen break; 1833f76ee892STomi Valkeinen 1834f76ee892STomi Valkeinen if (t == dsi->num_lanes_supported) 1835f76ee892STomi Valkeinen return -EINVAL; 1836f76ee892STomi Valkeinen 1837f76ee892STomi Valkeinen lane_number = t; 1838f76ee892STomi Valkeinen polarity = dsi->lanes[t].polarity; 1839f76ee892STomi Valkeinen 1840f76ee892STomi Valkeinen r = FLD_MOD(r, lane_number + 1, offset + 2, offset); 1841f76ee892STomi Valkeinen r = FLD_MOD(r, polarity, offset + 3, offset + 3); 1842f76ee892STomi Valkeinen } 1843f76ee892STomi Valkeinen 1844f76ee892STomi Valkeinen /* clear the unused lanes */ 1845f76ee892STomi Valkeinen for (; i < dsi->num_lanes_supported; ++i) { 1846f76ee892STomi Valkeinen unsigned offset = offsets[i]; 1847f76ee892STomi Valkeinen 1848f76ee892STomi Valkeinen r = FLD_MOD(r, 0, offset + 2, offset); 1849f76ee892STomi Valkeinen r = FLD_MOD(r, 0, offset + 3, offset + 3); 1850f76ee892STomi Valkeinen } 1851f76ee892STomi Valkeinen 1852f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r); 1853f76ee892STomi Valkeinen 1854f76ee892STomi Valkeinen return 0; 1855f76ee892STomi Valkeinen } 1856f76ee892STomi Valkeinen 1857f76ee892STomi Valkeinen static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns) 1858f76ee892STomi Valkeinen { 1859f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1860f76ee892STomi Valkeinen 1861f76ee892STomi Valkeinen /* convert time in ns to ddr ticks, rounding up */ 1862f76ee892STomi Valkeinen unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4; 1863f76ee892STomi Valkeinen return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; 1864f76ee892STomi Valkeinen } 1865f76ee892STomi Valkeinen 1866f76ee892STomi Valkeinen static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr) 1867f76ee892STomi Valkeinen { 1868f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1869f76ee892STomi Valkeinen 1870f76ee892STomi Valkeinen unsigned long ddr_clk = dsi->pll.cinfo.clkdco / 4; 1871f76ee892STomi Valkeinen return ddr * 1000 * 1000 / (ddr_clk / 1000); 1872f76ee892STomi Valkeinen } 1873f76ee892STomi Valkeinen 1874f76ee892STomi Valkeinen static void dsi_cio_timings(struct platform_device *dsidev) 1875f76ee892STomi Valkeinen { 1876f76ee892STomi Valkeinen u32 r; 1877f76ee892STomi Valkeinen u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; 1878f76ee892STomi Valkeinen u32 tlpx_half, tclk_trail, tclk_zero; 1879f76ee892STomi Valkeinen u32 tclk_prepare; 1880f76ee892STomi Valkeinen 1881f76ee892STomi Valkeinen /* calculate timings */ 1882f76ee892STomi Valkeinen 1883f76ee892STomi Valkeinen /* 1 * DDR_CLK = 2 * UI */ 1884f76ee892STomi Valkeinen 1885f76ee892STomi Valkeinen /* min 40ns + 4*UI max 85ns + 6*UI */ 1886f76ee892STomi Valkeinen ths_prepare = ns2ddr(dsidev, 70) + 2; 1887f76ee892STomi Valkeinen 1888f76ee892STomi Valkeinen /* min 145ns + 10*UI */ 1889f76ee892STomi Valkeinen ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2; 1890f76ee892STomi Valkeinen 1891f76ee892STomi Valkeinen /* min max(8*UI, 60ns+4*UI) */ 1892f76ee892STomi Valkeinen ths_trail = ns2ddr(dsidev, 60) + 5; 1893f76ee892STomi Valkeinen 1894f76ee892STomi Valkeinen /* min 100ns */ 1895f76ee892STomi Valkeinen ths_exit = ns2ddr(dsidev, 145); 1896f76ee892STomi Valkeinen 1897f76ee892STomi Valkeinen /* tlpx min 50n */ 1898f76ee892STomi Valkeinen tlpx_half = ns2ddr(dsidev, 25); 1899f76ee892STomi Valkeinen 1900f76ee892STomi Valkeinen /* min 60ns */ 1901f76ee892STomi Valkeinen tclk_trail = ns2ddr(dsidev, 60) + 2; 1902f76ee892STomi Valkeinen 1903f76ee892STomi Valkeinen /* min 38ns, max 95ns */ 1904f76ee892STomi Valkeinen tclk_prepare = ns2ddr(dsidev, 65); 1905f76ee892STomi Valkeinen 1906f76ee892STomi Valkeinen /* min tclk-prepare + tclk-zero = 300ns */ 1907f76ee892STomi Valkeinen tclk_zero = ns2ddr(dsidev, 260); 1908f76ee892STomi Valkeinen 1909f76ee892STomi Valkeinen DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", 1910f76ee892STomi Valkeinen ths_prepare, ddr2ns(dsidev, ths_prepare), 1911f76ee892STomi Valkeinen ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero)); 1912f76ee892STomi Valkeinen DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", 1913f76ee892STomi Valkeinen ths_trail, ddr2ns(dsidev, ths_trail), 1914f76ee892STomi Valkeinen ths_exit, ddr2ns(dsidev, ths_exit)); 1915f76ee892STomi Valkeinen 1916f76ee892STomi Valkeinen DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " 1917f76ee892STomi Valkeinen "tclk_zero %u (%uns)\n", 1918f76ee892STomi Valkeinen tlpx_half, ddr2ns(dsidev, tlpx_half), 1919f76ee892STomi Valkeinen tclk_trail, ddr2ns(dsidev, tclk_trail), 1920f76ee892STomi Valkeinen tclk_zero, ddr2ns(dsidev, tclk_zero)); 1921f76ee892STomi Valkeinen DSSDBG("tclk_prepare %u (%uns)\n", 1922f76ee892STomi Valkeinen tclk_prepare, ddr2ns(dsidev, tclk_prepare)); 1923f76ee892STomi Valkeinen 1924f76ee892STomi Valkeinen /* program timings */ 1925f76ee892STomi Valkeinen 1926f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); 1927f76ee892STomi Valkeinen r = FLD_MOD(r, ths_prepare, 31, 24); 1928f76ee892STomi Valkeinen r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); 1929f76ee892STomi Valkeinen r = FLD_MOD(r, ths_trail, 15, 8); 1930f76ee892STomi Valkeinen r = FLD_MOD(r, ths_exit, 7, 0); 1931f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r); 1932f76ee892STomi Valkeinen 1933f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); 1934f76ee892STomi Valkeinen r = FLD_MOD(r, tlpx_half, 20, 16); 1935f76ee892STomi Valkeinen r = FLD_MOD(r, tclk_trail, 15, 8); 1936f76ee892STomi Valkeinen r = FLD_MOD(r, tclk_zero, 7, 0); 1937f76ee892STomi Valkeinen 1938f76ee892STomi Valkeinen if (dss_has_feature(FEAT_DSI_PHY_DCC)) { 1939f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 21, 21); /* DCCEN = disable */ 1940f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 22, 22); /* CLKINP_DIVBY2EN = enable */ 1941f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 23, 23); /* CLKINP_SEL = enable */ 1942f76ee892STomi Valkeinen } 1943f76ee892STomi Valkeinen 1944f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r); 1945f76ee892STomi Valkeinen 1946f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); 1947f76ee892STomi Valkeinen r = FLD_MOD(r, tclk_prepare, 7, 0); 1948f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r); 1949f76ee892STomi Valkeinen } 1950f76ee892STomi Valkeinen 1951f76ee892STomi Valkeinen /* lane masks have lane 0 at lsb. mask_p for positive lines, n for negative */ 1952f76ee892STomi Valkeinen static void dsi_cio_enable_lane_override(struct platform_device *dsidev, 1953f76ee892STomi Valkeinen unsigned mask_p, unsigned mask_n) 1954f76ee892STomi Valkeinen { 1955f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 1956f76ee892STomi Valkeinen int i; 1957f76ee892STomi Valkeinen u32 l; 1958f76ee892STomi Valkeinen u8 lptxscp_start = dsi->num_lanes_supported == 3 ? 22 : 26; 1959f76ee892STomi Valkeinen 1960f76ee892STomi Valkeinen l = 0; 1961f76ee892STomi Valkeinen 1962f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_supported; ++i) { 1963f76ee892STomi Valkeinen unsigned p = dsi->lanes[i].polarity; 1964f76ee892STomi Valkeinen 1965f76ee892STomi Valkeinen if (mask_p & (1 << i)) 1966f76ee892STomi Valkeinen l |= 1 << (i * 2 + (p ? 0 : 1)); 1967f76ee892STomi Valkeinen 1968f76ee892STomi Valkeinen if (mask_n & (1 << i)) 1969f76ee892STomi Valkeinen l |= 1 << (i * 2 + (p ? 1 : 0)); 1970f76ee892STomi Valkeinen } 1971f76ee892STomi Valkeinen 1972f76ee892STomi Valkeinen /* 1973f76ee892STomi Valkeinen * Bits in REGLPTXSCPDAT4TO0DXDY: 1974f76ee892STomi Valkeinen * 17: DY0 18: DX0 1975f76ee892STomi Valkeinen * 19: DY1 20: DX1 1976f76ee892STomi Valkeinen * 21: DY2 22: DX2 1977f76ee892STomi Valkeinen * 23: DY3 24: DX3 1978f76ee892STomi Valkeinen * 25: DY4 26: DX4 1979f76ee892STomi Valkeinen */ 1980f76ee892STomi Valkeinen 1981f76ee892STomi Valkeinen /* Set the lane override configuration */ 1982f76ee892STomi Valkeinen 1983f76ee892STomi Valkeinen /* REGLPTXSCPDAT4TO0DXDY */ 1984f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17); 1985f76ee892STomi Valkeinen 1986f76ee892STomi Valkeinen /* Enable lane override */ 1987f76ee892STomi Valkeinen 1988f76ee892STomi Valkeinen /* ENLPTXSCPDAT */ 1989f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27); 1990f76ee892STomi Valkeinen } 1991f76ee892STomi Valkeinen 1992f76ee892STomi Valkeinen static void dsi_cio_disable_lane_override(struct platform_device *dsidev) 1993f76ee892STomi Valkeinen { 1994f76ee892STomi Valkeinen /* Disable lane override */ 1995f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */ 1996f76ee892STomi Valkeinen /* Reset the lane override configuration */ 1997f76ee892STomi Valkeinen /* REGLPTXSCPDAT4TO0DXDY */ 1998f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17); 1999f76ee892STomi Valkeinen } 2000f76ee892STomi Valkeinen 2001f76ee892STomi Valkeinen static int dsi_cio_wait_tx_clk_esc_reset(struct platform_device *dsidev) 2002f76ee892STomi Valkeinen { 2003f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2004f76ee892STomi Valkeinen int t, i; 2005f76ee892STomi Valkeinen bool in_use[DSI_MAX_NR_LANES]; 2006f76ee892STomi Valkeinen static const u8 offsets_old[] = { 28, 27, 26 }; 2007f76ee892STomi Valkeinen static const u8 offsets_new[] = { 24, 25, 26, 27, 28 }; 2008f76ee892STomi Valkeinen const u8 *offsets; 2009f76ee892STomi Valkeinen 2010f76ee892STomi Valkeinen if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) 2011f76ee892STomi Valkeinen offsets = offsets_old; 2012f76ee892STomi Valkeinen else 2013f76ee892STomi Valkeinen offsets = offsets_new; 2014f76ee892STomi Valkeinen 2015f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_supported; ++i) 2016f76ee892STomi Valkeinen in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED; 2017f76ee892STomi Valkeinen 2018f76ee892STomi Valkeinen t = 100000; 2019f76ee892STomi Valkeinen while (true) { 2020f76ee892STomi Valkeinen u32 l; 2021f76ee892STomi Valkeinen int ok; 2022f76ee892STomi Valkeinen 2023f76ee892STomi Valkeinen l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); 2024f76ee892STomi Valkeinen 2025f76ee892STomi Valkeinen ok = 0; 2026f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_supported; ++i) { 2027f76ee892STomi Valkeinen if (!in_use[i] || (l & (1 << offsets[i]))) 2028f76ee892STomi Valkeinen ok++; 2029f76ee892STomi Valkeinen } 2030f76ee892STomi Valkeinen 2031f76ee892STomi Valkeinen if (ok == dsi->num_lanes_supported) 2032f76ee892STomi Valkeinen break; 2033f76ee892STomi Valkeinen 2034f76ee892STomi Valkeinen if (--t == 0) { 2035f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_supported; ++i) { 2036f76ee892STomi Valkeinen if (!in_use[i] || (l & (1 << offsets[i]))) 2037f76ee892STomi Valkeinen continue; 2038f76ee892STomi Valkeinen 2039f76ee892STomi Valkeinen DSSERR("CIO TXCLKESC%d domain not coming " \ 2040f76ee892STomi Valkeinen "out of reset\n", i); 2041f76ee892STomi Valkeinen } 2042f76ee892STomi Valkeinen return -EIO; 2043f76ee892STomi Valkeinen } 2044f76ee892STomi Valkeinen } 2045f76ee892STomi Valkeinen 2046f76ee892STomi Valkeinen return 0; 2047f76ee892STomi Valkeinen } 2048f76ee892STomi Valkeinen 2049f76ee892STomi Valkeinen /* return bitmask of enabled lanes, lane0 being the lsb */ 2050f76ee892STomi Valkeinen static unsigned dsi_get_lane_mask(struct platform_device *dsidev) 2051f76ee892STomi Valkeinen { 2052f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2053f76ee892STomi Valkeinen unsigned mask = 0; 2054f76ee892STomi Valkeinen int i; 2055f76ee892STomi Valkeinen 2056f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_supported; ++i) { 2057f76ee892STomi Valkeinen if (dsi->lanes[i].function != DSI_LANE_UNUSED) 2058f76ee892STomi Valkeinen mask |= 1 << i; 2059f76ee892STomi Valkeinen } 2060f76ee892STomi Valkeinen 2061f76ee892STomi Valkeinen return mask; 2062f76ee892STomi Valkeinen } 2063f76ee892STomi Valkeinen 2064f76ee892STomi Valkeinen static int dsi_cio_init(struct platform_device *dsidev) 2065f76ee892STomi Valkeinen { 2066f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2067f76ee892STomi Valkeinen int r; 2068f76ee892STomi Valkeinen u32 l; 2069f76ee892STomi Valkeinen 2070f76ee892STomi Valkeinen DSSDBG("DSI CIO init starts"); 2071f76ee892STomi Valkeinen 2072f76ee892STomi Valkeinen r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dsidev)); 2073f76ee892STomi Valkeinen if (r) 2074f76ee892STomi Valkeinen return r; 2075f76ee892STomi Valkeinen 2076f76ee892STomi Valkeinen dsi_enable_scp_clk(dsidev); 2077f76ee892STomi Valkeinen 2078f76ee892STomi Valkeinen /* A dummy read using the SCP interface to any DSIPHY register is 2079f76ee892STomi Valkeinen * required after DSIPHY reset to complete the reset of the DSI complex 2080f76ee892STomi Valkeinen * I/O. */ 2081f76ee892STomi Valkeinen dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); 2082f76ee892STomi Valkeinen 2083f76ee892STomi Valkeinen if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) { 2084f76ee892STomi Valkeinen DSSERR("CIO SCP Clock domain not coming out of reset.\n"); 2085f76ee892STomi Valkeinen r = -EIO; 2086f76ee892STomi Valkeinen goto err_scp_clk_dom; 2087f76ee892STomi Valkeinen } 2088f76ee892STomi Valkeinen 2089f76ee892STomi Valkeinen r = dsi_set_lane_config(dsidev); 2090f76ee892STomi Valkeinen if (r) 2091f76ee892STomi Valkeinen goto err_scp_clk_dom; 2092f76ee892STomi Valkeinen 2093f76ee892STomi Valkeinen /* set TX STOP MODE timer to maximum for this operation */ 2094f76ee892STomi Valkeinen l = dsi_read_reg(dsidev, DSI_TIMING1); 2095f76ee892STomi Valkeinen l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ 2096f76ee892STomi Valkeinen l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */ 2097f76ee892STomi Valkeinen l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */ 2098f76ee892STomi Valkeinen l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */ 2099f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_TIMING1, l); 2100f76ee892STomi Valkeinen 2101f76ee892STomi Valkeinen if (dsi->ulps_enabled) { 2102f76ee892STomi Valkeinen unsigned mask_p; 2103f76ee892STomi Valkeinen int i; 2104f76ee892STomi Valkeinen 2105f76ee892STomi Valkeinen DSSDBG("manual ulps exit\n"); 2106f76ee892STomi Valkeinen 2107f76ee892STomi Valkeinen /* ULPS is exited by Mark-1 state for 1ms, followed by 2108f76ee892STomi Valkeinen * stop state. DSS HW cannot do this via the normal 2109f76ee892STomi Valkeinen * ULPS exit sequence, as after reset the DSS HW thinks 2110f76ee892STomi Valkeinen * that we are not in ULPS mode, and refuses to send the 2111f76ee892STomi Valkeinen * sequence. So we need to send the ULPS exit sequence 2112f76ee892STomi Valkeinen * manually by setting positive lines high and negative lines 2113f76ee892STomi Valkeinen * low for 1ms. 2114f76ee892STomi Valkeinen */ 2115f76ee892STomi Valkeinen 2116f76ee892STomi Valkeinen mask_p = 0; 2117f76ee892STomi Valkeinen 2118f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_supported; ++i) { 2119f76ee892STomi Valkeinen if (dsi->lanes[i].function == DSI_LANE_UNUSED) 2120f76ee892STomi Valkeinen continue; 2121f76ee892STomi Valkeinen mask_p |= 1 << i; 2122f76ee892STomi Valkeinen } 2123f76ee892STomi Valkeinen 2124f76ee892STomi Valkeinen dsi_cio_enable_lane_override(dsidev, mask_p, 0); 2125f76ee892STomi Valkeinen } 2126f76ee892STomi Valkeinen 2127f76ee892STomi Valkeinen r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON); 2128f76ee892STomi Valkeinen if (r) 2129f76ee892STomi Valkeinen goto err_cio_pwr; 2130f76ee892STomi Valkeinen 2131f76ee892STomi Valkeinen if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) { 2132f76ee892STomi Valkeinen DSSERR("CIO PWR clock domain not coming out of reset.\n"); 2133f76ee892STomi Valkeinen r = -ENODEV; 2134f76ee892STomi Valkeinen goto err_cio_pwr_dom; 2135f76ee892STomi Valkeinen } 2136f76ee892STomi Valkeinen 2137f76ee892STomi Valkeinen dsi_if_enable(dsidev, true); 2138f76ee892STomi Valkeinen dsi_if_enable(dsidev, false); 2139f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ 2140f76ee892STomi Valkeinen 2141f76ee892STomi Valkeinen r = dsi_cio_wait_tx_clk_esc_reset(dsidev); 2142f76ee892STomi Valkeinen if (r) 2143f76ee892STomi Valkeinen goto err_tx_clk_esc_rst; 2144f76ee892STomi Valkeinen 2145f76ee892STomi Valkeinen if (dsi->ulps_enabled) { 2146f76ee892STomi Valkeinen /* Keep Mark-1 state for 1ms (as per DSI spec) */ 2147f76ee892STomi Valkeinen ktime_t wait = ns_to_ktime(1000 * 1000); 2148f76ee892STomi Valkeinen set_current_state(TASK_UNINTERRUPTIBLE); 2149f76ee892STomi Valkeinen schedule_hrtimeout(&wait, HRTIMER_MODE_REL); 2150f76ee892STomi Valkeinen 2151f76ee892STomi Valkeinen /* Disable the override. The lanes should be set to Mark-11 2152f76ee892STomi Valkeinen * state by the HW */ 2153f76ee892STomi Valkeinen dsi_cio_disable_lane_override(dsidev); 2154f76ee892STomi Valkeinen } 2155f76ee892STomi Valkeinen 2156f76ee892STomi Valkeinen /* FORCE_TX_STOP_MODE_IO */ 2157f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15); 2158f76ee892STomi Valkeinen 2159f76ee892STomi Valkeinen dsi_cio_timings(dsidev); 2160f76ee892STomi Valkeinen 2161f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { 2162f76ee892STomi Valkeinen /* DDR_CLK_ALWAYS_ON */ 2163f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 2164f76ee892STomi Valkeinen dsi->vm_timings.ddr_clk_always_on, 13, 13); 2165f76ee892STomi Valkeinen } 2166f76ee892STomi Valkeinen 2167f76ee892STomi Valkeinen dsi->ulps_enabled = false; 2168f76ee892STomi Valkeinen 2169f76ee892STomi Valkeinen DSSDBG("CIO init done\n"); 2170f76ee892STomi Valkeinen 2171f76ee892STomi Valkeinen return 0; 2172f76ee892STomi Valkeinen 2173f76ee892STomi Valkeinen err_tx_clk_esc_rst: 2174f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */ 2175f76ee892STomi Valkeinen err_cio_pwr_dom: 2176f76ee892STomi Valkeinen dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); 2177f76ee892STomi Valkeinen err_cio_pwr: 2178f76ee892STomi Valkeinen if (dsi->ulps_enabled) 2179f76ee892STomi Valkeinen dsi_cio_disable_lane_override(dsidev); 2180f76ee892STomi Valkeinen err_scp_clk_dom: 2181f76ee892STomi Valkeinen dsi_disable_scp_clk(dsidev); 2182f76ee892STomi Valkeinen dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev)); 2183f76ee892STomi Valkeinen return r; 2184f76ee892STomi Valkeinen } 2185f76ee892STomi Valkeinen 2186f76ee892STomi Valkeinen static void dsi_cio_uninit(struct platform_device *dsidev) 2187f76ee892STomi Valkeinen { 2188f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2189f76ee892STomi Valkeinen 2190f76ee892STomi Valkeinen /* DDR_CLK_ALWAYS_ON */ 2191f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13); 2192f76ee892STomi Valkeinen 2193f76ee892STomi Valkeinen dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); 2194f76ee892STomi Valkeinen dsi_disable_scp_clk(dsidev); 2195f76ee892STomi Valkeinen dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev)); 2196f76ee892STomi Valkeinen } 2197f76ee892STomi Valkeinen 2198f76ee892STomi Valkeinen static void dsi_config_tx_fifo(struct platform_device *dsidev, 2199f76ee892STomi Valkeinen enum fifo_size size1, enum fifo_size size2, 2200f76ee892STomi Valkeinen enum fifo_size size3, enum fifo_size size4) 2201f76ee892STomi Valkeinen { 2202f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2203f76ee892STomi Valkeinen u32 r = 0; 2204f76ee892STomi Valkeinen int add = 0; 2205f76ee892STomi Valkeinen int i; 2206f76ee892STomi Valkeinen 2207f76ee892STomi Valkeinen dsi->vc[0].tx_fifo_size = size1; 2208f76ee892STomi Valkeinen dsi->vc[1].tx_fifo_size = size2; 2209f76ee892STomi Valkeinen dsi->vc[2].tx_fifo_size = size3; 2210f76ee892STomi Valkeinen dsi->vc[3].tx_fifo_size = size4; 2211f76ee892STomi Valkeinen 2212f76ee892STomi Valkeinen for (i = 0; i < 4; i++) { 2213f76ee892STomi Valkeinen u8 v; 2214f76ee892STomi Valkeinen int size = dsi->vc[i].tx_fifo_size; 2215f76ee892STomi Valkeinen 2216f76ee892STomi Valkeinen if (add + size > 4) { 2217f76ee892STomi Valkeinen DSSERR("Illegal FIFO configuration\n"); 2218f76ee892STomi Valkeinen BUG(); 2219f76ee892STomi Valkeinen return; 2220f76ee892STomi Valkeinen } 2221f76ee892STomi Valkeinen 2222f76ee892STomi Valkeinen v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); 2223f76ee892STomi Valkeinen r |= v << (8 * i); 2224f76ee892STomi Valkeinen /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */ 2225f76ee892STomi Valkeinen add += size; 2226f76ee892STomi Valkeinen } 2227f76ee892STomi Valkeinen 2228f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r); 2229f76ee892STomi Valkeinen } 2230f76ee892STomi Valkeinen 2231f76ee892STomi Valkeinen static void dsi_config_rx_fifo(struct platform_device *dsidev, 2232f76ee892STomi Valkeinen enum fifo_size size1, enum fifo_size size2, 2233f76ee892STomi Valkeinen enum fifo_size size3, enum fifo_size size4) 2234f76ee892STomi Valkeinen { 2235f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2236f76ee892STomi Valkeinen u32 r = 0; 2237f76ee892STomi Valkeinen int add = 0; 2238f76ee892STomi Valkeinen int i; 2239f76ee892STomi Valkeinen 2240f76ee892STomi Valkeinen dsi->vc[0].rx_fifo_size = size1; 2241f76ee892STomi Valkeinen dsi->vc[1].rx_fifo_size = size2; 2242f76ee892STomi Valkeinen dsi->vc[2].rx_fifo_size = size3; 2243f76ee892STomi Valkeinen dsi->vc[3].rx_fifo_size = size4; 2244f76ee892STomi Valkeinen 2245f76ee892STomi Valkeinen for (i = 0; i < 4; i++) { 2246f76ee892STomi Valkeinen u8 v; 2247f76ee892STomi Valkeinen int size = dsi->vc[i].rx_fifo_size; 2248f76ee892STomi Valkeinen 2249f76ee892STomi Valkeinen if (add + size > 4) { 2250f76ee892STomi Valkeinen DSSERR("Illegal FIFO configuration\n"); 2251f76ee892STomi Valkeinen BUG(); 2252f76ee892STomi Valkeinen return; 2253f76ee892STomi Valkeinen } 2254f76ee892STomi Valkeinen 2255f76ee892STomi Valkeinen v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); 2256f76ee892STomi Valkeinen r |= v << (8 * i); 2257f76ee892STomi Valkeinen /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */ 2258f76ee892STomi Valkeinen add += size; 2259f76ee892STomi Valkeinen } 2260f76ee892STomi Valkeinen 2261f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r); 2262f76ee892STomi Valkeinen } 2263f76ee892STomi Valkeinen 2264f76ee892STomi Valkeinen static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev) 2265f76ee892STomi Valkeinen { 2266f76ee892STomi Valkeinen u32 r; 2267f76ee892STomi Valkeinen 2268f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_TIMING1); 2269f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ 2270f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_TIMING1, r); 2271f76ee892STomi Valkeinen 2272f76ee892STomi Valkeinen if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) { 2273f76ee892STomi Valkeinen DSSERR("TX_STOP bit not going down\n"); 2274f76ee892STomi Valkeinen return -EIO; 2275f76ee892STomi Valkeinen } 2276f76ee892STomi Valkeinen 2277f76ee892STomi Valkeinen return 0; 2278f76ee892STomi Valkeinen } 2279f76ee892STomi Valkeinen 2280f76ee892STomi Valkeinen static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel) 2281f76ee892STomi Valkeinen { 2282f76ee892STomi Valkeinen return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0); 2283f76ee892STomi Valkeinen } 2284f76ee892STomi Valkeinen 2285f76ee892STomi Valkeinen static void dsi_packet_sent_handler_vp(void *data, u32 mask) 2286f76ee892STomi Valkeinen { 2287f76ee892STomi Valkeinen struct dsi_packet_sent_handler_data *vp_data = 2288f76ee892STomi Valkeinen (struct dsi_packet_sent_handler_data *) data; 2289f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev); 2290f76ee892STomi Valkeinen const int channel = dsi->update_channel; 2291f76ee892STomi Valkeinen u8 bit = dsi->te_enabled ? 30 : 31; 2292f76ee892STomi Valkeinen 2293f76ee892STomi Valkeinen if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0) 2294f76ee892STomi Valkeinen complete(vp_data->completion); 2295f76ee892STomi Valkeinen } 2296f76ee892STomi Valkeinen 2297f76ee892STomi Valkeinen static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel) 2298f76ee892STomi Valkeinen { 2299f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2300f76ee892STomi Valkeinen DECLARE_COMPLETION_ONSTACK(completion); 2301f76ee892STomi Valkeinen struct dsi_packet_sent_handler_data vp_data = { 2302f76ee892STomi Valkeinen .dsidev = dsidev, 2303f76ee892STomi Valkeinen .completion = &completion 2304f76ee892STomi Valkeinen }; 2305f76ee892STomi Valkeinen int r = 0; 2306f76ee892STomi Valkeinen u8 bit; 2307f76ee892STomi Valkeinen 2308f76ee892STomi Valkeinen bit = dsi->te_enabled ? 30 : 31; 2309f76ee892STomi Valkeinen 2310f76ee892STomi Valkeinen r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, 2311f76ee892STomi Valkeinen &vp_data, DSI_VC_IRQ_PACKET_SENT); 2312f76ee892STomi Valkeinen if (r) 2313f76ee892STomi Valkeinen goto err0; 2314f76ee892STomi Valkeinen 2315f76ee892STomi Valkeinen /* Wait for completion only if TE_EN/TE_START is still set */ 2316f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) { 2317f76ee892STomi Valkeinen if (wait_for_completion_timeout(&completion, 2318f76ee892STomi Valkeinen msecs_to_jiffies(10)) == 0) { 2319f76ee892STomi Valkeinen DSSERR("Failed to complete previous frame transfer\n"); 2320f76ee892STomi Valkeinen r = -EIO; 2321f76ee892STomi Valkeinen goto err1; 2322f76ee892STomi Valkeinen } 2323f76ee892STomi Valkeinen } 2324f76ee892STomi Valkeinen 2325f76ee892STomi Valkeinen dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, 2326f76ee892STomi Valkeinen &vp_data, DSI_VC_IRQ_PACKET_SENT); 2327f76ee892STomi Valkeinen 2328f76ee892STomi Valkeinen return 0; 2329f76ee892STomi Valkeinen err1: 2330f76ee892STomi Valkeinen dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, 2331f76ee892STomi Valkeinen &vp_data, DSI_VC_IRQ_PACKET_SENT); 2332f76ee892STomi Valkeinen err0: 2333f76ee892STomi Valkeinen return r; 2334f76ee892STomi Valkeinen } 2335f76ee892STomi Valkeinen 2336f76ee892STomi Valkeinen static void dsi_packet_sent_handler_l4(void *data, u32 mask) 2337f76ee892STomi Valkeinen { 2338f76ee892STomi Valkeinen struct dsi_packet_sent_handler_data *l4_data = 2339f76ee892STomi Valkeinen (struct dsi_packet_sent_handler_data *) data; 2340f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev); 2341f76ee892STomi Valkeinen const int channel = dsi->update_channel; 2342f76ee892STomi Valkeinen 2343f76ee892STomi Valkeinen if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0) 2344f76ee892STomi Valkeinen complete(l4_data->completion); 2345f76ee892STomi Valkeinen } 2346f76ee892STomi Valkeinen 2347f76ee892STomi Valkeinen static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel) 2348f76ee892STomi Valkeinen { 2349f76ee892STomi Valkeinen DECLARE_COMPLETION_ONSTACK(completion); 2350f76ee892STomi Valkeinen struct dsi_packet_sent_handler_data l4_data = { 2351f76ee892STomi Valkeinen .dsidev = dsidev, 2352f76ee892STomi Valkeinen .completion = &completion 2353f76ee892STomi Valkeinen }; 2354f76ee892STomi Valkeinen int r = 0; 2355f76ee892STomi Valkeinen 2356f76ee892STomi Valkeinen r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, 2357f76ee892STomi Valkeinen &l4_data, DSI_VC_IRQ_PACKET_SENT); 2358f76ee892STomi Valkeinen if (r) 2359f76ee892STomi Valkeinen goto err0; 2360f76ee892STomi Valkeinen 2361f76ee892STomi Valkeinen /* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */ 2362f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) { 2363f76ee892STomi Valkeinen if (wait_for_completion_timeout(&completion, 2364f76ee892STomi Valkeinen msecs_to_jiffies(10)) == 0) { 2365f76ee892STomi Valkeinen DSSERR("Failed to complete previous l4 transfer\n"); 2366f76ee892STomi Valkeinen r = -EIO; 2367f76ee892STomi Valkeinen goto err1; 2368f76ee892STomi Valkeinen } 2369f76ee892STomi Valkeinen } 2370f76ee892STomi Valkeinen 2371f76ee892STomi Valkeinen dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, 2372f76ee892STomi Valkeinen &l4_data, DSI_VC_IRQ_PACKET_SENT); 2373f76ee892STomi Valkeinen 2374f76ee892STomi Valkeinen return 0; 2375f76ee892STomi Valkeinen err1: 2376f76ee892STomi Valkeinen dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, 2377f76ee892STomi Valkeinen &l4_data, DSI_VC_IRQ_PACKET_SENT); 2378f76ee892STomi Valkeinen err0: 2379f76ee892STomi Valkeinen return r; 2380f76ee892STomi Valkeinen } 2381f76ee892STomi Valkeinen 2382f76ee892STomi Valkeinen static int dsi_sync_vc(struct platform_device *dsidev, int channel) 2383f76ee892STomi Valkeinen { 2384f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2385f76ee892STomi Valkeinen 2386f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 2387f76ee892STomi Valkeinen 2388f76ee892STomi Valkeinen WARN_ON(in_interrupt()); 2389f76ee892STomi Valkeinen 2390f76ee892STomi Valkeinen if (!dsi_vc_is_enabled(dsidev, channel)) 2391f76ee892STomi Valkeinen return 0; 2392f76ee892STomi Valkeinen 2393f76ee892STomi Valkeinen switch (dsi->vc[channel].source) { 2394f76ee892STomi Valkeinen case DSI_VC_SOURCE_VP: 2395f76ee892STomi Valkeinen return dsi_sync_vc_vp(dsidev, channel); 2396f76ee892STomi Valkeinen case DSI_VC_SOURCE_L4: 2397f76ee892STomi Valkeinen return dsi_sync_vc_l4(dsidev, channel); 2398f76ee892STomi Valkeinen default: 2399f76ee892STomi Valkeinen BUG(); 2400f76ee892STomi Valkeinen return -EINVAL; 2401f76ee892STomi Valkeinen } 2402f76ee892STomi Valkeinen } 2403f76ee892STomi Valkeinen 2404f76ee892STomi Valkeinen static int dsi_vc_enable(struct platform_device *dsidev, int channel, 2405f76ee892STomi Valkeinen bool enable) 2406f76ee892STomi Valkeinen { 2407f76ee892STomi Valkeinen DSSDBG("dsi_vc_enable channel %d, enable %d\n", 2408f76ee892STomi Valkeinen channel, enable); 2409f76ee892STomi Valkeinen 2410f76ee892STomi Valkeinen enable = enable ? 1 : 0; 2411f76ee892STomi Valkeinen 2412f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0); 2413f76ee892STomi Valkeinen 2414f76ee892STomi Valkeinen if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 2415f76ee892STomi Valkeinen 0, enable) != enable) { 2416f76ee892STomi Valkeinen DSSERR("Failed to set dsi_vc_enable to %d\n", enable); 2417f76ee892STomi Valkeinen return -EIO; 2418f76ee892STomi Valkeinen } 2419f76ee892STomi Valkeinen 2420f76ee892STomi Valkeinen return 0; 2421f76ee892STomi Valkeinen } 2422f76ee892STomi Valkeinen 2423f76ee892STomi Valkeinen static void dsi_vc_initial_config(struct platform_device *dsidev, int channel) 2424f76ee892STomi Valkeinen { 2425f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2426f76ee892STomi Valkeinen u32 r; 2427f76ee892STomi Valkeinen 2428f76ee892STomi Valkeinen DSSDBG("Initial config of virtual channel %d", channel); 2429f76ee892STomi Valkeinen 2430f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel)); 2431f76ee892STomi Valkeinen 2432f76ee892STomi Valkeinen if (FLD_GET(r, 15, 15)) /* VC_BUSY */ 2433f76ee892STomi Valkeinen DSSERR("VC(%d) busy when trying to configure it!\n", 2434f76ee892STomi Valkeinen channel); 2435f76ee892STomi Valkeinen 2436f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */ 2437f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ 2438f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */ 2439f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */ 2440f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ 2441f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ 2442f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ 2443f76ee892STomi Valkeinen if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH)) 2444f76ee892STomi Valkeinen r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */ 2445f76ee892STomi Valkeinen 2446f76ee892STomi Valkeinen r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ 2447f76ee892STomi Valkeinen r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ 2448f76ee892STomi Valkeinen 2449f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r); 2450f76ee892STomi Valkeinen 2451f76ee892STomi Valkeinen dsi->vc[channel].source = DSI_VC_SOURCE_L4; 2452f76ee892STomi Valkeinen } 2453f76ee892STomi Valkeinen 2454f76ee892STomi Valkeinen static int dsi_vc_config_source(struct platform_device *dsidev, int channel, 2455f76ee892STomi Valkeinen enum dsi_vc_source source) 2456f76ee892STomi Valkeinen { 2457f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2458f76ee892STomi Valkeinen 2459f76ee892STomi Valkeinen if (dsi->vc[channel].source == source) 2460f76ee892STomi Valkeinen return 0; 2461f76ee892STomi Valkeinen 2462f76ee892STomi Valkeinen DSSDBG("Source config of virtual channel %d", channel); 2463f76ee892STomi Valkeinen 2464f76ee892STomi Valkeinen dsi_sync_vc(dsidev, channel); 2465f76ee892STomi Valkeinen 2466f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, 0); 2467f76ee892STomi Valkeinen 2468f76ee892STomi Valkeinen /* VC_BUSY */ 2469f76ee892STomi Valkeinen if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) { 2470f76ee892STomi Valkeinen DSSERR("vc(%d) busy when trying to config for VP\n", channel); 2471f76ee892STomi Valkeinen return -EIO; 2472f76ee892STomi Valkeinen } 2473f76ee892STomi Valkeinen 2474f76ee892STomi Valkeinen /* SOURCE, 0 = L4, 1 = video port */ 2475f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1); 2476f76ee892STomi Valkeinen 2477f76ee892STomi Valkeinen /* DCS_CMD_ENABLE */ 2478f76ee892STomi Valkeinen if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { 2479f76ee892STomi Valkeinen bool enable = source == DSI_VC_SOURCE_VP; 2480f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30); 2481f76ee892STomi Valkeinen } 2482f76ee892STomi Valkeinen 2483f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, 1); 2484f76ee892STomi Valkeinen 2485f76ee892STomi Valkeinen dsi->vc[channel].source = source; 2486f76ee892STomi Valkeinen 2487f76ee892STomi Valkeinen return 0; 2488f76ee892STomi Valkeinen } 2489f76ee892STomi Valkeinen 2490f76ee892STomi Valkeinen static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel, 2491f76ee892STomi Valkeinen bool enable) 2492f76ee892STomi Valkeinen { 2493f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 2494f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2495f76ee892STomi Valkeinen 2496f76ee892STomi Valkeinen DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); 2497f76ee892STomi Valkeinen 2498f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 2499f76ee892STomi Valkeinen 2500f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, 0); 2501f76ee892STomi Valkeinen dsi_if_enable(dsidev, 0); 2502f76ee892STomi Valkeinen 2503f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9); 2504f76ee892STomi Valkeinen 2505f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, 1); 2506f76ee892STomi Valkeinen dsi_if_enable(dsidev, 1); 2507f76ee892STomi Valkeinen 2508f76ee892STomi Valkeinen dsi_force_tx_stop_mode_io(dsidev); 2509f76ee892STomi Valkeinen 2510f76ee892STomi Valkeinen /* start the DDR clock by sending a NULL packet */ 2511f76ee892STomi Valkeinen if (dsi->vm_timings.ddr_clk_always_on && enable) 2512f76ee892STomi Valkeinen dsi_vc_send_null(dssdev, channel); 2513f76ee892STomi Valkeinen } 2514f76ee892STomi Valkeinen 2515f76ee892STomi Valkeinen static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel) 2516f76ee892STomi Valkeinen { 2517f76ee892STomi Valkeinen while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { 2518f76ee892STomi Valkeinen u32 val; 2519f76ee892STomi Valkeinen val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); 2520f76ee892STomi Valkeinen DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", 2521f76ee892STomi Valkeinen (val >> 0) & 0xff, 2522f76ee892STomi Valkeinen (val >> 8) & 0xff, 2523f76ee892STomi Valkeinen (val >> 16) & 0xff, 2524f76ee892STomi Valkeinen (val >> 24) & 0xff); 2525f76ee892STomi Valkeinen } 2526f76ee892STomi Valkeinen } 2527f76ee892STomi Valkeinen 2528f76ee892STomi Valkeinen static void dsi_show_rx_ack_with_err(u16 err) 2529f76ee892STomi Valkeinen { 2530f76ee892STomi Valkeinen DSSERR("\tACK with ERROR (%#x):\n", err); 2531f76ee892STomi Valkeinen if (err & (1 << 0)) 2532f76ee892STomi Valkeinen DSSERR("\t\tSoT Error\n"); 2533f76ee892STomi Valkeinen if (err & (1 << 1)) 2534f76ee892STomi Valkeinen DSSERR("\t\tSoT Sync Error\n"); 2535f76ee892STomi Valkeinen if (err & (1 << 2)) 2536f76ee892STomi Valkeinen DSSERR("\t\tEoT Sync Error\n"); 2537f76ee892STomi Valkeinen if (err & (1 << 3)) 2538f76ee892STomi Valkeinen DSSERR("\t\tEscape Mode Entry Command Error\n"); 2539f76ee892STomi Valkeinen if (err & (1 << 4)) 2540f76ee892STomi Valkeinen DSSERR("\t\tLP Transmit Sync Error\n"); 2541f76ee892STomi Valkeinen if (err & (1 << 5)) 2542f76ee892STomi Valkeinen DSSERR("\t\tHS Receive Timeout Error\n"); 2543f76ee892STomi Valkeinen if (err & (1 << 6)) 2544f76ee892STomi Valkeinen DSSERR("\t\tFalse Control Error\n"); 2545f76ee892STomi Valkeinen if (err & (1 << 7)) 2546f76ee892STomi Valkeinen DSSERR("\t\t(reserved7)\n"); 2547f76ee892STomi Valkeinen if (err & (1 << 8)) 2548f76ee892STomi Valkeinen DSSERR("\t\tECC Error, single-bit (corrected)\n"); 2549f76ee892STomi Valkeinen if (err & (1 << 9)) 2550f76ee892STomi Valkeinen DSSERR("\t\tECC Error, multi-bit (not corrected)\n"); 2551f76ee892STomi Valkeinen if (err & (1 << 10)) 2552f76ee892STomi Valkeinen DSSERR("\t\tChecksum Error\n"); 2553f76ee892STomi Valkeinen if (err & (1 << 11)) 2554f76ee892STomi Valkeinen DSSERR("\t\tData type not recognized\n"); 2555f76ee892STomi Valkeinen if (err & (1 << 12)) 2556f76ee892STomi Valkeinen DSSERR("\t\tInvalid VC ID\n"); 2557f76ee892STomi Valkeinen if (err & (1 << 13)) 2558f76ee892STomi Valkeinen DSSERR("\t\tInvalid Transmission Length\n"); 2559f76ee892STomi Valkeinen if (err & (1 << 14)) 2560f76ee892STomi Valkeinen DSSERR("\t\t(reserved14)\n"); 2561f76ee892STomi Valkeinen if (err & (1 << 15)) 2562f76ee892STomi Valkeinen DSSERR("\t\tDSI Protocol Violation\n"); 2563f76ee892STomi Valkeinen } 2564f76ee892STomi Valkeinen 2565f76ee892STomi Valkeinen static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev, 2566f76ee892STomi Valkeinen int channel) 2567f76ee892STomi Valkeinen { 2568f76ee892STomi Valkeinen /* RX_FIFO_NOT_EMPTY */ 2569f76ee892STomi Valkeinen while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { 2570f76ee892STomi Valkeinen u32 val; 2571f76ee892STomi Valkeinen u8 dt; 2572f76ee892STomi Valkeinen val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); 2573f76ee892STomi Valkeinen DSSERR("\trawval %#08x\n", val); 2574f76ee892STomi Valkeinen dt = FLD_GET(val, 5, 0); 2575f76ee892STomi Valkeinen if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) { 2576f76ee892STomi Valkeinen u16 err = FLD_GET(val, 23, 8); 2577f76ee892STomi Valkeinen dsi_show_rx_ack_with_err(err); 2578f76ee892STomi Valkeinen } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) { 2579f76ee892STomi Valkeinen DSSERR("\tDCS short response, 1 byte: %#x\n", 2580f76ee892STomi Valkeinen FLD_GET(val, 23, 8)); 2581f76ee892STomi Valkeinen } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) { 2582f76ee892STomi Valkeinen DSSERR("\tDCS short response, 2 byte: %#x\n", 2583f76ee892STomi Valkeinen FLD_GET(val, 23, 8)); 2584f76ee892STomi Valkeinen } else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) { 2585f76ee892STomi Valkeinen DSSERR("\tDCS long response, len %d\n", 2586f76ee892STomi Valkeinen FLD_GET(val, 23, 8)); 2587f76ee892STomi Valkeinen dsi_vc_flush_long_data(dsidev, channel); 2588f76ee892STomi Valkeinen } else { 2589f76ee892STomi Valkeinen DSSERR("\tunknown datatype 0x%02x\n", dt); 2590f76ee892STomi Valkeinen } 2591f76ee892STomi Valkeinen } 2592f76ee892STomi Valkeinen return 0; 2593f76ee892STomi Valkeinen } 2594f76ee892STomi Valkeinen 2595f76ee892STomi Valkeinen static int dsi_vc_send_bta(struct platform_device *dsidev, int channel) 2596f76ee892STomi Valkeinen { 2597f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2598f76ee892STomi Valkeinen 2599f76ee892STomi Valkeinen if (dsi->debug_write || dsi->debug_read) 2600f76ee892STomi Valkeinen DSSDBG("dsi_vc_send_bta %d\n", channel); 2601f76ee892STomi Valkeinen 2602f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 2603f76ee892STomi Valkeinen 2604f76ee892STomi Valkeinen /* RX_FIFO_NOT_EMPTY */ 2605f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { 2606f76ee892STomi Valkeinen DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); 2607f76ee892STomi Valkeinen dsi_vc_flush_receive_data(dsidev, channel); 2608f76ee892STomi Valkeinen } 2609f76ee892STomi Valkeinen 2610f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ 2611f76ee892STomi Valkeinen 2612f76ee892STomi Valkeinen /* flush posted write */ 2613f76ee892STomi Valkeinen dsi_read_reg(dsidev, DSI_VC_CTRL(channel)); 2614f76ee892STomi Valkeinen 2615f76ee892STomi Valkeinen return 0; 2616f76ee892STomi Valkeinen } 2617f76ee892STomi Valkeinen 2618f76ee892STomi Valkeinen static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel) 2619f76ee892STomi Valkeinen { 2620f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 2621f76ee892STomi Valkeinen DECLARE_COMPLETION_ONSTACK(completion); 2622f76ee892STomi Valkeinen int r = 0; 2623f76ee892STomi Valkeinen u32 err; 2624f76ee892STomi Valkeinen 2625f76ee892STomi Valkeinen r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler, 2626f76ee892STomi Valkeinen &completion, DSI_VC_IRQ_BTA); 2627f76ee892STomi Valkeinen if (r) 2628f76ee892STomi Valkeinen goto err0; 2629f76ee892STomi Valkeinen 2630f76ee892STomi Valkeinen r = dsi_register_isr(dsidev, dsi_completion_handler, &completion, 2631f76ee892STomi Valkeinen DSI_IRQ_ERROR_MASK); 2632f76ee892STomi Valkeinen if (r) 2633f76ee892STomi Valkeinen goto err1; 2634f76ee892STomi Valkeinen 2635f76ee892STomi Valkeinen r = dsi_vc_send_bta(dsidev, channel); 2636f76ee892STomi Valkeinen if (r) 2637f76ee892STomi Valkeinen goto err2; 2638f76ee892STomi Valkeinen 2639f76ee892STomi Valkeinen if (wait_for_completion_timeout(&completion, 2640f76ee892STomi Valkeinen msecs_to_jiffies(500)) == 0) { 2641f76ee892STomi Valkeinen DSSERR("Failed to receive BTA\n"); 2642f76ee892STomi Valkeinen r = -EIO; 2643f76ee892STomi Valkeinen goto err2; 2644f76ee892STomi Valkeinen } 2645f76ee892STomi Valkeinen 2646f76ee892STomi Valkeinen err = dsi_get_errors(dsidev); 2647f76ee892STomi Valkeinen if (err) { 2648f76ee892STomi Valkeinen DSSERR("Error while sending BTA: %x\n", err); 2649f76ee892STomi Valkeinen r = -EIO; 2650f76ee892STomi Valkeinen goto err2; 2651f76ee892STomi Valkeinen } 2652f76ee892STomi Valkeinen err2: 2653f76ee892STomi Valkeinen dsi_unregister_isr(dsidev, dsi_completion_handler, &completion, 2654f76ee892STomi Valkeinen DSI_IRQ_ERROR_MASK); 2655f76ee892STomi Valkeinen err1: 2656f76ee892STomi Valkeinen dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler, 2657f76ee892STomi Valkeinen &completion, DSI_VC_IRQ_BTA); 2658f76ee892STomi Valkeinen err0: 2659f76ee892STomi Valkeinen return r; 2660f76ee892STomi Valkeinen } 2661f76ee892STomi Valkeinen 2662f76ee892STomi Valkeinen static inline void dsi_vc_write_long_header(struct platform_device *dsidev, 2663f76ee892STomi Valkeinen int channel, u8 data_type, u16 len, u8 ecc) 2664f76ee892STomi Valkeinen { 2665f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2666f76ee892STomi Valkeinen u32 val; 2667f76ee892STomi Valkeinen u8 data_id; 2668f76ee892STomi Valkeinen 2669f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 2670f76ee892STomi Valkeinen 2671f76ee892STomi Valkeinen data_id = data_type | dsi->vc[channel].vc_id << 6; 2672f76ee892STomi Valkeinen 2673f76ee892STomi Valkeinen val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | 2674f76ee892STomi Valkeinen FLD_VAL(ecc, 31, 24); 2675f76ee892STomi Valkeinen 2676f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val); 2677f76ee892STomi Valkeinen } 2678f76ee892STomi Valkeinen 2679f76ee892STomi Valkeinen static inline void dsi_vc_write_long_payload(struct platform_device *dsidev, 2680f76ee892STomi Valkeinen int channel, u8 b1, u8 b2, u8 b3, u8 b4) 2681f76ee892STomi Valkeinen { 2682f76ee892STomi Valkeinen u32 val; 2683f76ee892STomi Valkeinen 2684f76ee892STomi Valkeinen val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0; 2685f76ee892STomi Valkeinen 2686f76ee892STomi Valkeinen /* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", 2687f76ee892STomi Valkeinen b1, b2, b3, b4, val); */ 2688f76ee892STomi Valkeinen 2689f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val); 2690f76ee892STomi Valkeinen } 2691f76ee892STomi Valkeinen 2692f76ee892STomi Valkeinen static int dsi_vc_send_long(struct platform_device *dsidev, int channel, 2693f76ee892STomi Valkeinen u8 data_type, u8 *data, u16 len, u8 ecc) 2694f76ee892STomi Valkeinen { 2695f76ee892STomi Valkeinen /*u32 val; */ 2696f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2697f76ee892STomi Valkeinen int i; 2698f76ee892STomi Valkeinen u8 *p; 2699f76ee892STomi Valkeinen int r = 0; 2700f76ee892STomi Valkeinen u8 b1, b2, b3, b4; 2701f76ee892STomi Valkeinen 2702f76ee892STomi Valkeinen if (dsi->debug_write) 2703f76ee892STomi Valkeinen DSSDBG("dsi_vc_send_long, %d bytes\n", len); 2704f76ee892STomi Valkeinen 2705f76ee892STomi Valkeinen /* len + header */ 2706f76ee892STomi Valkeinen if (dsi->vc[channel].tx_fifo_size * 32 * 4 < len + 4) { 2707f76ee892STomi Valkeinen DSSERR("unable to send long packet: packet too long.\n"); 2708f76ee892STomi Valkeinen return -EINVAL; 2709f76ee892STomi Valkeinen } 2710f76ee892STomi Valkeinen 2711f76ee892STomi Valkeinen dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4); 2712f76ee892STomi Valkeinen 2713f76ee892STomi Valkeinen dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc); 2714f76ee892STomi Valkeinen 2715f76ee892STomi Valkeinen p = data; 2716f76ee892STomi Valkeinen for (i = 0; i < len >> 2; i++) { 2717f76ee892STomi Valkeinen if (dsi->debug_write) 2718f76ee892STomi Valkeinen DSSDBG("\tsending full packet %d\n", i); 2719f76ee892STomi Valkeinen 2720f76ee892STomi Valkeinen b1 = *p++; 2721f76ee892STomi Valkeinen b2 = *p++; 2722f76ee892STomi Valkeinen b3 = *p++; 2723f76ee892STomi Valkeinen b4 = *p++; 2724f76ee892STomi Valkeinen 2725f76ee892STomi Valkeinen dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4); 2726f76ee892STomi Valkeinen } 2727f76ee892STomi Valkeinen 2728f76ee892STomi Valkeinen i = len % 4; 2729f76ee892STomi Valkeinen if (i) { 2730f76ee892STomi Valkeinen b1 = 0; b2 = 0; b3 = 0; 2731f76ee892STomi Valkeinen 2732f76ee892STomi Valkeinen if (dsi->debug_write) 2733f76ee892STomi Valkeinen DSSDBG("\tsending remainder bytes %d\n", i); 2734f76ee892STomi Valkeinen 2735f76ee892STomi Valkeinen switch (i) { 2736f76ee892STomi Valkeinen case 3: 2737f76ee892STomi Valkeinen b1 = *p++; 2738f76ee892STomi Valkeinen b2 = *p++; 2739f76ee892STomi Valkeinen b3 = *p++; 2740f76ee892STomi Valkeinen break; 2741f76ee892STomi Valkeinen case 2: 2742f76ee892STomi Valkeinen b1 = *p++; 2743f76ee892STomi Valkeinen b2 = *p++; 2744f76ee892STomi Valkeinen break; 2745f76ee892STomi Valkeinen case 1: 2746f76ee892STomi Valkeinen b1 = *p++; 2747f76ee892STomi Valkeinen break; 2748f76ee892STomi Valkeinen } 2749f76ee892STomi Valkeinen 2750f76ee892STomi Valkeinen dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0); 2751f76ee892STomi Valkeinen } 2752f76ee892STomi Valkeinen 2753f76ee892STomi Valkeinen return r; 2754f76ee892STomi Valkeinen } 2755f76ee892STomi Valkeinen 2756f76ee892STomi Valkeinen static int dsi_vc_send_short(struct platform_device *dsidev, int channel, 2757f76ee892STomi Valkeinen u8 data_type, u16 data, u8 ecc) 2758f76ee892STomi Valkeinen { 2759f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2760f76ee892STomi Valkeinen u32 r; 2761f76ee892STomi Valkeinen u8 data_id; 2762f76ee892STomi Valkeinen 2763f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 2764f76ee892STomi Valkeinen 2765f76ee892STomi Valkeinen if (dsi->debug_write) 2766f76ee892STomi Valkeinen DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", 2767f76ee892STomi Valkeinen channel, 2768f76ee892STomi Valkeinen data_type, data & 0xff, (data >> 8) & 0xff); 2769f76ee892STomi Valkeinen 2770f76ee892STomi Valkeinen dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4); 2771f76ee892STomi Valkeinen 2772f76ee892STomi Valkeinen if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) { 2773f76ee892STomi Valkeinen DSSERR("ERROR FIFO FULL, aborting transfer\n"); 2774f76ee892STomi Valkeinen return -EINVAL; 2775f76ee892STomi Valkeinen } 2776f76ee892STomi Valkeinen 2777f76ee892STomi Valkeinen data_id = data_type | dsi->vc[channel].vc_id << 6; 2778f76ee892STomi Valkeinen 2779f76ee892STomi Valkeinen r = (data_id << 0) | (data << 8) | (ecc << 24); 2780f76ee892STomi Valkeinen 2781f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r); 2782f76ee892STomi Valkeinen 2783f76ee892STomi Valkeinen return 0; 2784f76ee892STomi Valkeinen } 2785f76ee892STomi Valkeinen 2786f76ee892STomi Valkeinen static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel) 2787f76ee892STomi Valkeinen { 2788f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 2789f76ee892STomi Valkeinen 2790f76ee892STomi Valkeinen return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL, 2791f76ee892STomi Valkeinen 0, 0); 2792f76ee892STomi Valkeinen } 2793f76ee892STomi Valkeinen 2794f76ee892STomi Valkeinen static int dsi_vc_write_nosync_common(struct platform_device *dsidev, 2795f76ee892STomi Valkeinen int channel, u8 *data, int len, enum dss_dsi_content_type type) 2796f76ee892STomi Valkeinen { 2797f76ee892STomi Valkeinen int r; 2798f76ee892STomi Valkeinen 2799f76ee892STomi Valkeinen if (len == 0) { 2800f76ee892STomi Valkeinen BUG_ON(type == DSS_DSI_CONTENT_DCS); 2801f76ee892STomi Valkeinen r = dsi_vc_send_short(dsidev, channel, 2802f76ee892STomi Valkeinen MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0); 2803f76ee892STomi Valkeinen } else if (len == 1) { 2804f76ee892STomi Valkeinen r = dsi_vc_send_short(dsidev, channel, 2805f76ee892STomi Valkeinen type == DSS_DSI_CONTENT_GENERIC ? 2806f76ee892STomi Valkeinen MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM : 2807f76ee892STomi Valkeinen MIPI_DSI_DCS_SHORT_WRITE, data[0], 0); 2808f76ee892STomi Valkeinen } else if (len == 2) { 2809f76ee892STomi Valkeinen r = dsi_vc_send_short(dsidev, channel, 2810f76ee892STomi Valkeinen type == DSS_DSI_CONTENT_GENERIC ? 2811f76ee892STomi Valkeinen MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM : 2812f76ee892STomi Valkeinen MIPI_DSI_DCS_SHORT_WRITE_PARAM, 2813f76ee892STomi Valkeinen data[0] | (data[1] << 8), 0); 2814f76ee892STomi Valkeinen } else { 2815f76ee892STomi Valkeinen r = dsi_vc_send_long(dsidev, channel, 2816f76ee892STomi Valkeinen type == DSS_DSI_CONTENT_GENERIC ? 2817f76ee892STomi Valkeinen MIPI_DSI_GENERIC_LONG_WRITE : 2818f76ee892STomi Valkeinen MIPI_DSI_DCS_LONG_WRITE, data, len, 0); 2819f76ee892STomi Valkeinen } 2820f76ee892STomi Valkeinen 2821f76ee892STomi Valkeinen return r; 2822f76ee892STomi Valkeinen } 2823f76ee892STomi Valkeinen 2824f76ee892STomi Valkeinen static int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel, 2825f76ee892STomi Valkeinen u8 *data, int len) 2826f76ee892STomi Valkeinen { 2827f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 2828f76ee892STomi Valkeinen 2829f76ee892STomi Valkeinen return dsi_vc_write_nosync_common(dsidev, channel, data, len, 2830f76ee892STomi Valkeinen DSS_DSI_CONTENT_DCS); 2831f76ee892STomi Valkeinen } 2832f76ee892STomi Valkeinen 2833f76ee892STomi Valkeinen static int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel, 2834f76ee892STomi Valkeinen u8 *data, int len) 2835f76ee892STomi Valkeinen { 2836f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 2837f76ee892STomi Valkeinen 2838f76ee892STomi Valkeinen return dsi_vc_write_nosync_common(dsidev, channel, data, len, 2839f76ee892STomi Valkeinen DSS_DSI_CONTENT_GENERIC); 2840f76ee892STomi Valkeinen } 2841f76ee892STomi Valkeinen 2842f76ee892STomi Valkeinen static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel, 2843f76ee892STomi Valkeinen u8 *data, int len, enum dss_dsi_content_type type) 2844f76ee892STomi Valkeinen { 2845f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 2846f76ee892STomi Valkeinen int r; 2847f76ee892STomi Valkeinen 2848f76ee892STomi Valkeinen r = dsi_vc_write_nosync_common(dsidev, channel, data, len, type); 2849f76ee892STomi Valkeinen if (r) 2850f76ee892STomi Valkeinen goto err; 2851f76ee892STomi Valkeinen 2852f76ee892STomi Valkeinen r = dsi_vc_send_bta_sync(dssdev, channel); 2853f76ee892STomi Valkeinen if (r) 2854f76ee892STomi Valkeinen goto err; 2855f76ee892STomi Valkeinen 2856f76ee892STomi Valkeinen /* RX_FIFO_NOT_EMPTY */ 2857f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { 2858f76ee892STomi Valkeinen DSSERR("rx fifo not empty after write, dumping data:\n"); 2859f76ee892STomi Valkeinen dsi_vc_flush_receive_data(dsidev, channel); 2860f76ee892STomi Valkeinen r = -EIO; 2861f76ee892STomi Valkeinen goto err; 2862f76ee892STomi Valkeinen } 2863f76ee892STomi Valkeinen 2864f76ee892STomi Valkeinen return 0; 2865f76ee892STomi Valkeinen err: 2866f76ee892STomi Valkeinen DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n", 2867f76ee892STomi Valkeinen channel, data[0], len); 2868f76ee892STomi Valkeinen return r; 2869f76ee892STomi Valkeinen } 2870f76ee892STomi Valkeinen 2871f76ee892STomi Valkeinen static int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data, 2872f76ee892STomi Valkeinen int len) 2873f76ee892STomi Valkeinen { 2874f76ee892STomi Valkeinen return dsi_vc_write_common(dssdev, channel, data, len, 2875f76ee892STomi Valkeinen DSS_DSI_CONTENT_DCS); 2876f76ee892STomi Valkeinen } 2877f76ee892STomi Valkeinen 2878f76ee892STomi Valkeinen static int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data, 2879f76ee892STomi Valkeinen int len) 2880f76ee892STomi Valkeinen { 2881f76ee892STomi Valkeinen return dsi_vc_write_common(dssdev, channel, data, len, 2882f76ee892STomi Valkeinen DSS_DSI_CONTENT_GENERIC); 2883f76ee892STomi Valkeinen } 2884f76ee892STomi Valkeinen 2885f76ee892STomi Valkeinen static int dsi_vc_dcs_send_read_request(struct platform_device *dsidev, 2886f76ee892STomi Valkeinen int channel, u8 dcs_cmd) 2887f76ee892STomi Valkeinen { 2888f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2889f76ee892STomi Valkeinen int r; 2890f76ee892STomi Valkeinen 2891f76ee892STomi Valkeinen if (dsi->debug_read) 2892f76ee892STomi Valkeinen DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n", 2893f76ee892STomi Valkeinen channel, dcs_cmd); 2894f76ee892STomi Valkeinen 2895f76ee892STomi Valkeinen r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0); 2896f76ee892STomi Valkeinen if (r) { 2897f76ee892STomi Valkeinen DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)" 2898f76ee892STomi Valkeinen " failed\n", channel, dcs_cmd); 2899f76ee892STomi Valkeinen return r; 2900f76ee892STomi Valkeinen } 2901f76ee892STomi Valkeinen 2902f76ee892STomi Valkeinen return 0; 2903f76ee892STomi Valkeinen } 2904f76ee892STomi Valkeinen 2905f76ee892STomi Valkeinen static int dsi_vc_generic_send_read_request(struct platform_device *dsidev, 2906f76ee892STomi Valkeinen int channel, u8 *reqdata, int reqlen) 2907f76ee892STomi Valkeinen { 2908f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2909f76ee892STomi Valkeinen u16 data; 2910f76ee892STomi Valkeinen u8 data_type; 2911f76ee892STomi Valkeinen int r; 2912f76ee892STomi Valkeinen 2913f76ee892STomi Valkeinen if (dsi->debug_read) 2914f76ee892STomi Valkeinen DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n", 2915f76ee892STomi Valkeinen channel, reqlen); 2916f76ee892STomi Valkeinen 2917f76ee892STomi Valkeinen if (reqlen == 0) { 2918f76ee892STomi Valkeinen data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM; 2919f76ee892STomi Valkeinen data = 0; 2920f76ee892STomi Valkeinen } else if (reqlen == 1) { 2921f76ee892STomi Valkeinen data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM; 2922f76ee892STomi Valkeinen data = reqdata[0]; 2923f76ee892STomi Valkeinen } else if (reqlen == 2) { 2924f76ee892STomi Valkeinen data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM; 2925f76ee892STomi Valkeinen data = reqdata[0] | (reqdata[1] << 8); 2926f76ee892STomi Valkeinen } else { 2927f76ee892STomi Valkeinen BUG(); 2928f76ee892STomi Valkeinen return -EINVAL; 2929f76ee892STomi Valkeinen } 2930f76ee892STomi Valkeinen 2931f76ee892STomi Valkeinen r = dsi_vc_send_short(dsidev, channel, data_type, data, 0); 2932f76ee892STomi Valkeinen if (r) { 2933f76ee892STomi Valkeinen DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)" 2934f76ee892STomi Valkeinen " failed\n", channel, reqlen); 2935f76ee892STomi Valkeinen return r; 2936f76ee892STomi Valkeinen } 2937f76ee892STomi Valkeinen 2938f76ee892STomi Valkeinen return 0; 2939f76ee892STomi Valkeinen } 2940f76ee892STomi Valkeinen 2941f76ee892STomi Valkeinen static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel, 2942f76ee892STomi Valkeinen u8 *buf, int buflen, enum dss_dsi_content_type type) 2943f76ee892STomi Valkeinen { 2944f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 2945f76ee892STomi Valkeinen u32 val; 2946f76ee892STomi Valkeinen u8 dt; 2947f76ee892STomi Valkeinen int r; 2948f76ee892STomi Valkeinen 2949f76ee892STomi Valkeinen /* RX_FIFO_NOT_EMPTY */ 2950f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) { 2951f76ee892STomi Valkeinen DSSERR("RX fifo empty when trying to read.\n"); 2952f76ee892STomi Valkeinen r = -EIO; 2953f76ee892STomi Valkeinen goto err; 2954f76ee892STomi Valkeinen } 2955f76ee892STomi Valkeinen 2956f76ee892STomi Valkeinen val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); 2957f76ee892STomi Valkeinen if (dsi->debug_read) 2958f76ee892STomi Valkeinen DSSDBG("\theader: %08x\n", val); 2959f76ee892STomi Valkeinen dt = FLD_GET(val, 5, 0); 2960f76ee892STomi Valkeinen if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) { 2961f76ee892STomi Valkeinen u16 err = FLD_GET(val, 23, 8); 2962f76ee892STomi Valkeinen dsi_show_rx_ack_with_err(err); 2963f76ee892STomi Valkeinen r = -EIO; 2964f76ee892STomi Valkeinen goto err; 2965f76ee892STomi Valkeinen 2966f76ee892STomi Valkeinen } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ? 2967f76ee892STomi Valkeinen MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE : 2968f76ee892STomi Valkeinen MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) { 2969f76ee892STomi Valkeinen u8 data = FLD_GET(val, 15, 8); 2970f76ee892STomi Valkeinen if (dsi->debug_read) 2971f76ee892STomi Valkeinen DSSDBG("\t%s short response, 1 byte: %02x\n", 2972f76ee892STomi Valkeinen type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : 2973f76ee892STomi Valkeinen "DCS", data); 2974f76ee892STomi Valkeinen 2975f76ee892STomi Valkeinen if (buflen < 1) { 2976f76ee892STomi Valkeinen r = -EIO; 2977f76ee892STomi Valkeinen goto err; 2978f76ee892STomi Valkeinen } 2979f76ee892STomi Valkeinen 2980f76ee892STomi Valkeinen buf[0] = data; 2981f76ee892STomi Valkeinen 2982f76ee892STomi Valkeinen return 1; 2983f76ee892STomi Valkeinen } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ? 2984f76ee892STomi Valkeinen MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE : 2985f76ee892STomi Valkeinen MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) { 2986f76ee892STomi Valkeinen u16 data = FLD_GET(val, 23, 8); 2987f76ee892STomi Valkeinen if (dsi->debug_read) 2988f76ee892STomi Valkeinen DSSDBG("\t%s short response, 2 byte: %04x\n", 2989f76ee892STomi Valkeinen type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : 2990f76ee892STomi Valkeinen "DCS", data); 2991f76ee892STomi Valkeinen 2992f76ee892STomi Valkeinen if (buflen < 2) { 2993f76ee892STomi Valkeinen r = -EIO; 2994f76ee892STomi Valkeinen goto err; 2995f76ee892STomi Valkeinen } 2996f76ee892STomi Valkeinen 2997f76ee892STomi Valkeinen buf[0] = data & 0xff; 2998f76ee892STomi Valkeinen buf[1] = (data >> 8) & 0xff; 2999f76ee892STomi Valkeinen 3000f76ee892STomi Valkeinen return 2; 3001f76ee892STomi Valkeinen } else if (dt == (type == DSS_DSI_CONTENT_GENERIC ? 3002f76ee892STomi Valkeinen MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE : 3003f76ee892STomi Valkeinen MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) { 3004f76ee892STomi Valkeinen int w; 3005f76ee892STomi Valkeinen int len = FLD_GET(val, 23, 8); 3006f76ee892STomi Valkeinen if (dsi->debug_read) 3007f76ee892STomi Valkeinen DSSDBG("\t%s long response, len %d\n", 3008f76ee892STomi Valkeinen type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : 3009f76ee892STomi Valkeinen "DCS", len); 3010f76ee892STomi Valkeinen 3011f76ee892STomi Valkeinen if (len > buflen) { 3012f76ee892STomi Valkeinen r = -EIO; 3013f76ee892STomi Valkeinen goto err; 3014f76ee892STomi Valkeinen } 3015f76ee892STomi Valkeinen 3016f76ee892STomi Valkeinen /* two byte checksum ends the packet, not included in len */ 3017f76ee892STomi Valkeinen for (w = 0; w < len + 2;) { 3018f76ee892STomi Valkeinen int b; 3019f76ee892STomi Valkeinen val = dsi_read_reg(dsidev, 3020f76ee892STomi Valkeinen DSI_VC_SHORT_PACKET_HEADER(channel)); 3021f76ee892STomi Valkeinen if (dsi->debug_read) 3022f76ee892STomi Valkeinen DSSDBG("\t\t%02x %02x %02x %02x\n", 3023f76ee892STomi Valkeinen (val >> 0) & 0xff, 3024f76ee892STomi Valkeinen (val >> 8) & 0xff, 3025f76ee892STomi Valkeinen (val >> 16) & 0xff, 3026f76ee892STomi Valkeinen (val >> 24) & 0xff); 3027f76ee892STomi Valkeinen 3028f76ee892STomi Valkeinen for (b = 0; b < 4; ++b) { 3029f76ee892STomi Valkeinen if (w < len) 3030f76ee892STomi Valkeinen buf[w] = (val >> (b * 8)) & 0xff; 3031f76ee892STomi Valkeinen /* we discard the 2 byte checksum */ 3032f76ee892STomi Valkeinen ++w; 3033f76ee892STomi Valkeinen } 3034f76ee892STomi Valkeinen } 3035f76ee892STomi Valkeinen 3036f76ee892STomi Valkeinen return len; 3037f76ee892STomi Valkeinen } else { 3038f76ee892STomi Valkeinen DSSERR("\tunknown datatype 0x%02x\n", dt); 3039f76ee892STomi Valkeinen r = -EIO; 3040f76ee892STomi Valkeinen goto err; 3041f76ee892STomi Valkeinen } 3042f76ee892STomi Valkeinen 3043f76ee892STomi Valkeinen err: 3044f76ee892STomi Valkeinen DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel, 3045f76ee892STomi Valkeinen type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS"); 3046f76ee892STomi Valkeinen 3047f76ee892STomi Valkeinen return r; 3048f76ee892STomi Valkeinen } 3049f76ee892STomi Valkeinen 3050f76ee892STomi Valkeinen static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, 3051f76ee892STomi Valkeinen u8 *buf, int buflen) 3052f76ee892STomi Valkeinen { 3053f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 3054f76ee892STomi Valkeinen int r; 3055f76ee892STomi Valkeinen 3056f76ee892STomi Valkeinen r = dsi_vc_dcs_send_read_request(dsidev, channel, dcs_cmd); 3057f76ee892STomi Valkeinen if (r) 3058f76ee892STomi Valkeinen goto err; 3059f76ee892STomi Valkeinen 3060f76ee892STomi Valkeinen r = dsi_vc_send_bta_sync(dssdev, channel); 3061f76ee892STomi Valkeinen if (r) 3062f76ee892STomi Valkeinen goto err; 3063f76ee892STomi Valkeinen 3064f76ee892STomi Valkeinen r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen, 3065f76ee892STomi Valkeinen DSS_DSI_CONTENT_DCS); 3066f76ee892STomi Valkeinen if (r < 0) 3067f76ee892STomi Valkeinen goto err; 3068f76ee892STomi Valkeinen 3069f76ee892STomi Valkeinen if (r != buflen) { 3070f76ee892STomi Valkeinen r = -EIO; 3071f76ee892STomi Valkeinen goto err; 3072f76ee892STomi Valkeinen } 3073f76ee892STomi Valkeinen 3074f76ee892STomi Valkeinen return 0; 3075f76ee892STomi Valkeinen err: 3076f76ee892STomi Valkeinen DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd); 3077f76ee892STomi Valkeinen return r; 3078f76ee892STomi Valkeinen } 3079f76ee892STomi Valkeinen 3080f76ee892STomi Valkeinen static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel, 3081f76ee892STomi Valkeinen u8 *reqdata, int reqlen, u8 *buf, int buflen) 3082f76ee892STomi Valkeinen { 3083f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 3084f76ee892STomi Valkeinen int r; 3085f76ee892STomi Valkeinen 3086f76ee892STomi Valkeinen r = dsi_vc_generic_send_read_request(dsidev, channel, reqdata, reqlen); 3087f76ee892STomi Valkeinen if (r) 3088f76ee892STomi Valkeinen return r; 3089f76ee892STomi Valkeinen 3090f76ee892STomi Valkeinen r = dsi_vc_send_bta_sync(dssdev, channel); 3091f76ee892STomi Valkeinen if (r) 3092f76ee892STomi Valkeinen return r; 3093f76ee892STomi Valkeinen 3094f76ee892STomi Valkeinen r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen, 3095f76ee892STomi Valkeinen DSS_DSI_CONTENT_GENERIC); 3096f76ee892STomi Valkeinen if (r < 0) 3097f76ee892STomi Valkeinen return r; 3098f76ee892STomi Valkeinen 3099f76ee892STomi Valkeinen if (r != buflen) { 3100f76ee892STomi Valkeinen r = -EIO; 3101f76ee892STomi Valkeinen return r; 3102f76ee892STomi Valkeinen } 3103f76ee892STomi Valkeinen 3104f76ee892STomi Valkeinen return 0; 3105f76ee892STomi Valkeinen } 3106f76ee892STomi Valkeinen 3107f76ee892STomi Valkeinen static int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel, 3108f76ee892STomi Valkeinen u16 len) 3109f76ee892STomi Valkeinen { 3110f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 3111f76ee892STomi Valkeinen 3112f76ee892STomi Valkeinen return dsi_vc_send_short(dsidev, channel, 3113f76ee892STomi Valkeinen MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0); 3114f76ee892STomi Valkeinen } 3115f76ee892STomi Valkeinen 3116f76ee892STomi Valkeinen static int dsi_enter_ulps(struct platform_device *dsidev) 3117f76ee892STomi Valkeinen { 3118f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3119f76ee892STomi Valkeinen DECLARE_COMPLETION_ONSTACK(completion); 3120f76ee892STomi Valkeinen int r, i; 3121f76ee892STomi Valkeinen unsigned mask; 3122f76ee892STomi Valkeinen 3123f76ee892STomi Valkeinen DSSDBG("Entering ULPS"); 3124f76ee892STomi Valkeinen 3125f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 3126f76ee892STomi Valkeinen 3127f76ee892STomi Valkeinen WARN_ON(dsi->ulps_enabled); 3128f76ee892STomi Valkeinen 3129f76ee892STomi Valkeinen if (dsi->ulps_enabled) 3130f76ee892STomi Valkeinen return 0; 3131f76ee892STomi Valkeinen 3132f76ee892STomi Valkeinen /* DDR_CLK_ALWAYS_ON */ 3133f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) { 3134f76ee892STomi Valkeinen dsi_if_enable(dsidev, 0); 3135f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13); 3136f76ee892STomi Valkeinen dsi_if_enable(dsidev, 1); 3137f76ee892STomi Valkeinen } 3138f76ee892STomi Valkeinen 3139f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 0); 3140f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 1); 3141f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 2); 3142f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 3); 3143f76ee892STomi Valkeinen 3144f76ee892STomi Valkeinen dsi_force_tx_stop_mode_io(dsidev); 3145f76ee892STomi Valkeinen 3146f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 0, false); 3147f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 1, false); 3148f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 2, false); 3149f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 3, false); 3150f76ee892STomi Valkeinen 3151f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */ 3152f76ee892STomi Valkeinen DSSERR("HS busy when enabling ULPS\n"); 3153f76ee892STomi Valkeinen return -EIO; 3154f76ee892STomi Valkeinen } 3155f76ee892STomi Valkeinen 3156f76ee892STomi Valkeinen if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */ 3157f76ee892STomi Valkeinen DSSERR("LP busy when enabling ULPS\n"); 3158f76ee892STomi Valkeinen return -EIO; 3159f76ee892STomi Valkeinen } 3160f76ee892STomi Valkeinen 3161f76ee892STomi Valkeinen r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion, 3162f76ee892STomi Valkeinen DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); 3163f76ee892STomi Valkeinen if (r) 3164f76ee892STomi Valkeinen return r; 3165f76ee892STomi Valkeinen 3166f76ee892STomi Valkeinen mask = 0; 3167f76ee892STomi Valkeinen 3168f76ee892STomi Valkeinen for (i = 0; i < dsi->num_lanes_supported; ++i) { 3169f76ee892STomi Valkeinen if (dsi->lanes[i].function == DSI_LANE_UNUSED) 3170f76ee892STomi Valkeinen continue; 3171f76ee892STomi Valkeinen mask |= 1 << i; 3172f76ee892STomi Valkeinen } 3173f76ee892STomi Valkeinen /* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */ 3174f76ee892STomi Valkeinen /* LANEx_ULPS_SIG2 */ 3175f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, mask, 9, 5); 3176f76ee892STomi Valkeinen 3177f76ee892STomi Valkeinen /* flush posted write and wait for SCP interface to finish the write */ 3178f76ee892STomi Valkeinen dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2); 3179f76ee892STomi Valkeinen 3180f76ee892STomi Valkeinen if (wait_for_completion_timeout(&completion, 3181f76ee892STomi Valkeinen msecs_to_jiffies(1000)) == 0) { 3182f76ee892STomi Valkeinen DSSERR("ULPS enable timeout\n"); 3183f76ee892STomi Valkeinen r = -EIO; 3184f76ee892STomi Valkeinen goto err; 3185f76ee892STomi Valkeinen } 3186f76ee892STomi Valkeinen 3187f76ee892STomi Valkeinen dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, 3188f76ee892STomi Valkeinen DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); 3189f76ee892STomi Valkeinen 3190f76ee892STomi Valkeinen /* Reset LANEx_ULPS_SIG2 */ 3191f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, 0, 9, 5); 3192f76ee892STomi Valkeinen 3193f76ee892STomi Valkeinen /* flush posted write and wait for SCP interface to finish the write */ 3194f76ee892STomi Valkeinen dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2); 3195f76ee892STomi Valkeinen 3196f76ee892STomi Valkeinen dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS); 3197f76ee892STomi Valkeinen 3198f76ee892STomi Valkeinen dsi_if_enable(dsidev, false); 3199f76ee892STomi Valkeinen 3200f76ee892STomi Valkeinen dsi->ulps_enabled = true; 3201f76ee892STomi Valkeinen 3202f76ee892STomi Valkeinen return 0; 3203f76ee892STomi Valkeinen 3204f76ee892STomi Valkeinen err: 3205f76ee892STomi Valkeinen dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, 3206f76ee892STomi Valkeinen DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); 3207f76ee892STomi Valkeinen return r; 3208f76ee892STomi Valkeinen } 3209f76ee892STomi Valkeinen 3210f76ee892STomi Valkeinen static void dsi_set_lp_rx_timeout(struct platform_device *dsidev, 3211f76ee892STomi Valkeinen unsigned ticks, bool x4, bool x16) 3212f76ee892STomi Valkeinen { 3213f76ee892STomi Valkeinen unsigned long fck; 3214f76ee892STomi Valkeinen unsigned long total_ticks; 3215f76ee892STomi Valkeinen u32 r; 3216f76ee892STomi Valkeinen 3217f76ee892STomi Valkeinen BUG_ON(ticks > 0x1fff); 3218f76ee892STomi Valkeinen 3219f76ee892STomi Valkeinen /* ticks in DSI_FCK */ 3220f76ee892STomi Valkeinen fck = dsi_fclk_rate(dsidev); 3221f76ee892STomi Valkeinen 3222f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_TIMING2); 3223f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ 3224f76ee892STomi Valkeinen r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */ 3225f76ee892STomi Valkeinen r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */ 3226f76ee892STomi Valkeinen r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ 3227f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_TIMING2, r); 3228f76ee892STomi Valkeinen 3229f76ee892STomi Valkeinen total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); 3230f76ee892STomi Valkeinen 3231f76ee892STomi Valkeinen DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n", 3232f76ee892STomi Valkeinen total_ticks, 3233f76ee892STomi Valkeinen ticks, x4 ? " x4" : "", x16 ? " x16" : "", 3234f76ee892STomi Valkeinen (total_ticks * 1000) / (fck / 1000 / 1000)); 3235f76ee892STomi Valkeinen } 3236f76ee892STomi Valkeinen 3237f76ee892STomi Valkeinen static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks, 3238f76ee892STomi Valkeinen bool x8, bool x16) 3239f76ee892STomi Valkeinen { 3240f76ee892STomi Valkeinen unsigned long fck; 3241f76ee892STomi Valkeinen unsigned long total_ticks; 3242f76ee892STomi Valkeinen u32 r; 3243f76ee892STomi Valkeinen 3244f76ee892STomi Valkeinen BUG_ON(ticks > 0x1fff); 3245f76ee892STomi Valkeinen 3246f76ee892STomi Valkeinen /* ticks in DSI_FCK */ 3247f76ee892STomi Valkeinen fck = dsi_fclk_rate(dsidev); 3248f76ee892STomi Valkeinen 3249f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_TIMING1); 3250f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ 3251f76ee892STomi Valkeinen r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */ 3252f76ee892STomi Valkeinen r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */ 3253f76ee892STomi Valkeinen r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ 3254f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_TIMING1, r); 3255f76ee892STomi Valkeinen 3256f76ee892STomi Valkeinen total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1); 3257f76ee892STomi Valkeinen 3258f76ee892STomi Valkeinen DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n", 3259f76ee892STomi Valkeinen total_ticks, 3260f76ee892STomi Valkeinen ticks, x8 ? " x8" : "", x16 ? " x16" : "", 3261f76ee892STomi Valkeinen (total_ticks * 1000) / (fck / 1000 / 1000)); 3262f76ee892STomi Valkeinen } 3263f76ee892STomi Valkeinen 3264f76ee892STomi Valkeinen static void dsi_set_stop_state_counter(struct platform_device *dsidev, 3265f76ee892STomi Valkeinen unsigned ticks, bool x4, bool x16) 3266f76ee892STomi Valkeinen { 3267f76ee892STomi Valkeinen unsigned long fck; 3268f76ee892STomi Valkeinen unsigned long total_ticks; 3269f76ee892STomi Valkeinen u32 r; 3270f76ee892STomi Valkeinen 3271f76ee892STomi Valkeinen BUG_ON(ticks > 0x1fff); 3272f76ee892STomi Valkeinen 3273f76ee892STomi Valkeinen /* ticks in DSI_FCK */ 3274f76ee892STomi Valkeinen fck = dsi_fclk_rate(dsidev); 3275f76ee892STomi Valkeinen 3276f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_TIMING1); 3277f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ 3278f76ee892STomi Valkeinen r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */ 3279f76ee892STomi Valkeinen r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */ 3280f76ee892STomi Valkeinen r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ 3281f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_TIMING1, r); 3282f76ee892STomi Valkeinen 3283f76ee892STomi Valkeinen total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); 3284f76ee892STomi Valkeinen 3285f76ee892STomi Valkeinen DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n", 3286f76ee892STomi Valkeinen total_ticks, 3287f76ee892STomi Valkeinen ticks, x4 ? " x4" : "", x16 ? " x16" : "", 3288f76ee892STomi Valkeinen (total_ticks * 1000) / (fck / 1000 / 1000)); 3289f76ee892STomi Valkeinen } 3290f76ee892STomi Valkeinen 3291f76ee892STomi Valkeinen static void dsi_set_hs_tx_timeout(struct platform_device *dsidev, 3292f76ee892STomi Valkeinen unsigned ticks, bool x4, bool x16) 3293f76ee892STomi Valkeinen { 3294f76ee892STomi Valkeinen unsigned long fck; 3295f76ee892STomi Valkeinen unsigned long total_ticks; 3296f76ee892STomi Valkeinen u32 r; 3297f76ee892STomi Valkeinen 3298f76ee892STomi Valkeinen BUG_ON(ticks > 0x1fff); 3299f76ee892STomi Valkeinen 3300f76ee892STomi Valkeinen /* ticks in TxByteClkHS */ 3301f76ee892STomi Valkeinen fck = dsi_get_txbyteclkhs(dsidev); 3302f76ee892STomi Valkeinen 3303f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_TIMING2); 3304f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ 3305f76ee892STomi Valkeinen r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */ 3306f76ee892STomi Valkeinen r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */ 3307f76ee892STomi Valkeinen r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ 3308f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_TIMING2, r); 3309f76ee892STomi Valkeinen 3310f76ee892STomi Valkeinen total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); 3311f76ee892STomi Valkeinen 3312f76ee892STomi Valkeinen DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n", 3313f76ee892STomi Valkeinen total_ticks, 3314f76ee892STomi Valkeinen ticks, x4 ? " x4" : "", x16 ? " x16" : "", 3315f76ee892STomi Valkeinen (total_ticks * 1000) / (fck / 1000 / 1000)); 3316f76ee892STomi Valkeinen } 3317f76ee892STomi Valkeinen 3318f76ee892STomi Valkeinen static void dsi_config_vp_num_line_buffers(struct platform_device *dsidev) 3319f76ee892STomi Valkeinen { 3320f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3321f76ee892STomi Valkeinen int num_line_buffers; 3322f76ee892STomi Valkeinen 3323f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { 3324f76ee892STomi Valkeinen int bpp = dsi_get_pixel_size(dsi->pix_fmt); 3325f76ee892STomi Valkeinen struct omap_video_timings *timings = &dsi->timings; 3326f76ee892STomi Valkeinen /* 3327f76ee892STomi Valkeinen * Don't use line buffers if width is greater than the video 3328f76ee892STomi Valkeinen * port's line buffer size 3329f76ee892STomi Valkeinen */ 3330f76ee892STomi Valkeinen if (dsi->line_buffer_size <= timings->x_res * bpp / 8) 3331f76ee892STomi Valkeinen num_line_buffers = 0; 3332f76ee892STomi Valkeinen else 3333f76ee892STomi Valkeinen num_line_buffers = 2; 3334f76ee892STomi Valkeinen } else { 3335f76ee892STomi Valkeinen /* Use maximum number of line buffers in command mode */ 3336f76ee892STomi Valkeinen num_line_buffers = 2; 3337f76ee892STomi Valkeinen } 3338f76ee892STomi Valkeinen 3339f76ee892STomi Valkeinen /* LINE_BUFFER */ 3340f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12); 3341f76ee892STomi Valkeinen } 3342f76ee892STomi Valkeinen 3343f76ee892STomi Valkeinen static void dsi_config_vp_sync_events(struct platform_device *dsidev) 3344f76ee892STomi Valkeinen { 3345f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3346f76ee892STomi Valkeinen bool sync_end; 3347f76ee892STomi Valkeinen u32 r; 3348f76ee892STomi Valkeinen 3349f76ee892STomi Valkeinen if (dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE) 3350f76ee892STomi Valkeinen sync_end = true; 3351f76ee892STomi Valkeinen else 3352f76ee892STomi Valkeinen sync_end = false; 3353f76ee892STomi Valkeinen 3354f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_CTRL); 3355f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */ 3356f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */ 3357f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */ 3358f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */ 3359f76ee892STomi Valkeinen r = FLD_MOD(r, sync_end, 16, 16); /* VP_VSYNC_END */ 3360f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */ 3361f76ee892STomi Valkeinen r = FLD_MOD(r, sync_end, 18, 18); /* VP_HSYNC_END */ 3362f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_CTRL, r); 3363f76ee892STomi Valkeinen } 3364f76ee892STomi Valkeinen 3365f76ee892STomi Valkeinen static void dsi_config_blanking_modes(struct platform_device *dsidev) 3366f76ee892STomi Valkeinen { 3367f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3368f76ee892STomi Valkeinen int blanking_mode = dsi->vm_timings.blanking_mode; 3369f76ee892STomi Valkeinen int hfp_blanking_mode = dsi->vm_timings.hfp_blanking_mode; 3370f76ee892STomi Valkeinen int hbp_blanking_mode = dsi->vm_timings.hbp_blanking_mode; 3371f76ee892STomi Valkeinen int hsa_blanking_mode = dsi->vm_timings.hsa_blanking_mode; 3372f76ee892STomi Valkeinen u32 r; 3373f76ee892STomi Valkeinen 3374f76ee892STomi Valkeinen /* 3375f76ee892STomi Valkeinen * 0 = TX FIFO packets sent or LPS in corresponding blanking periods 3376f76ee892STomi Valkeinen * 1 = Long blanking packets are sent in corresponding blanking periods 3377f76ee892STomi Valkeinen */ 3378f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_CTRL); 3379f76ee892STomi Valkeinen r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */ 3380f76ee892STomi Valkeinen r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */ 3381f76ee892STomi Valkeinen r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */ 3382f76ee892STomi Valkeinen r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */ 3383f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_CTRL, r); 3384f76ee892STomi Valkeinen } 3385f76ee892STomi Valkeinen 3386f76ee892STomi Valkeinen /* 3387f76ee892STomi Valkeinen * According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3 3388f76ee892STomi Valkeinen * results in maximum transition time for data and clock lanes to enter and 3389f76ee892STomi Valkeinen * exit HS mode. Hence, this is the scenario where the least amount of command 3390f76ee892STomi Valkeinen * mode data can be interleaved. We program the minimum amount of TXBYTECLKHS 3391f76ee892STomi Valkeinen * clock cycles that can be used to interleave command mode data in HS so that 3392f76ee892STomi Valkeinen * all scenarios are satisfied. 3393f76ee892STomi Valkeinen */ 3394f76ee892STomi Valkeinen static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs, 3395f76ee892STomi Valkeinen int exit_hs, int exiths_clk, int ddr_pre, int ddr_post) 3396f76ee892STomi Valkeinen { 3397f76ee892STomi Valkeinen int transition; 3398f76ee892STomi Valkeinen 3399f76ee892STomi Valkeinen /* 3400f76ee892STomi Valkeinen * If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition 3401f76ee892STomi Valkeinen * time of data lanes only, if it isn't set, we need to consider HS 3402f76ee892STomi Valkeinen * transition time of both data and clock lanes. HS transition time 3403f76ee892STomi Valkeinen * of Scenario 3 is considered. 3404f76ee892STomi Valkeinen */ 3405f76ee892STomi Valkeinen if (ddr_alwon) { 3406f76ee892STomi Valkeinen transition = enter_hs + exit_hs + max(enter_hs, 2) + 1; 3407f76ee892STomi Valkeinen } else { 3408f76ee892STomi Valkeinen int trans1, trans2; 3409f76ee892STomi Valkeinen trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1; 3410f76ee892STomi Valkeinen trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre + 3411f76ee892STomi Valkeinen enter_hs + 1; 3412f76ee892STomi Valkeinen transition = max(trans1, trans2); 3413f76ee892STomi Valkeinen } 3414f76ee892STomi Valkeinen 3415f76ee892STomi Valkeinen return blank > transition ? blank - transition : 0; 3416f76ee892STomi Valkeinen } 3417f76ee892STomi Valkeinen 3418f76ee892STomi Valkeinen /* 3419f76ee892STomi Valkeinen * According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1 3420f76ee892STomi Valkeinen * results in maximum transition time for data lanes to enter and exit LP mode. 3421f76ee892STomi Valkeinen * Hence, this is the scenario where the least amount of command mode data can 3422f76ee892STomi Valkeinen * be interleaved. We program the minimum amount of bytes that can be 3423f76ee892STomi Valkeinen * interleaved in LP so that all scenarios are satisfied. 3424f76ee892STomi Valkeinen */ 3425f76ee892STomi Valkeinen static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs, 3426f76ee892STomi Valkeinen int lp_clk_div, int tdsi_fclk) 3427f76ee892STomi Valkeinen { 3428f76ee892STomi Valkeinen int trans_lp; /* time required for a LP transition, in TXBYTECLKHS */ 3429f76ee892STomi Valkeinen int tlp_avail; /* time left for interleaving commands, in CLKIN4DDR */ 3430f76ee892STomi Valkeinen int ttxclkesc; /* period of LP transmit escape clock, in CLKIN4DDR */ 3431f76ee892STomi Valkeinen int thsbyte_clk = 16; /* Period of TXBYTECLKHS clock, in CLKIN4DDR */ 3432f76ee892STomi Valkeinen int lp_inter; /* cmd mode data that can be interleaved, in bytes */ 3433f76ee892STomi Valkeinen 3434f76ee892STomi Valkeinen /* maximum LP transition time according to Scenario 1 */ 3435f76ee892STomi Valkeinen trans_lp = exit_hs + max(enter_hs, 2) + 1; 3436f76ee892STomi Valkeinen 3437f76ee892STomi Valkeinen /* CLKIN4DDR = 16 * TXBYTECLKHS */ 3438f76ee892STomi Valkeinen tlp_avail = thsbyte_clk * (blank - trans_lp); 3439f76ee892STomi Valkeinen 3440f76ee892STomi Valkeinen ttxclkesc = tdsi_fclk * lp_clk_div; 3441f76ee892STomi Valkeinen 3442f76ee892STomi Valkeinen lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc - 3443f76ee892STomi Valkeinen 26) / 16; 3444f76ee892STomi Valkeinen 3445f76ee892STomi Valkeinen return max(lp_inter, 0); 3446f76ee892STomi Valkeinen } 3447f76ee892STomi Valkeinen 3448f76ee892STomi Valkeinen static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev) 3449f76ee892STomi Valkeinen { 3450f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3451f76ee892STomi Valkeinen int blanking_mode; 3452f76ee892STomi Valkeinen int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode; 3453f76ee892STomi Valkeinen int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div; 3454f76ee892STomi Valkeinen int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat; 3455f76ee892STomi Valkeinen int tclk_trail, ths_exit, exiths_clk; 3456f76ee892STomi Valkeinen bool ddr_alwon; 3457f76ee892STomi Valkeinen struct omap_video_timings *timings = &dsi->timings; 3458f76ee892STomi Valkeinen int bpp = dsi_get_pixel_size(dsi->pix_fmt); 3459f76ee892STomi Valkeinen int ndl = dsi->num_lanes_used - 1; 3460f76ee892STomi Valkeinen int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.mX[HSDIV_DSI] + 1; 3461f76ee892STomi Valkeinen int hsa_interleave_hs = 0, hsa_interleave_lp = 0; 3462f76ee892STomi Valkeinen int hfp_interleave_hs = 0, hfp_interleave_lp = 0; 3463f76ee892STomi Valkeinen int hbp_interleave_hs = 0, hbp_interleave_lp = 0; 3464f76ee892STomi Valkeinen int bl_interleave_hs = 0, bl_interleave_lp = 0; 3465f76ee892STomi Valkeinen u32 r; 3466f76ee892STomi Valkeinen 3467f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_CTRL); 3468f76ee892STomi Valkeinen blanking_mode = FLD_GET(r, 20, 20); 3469f76ee892STomi Valkeinen hfp_blanking_mode = FLD_GET(r, 21, 21); 3470f76ee892STomi Valkeinen hbp_blanking_mode = FLD_GET(r, 22, 22); 3471f76ee892STomi Valkeinen hsa_blanking_mode = FLD_GET(r, 23, 23); 3472f76ee892STomi Valkeinen 3473f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING1); 3474f76ee892STomi Valkeinen hbp = FLD_GET(r, 11, 0); 3475f76ee892STomi Valkeinen hfp = FLD_GET(r, 23, 12); 3476f76ee892STomi Valkeinen hsa = FLD_GET(r, 31, 24); 3477f76ee892STomi Valkeinen 3478f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_CLK_TIMING); 3479f76ee892STomi Valkeinen ddr_clk_post = FLD_GET(r, 7, 0); 3480f76ee892STomi Valkeinen ddr_clk_pre = FLD_GET(r, 15, 8); 3481f76ee892STomi Valkeinen 3482f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING7); 3483f76ee892STomi Valkeinen exit_hs_mode_lat = FLD_GET(r, 15, 0); 3484f76ee892STomi Valkeinen enter_hs_mode_lat = FLD_GET(r, 31, 16); 3485f76ee892STomi Valkeinen 3486f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_CLK_CTRL); 3487f76ee892STomi Valkeinen lp_clk_div = FLD_GET(r, 12, 0); 3488f76ee892STomi Valkeinen ddr_alwon = FLD_GET(r, 13, 13); 3489f76ee892STomi Valkeinen 3490f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); 3491f76ee892STomi Valkeinen ths_exit = FLD_GET(r, 7, 0); 3492f76ee892STomi Valkeinen 3493f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); 3494f76ee892STomi Valkeinen tclk_trail = FLD_GET(r, 15, 8); 3495f76ee892STomi Valkeinen 3496f76ee892STomi Valkeinen exiths_clk = ths_exit + tclk_trail; 3497f76ee892STomi Valkeinen 3498f76ee892STomi Valkeinen width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8); 3499f76ee892STomi Valkeinen bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl); 3500f76ee892STomi Valkeinen 3501f76ee892STomi Valkeinen if (!hsa_blanking_mode) { 3502f76ee892STomi Valkeinen hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon, 3503f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3504f76ee892STomi Valkeinen exiths_clk, ddr_clk_pre, ddr_clk_post); 3505f76ee892STomi Valkeinen hsa_interleave_lp = dsi_compute_interleave_lp(hsa, 3506f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3507f76ee892STomi Valkeinen lp_clk_div, dsi_fclk_hsdiv); 3508f76ee892STomi Valkeinen } 3509f76ee892STomi Valkeinen 3510f76ee892STomi Valkeinen if (!hfp_blanking_mode) { 3511f76ee892STomi Valkeinen hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon, 3512f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3513f76ee892STomi Valkeinen exiths_clk, ddr_clk_pre, ddr_clk_post); 3514f76ee892STomi Valkeinen hfp_interleave_lp = dsi_compute_interleave_lp(hfp, 3515f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3516f76ee892STomi Valkeinen lp_clk_div, dsi_fclk_hsdiv); 3517f76ee892STomi Valkeinen } 3518f76ee892STomi Valkeinen 3519f76ee892STomi Valkeinen if (!hbp_blanking_mode) { 3520f76ee892STomi Valkeinen hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon, 3521f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3522f76ee892STomi Valkeinen exiths_clk, ddr_clk_pre, ddr_clk_post); 3523f76ee892STomi Valkeinen 3524f76ee892STomi Valkeinen hbp_interleave_lp = dsi_compute_interleave_lp(hbp, 3525f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3526f76ee892STomi Valkeinen lp_clk_div, dsi_fclk_hsdiv); 3527f76ee892STomi Valkeinen } 3528f76ee892STomi Valkeinen 3529f76ee892STomi Valkeinen if (!blanking_mode) { 3530f76ee892STomi Valkeinen bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon, 3531f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3532f76ee892STomi Valkeinen exiths_clk, ddr_clk_pre, ddr_clk_post); 3533f76ee892STomi Valkeinen 3534f76ee892STomi Valkeinen bl_interleave_lp = dsi_compute_interleave_lp(bllp, 3535f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat, 3536f76ee892STomi Valkeinen lp_clk_div, dsi_fclk_hsdiv); 3537f76ee892STomi Valkeinen } 3538f76ee892STomi Valkeinen 3539f76ee892STomi Valkeinen DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n", 3540f76ee892STomi Valkeinen hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs, 3541f76ee892STomi Valkeinen bl_interleave_hs); 3542f76ee892STomi Valkeinen 3543f76ee892STomi Valkeinen DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n", 3544f76ee892STomi Valkeinen hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp, 3545f76ee892STomi Valkeinen bl_interleave_lp); 3546f76ee892STomi Valkeinen 3547f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING4); 3548f76ee892STomi Valkeinen r = FLD_MOD(r, hsa_interleave_hs, 23, 16); 3549f76ee892STomi Valkeinen r = FLD_MOD(r, hfp_interleave_hs, 15, 8); 3550f76ee892STomi Valkeinen r = FLD_MOD(r, hbp_interleave_hs, 7, 0); 3551f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VM_TIMING4, r); 3552f76ee892STomi Valkeinen 3553f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING5); 3554f76ee892STomi Valkeinen r = FLD_MOD(r, hsa_interleave_lp, 23, 16); 3555f76ee892STomi Valkeinen r = FLD_MOD(r, hfp_interleave_lp, 15, 8); 3556f76ee892STomi Valkeinen r = FLD_MOD(r, hbp_interleave_lp, 7, 0); 3557f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VM_TIMING5, r); 3558f76ee892STomi Valkeinen 3559f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING6); 3560f76ee892STomi Valkeinen r = FLD_MOD(r, bl_interleave_hs, 31, 15); 3561f76ee892STomi Valkeinen r = FLD_MOD(r, bl_interleave_lp, 16, 0); 3562f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VM_TIMING6, r); 3563f76ee892STomi Valkeinen } 3564f76ee892STomi Valkeinen 3565f76ee892STomi Valkeinen static int dsi_proto_config(struct platform_device *dsidev) 3566f76ee892STomi Valkeinen { 3567f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3568f76ee892STomi Valkeinen u32 r; 3569f76ee892STomi Valkeinen int buswidth = 0; 3570f76ee892STomi Valkeinen 3571f76ee892STomi Valkeinen dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32, 3572f76ee892STomi Valkeinen DSI_FIFO_SIZE_32, 3573f76ee892STomi Valkeinen DSI_FIFO_SIZE_32, 3574f76ee892STomi Valkeinen DSI_FIFO_SIZE_32); 3575f76ee892STomi Valkeinen 3576f76ee892STomi Valkeinen dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32, 3577f76ee892STomi Valkeinen DSI_FIFO_SIZE_32, 3578f76ee892STomi Valkeinen DSI_FIFO_SIZE_32, 3579f76ee892STomi Valkeinen DSI_FIFO_SIZE_32); 3580f76ee892STomi Valkeinen 3581f76ee892STomi Valkeinen /* XXX what values for the timeouts? */ 3582f76ee892STomi Valkeinen dsi_set_stop_state_counter(dsidev, 0x1000, false, false); 3583f76ee892STomi Valkeinen dsi_set_ta_timeout(dsidev, 0x1fff, true, true); 3584f76ee892STomi Valkeinen dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true); 3585f76ee892STomi Valkeinen dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true); 3586f76ee892STomi Valkeinen 3587f76ee892STomi Valkeinen switch (dsi_get_pixel_size(dsi->pix_fmt)) { 3588f76ee892STomi Valkeinen case 16: 3589f76ee892STomi Valkeinen buswidth = 0; 3590f76ee892STomi Valkeinen break; 3591f76ee892STomi Valkeinen case 18: 3592f76ee892STomi Valkeinen buswidth = 1; 3593f76ee892STomi Valkeinen break; 3594f76ee892STomi Valkeinen case 24: 3595f76ee892STomi Valkeinen buswidth = 2; 3596f76ee892STomi Valkeinen break; 3597f76ee892STomi Valkeinen default: 3598f76ee892STomi Valkeinen BUG(); 3599f76ee892STomi Valkeinen return -EINVAL; 3600f76ee892STomi Valkeinen } 3601f76ee892STomi Valkeinen 3602f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_CTRL); 3603f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ 3604f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ 3605f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ 3606f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/ 3607f76ee892STomi Valkeinen r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */ 3608f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ 3609f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ 3610f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ 3611f76ee892STomi Valkeinen if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { 3612f76ee892STomi Valkeinen r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ 3613f76ee892STomi Valkeinen /* DCS_CMD_CODE, 1=start, 0=continue */ 3614f76ee892STomi Valkeinen r = FLD_MOD(r, 0, 25, 25); 3615f76ee892STomi Valkeinen } 3616f76ee892STomi Valkeinen 3617f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_CTRL, r); 3618f76ee892STomi Valkeinen 3619f76ee892STomi Valkeinen dsi_config_vp_num_line_buffers(dsidev); 3620f76ee892STomi Valkeinen 3621f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { 3622f76ee892STomi Valkeinen dsi_config_vp_sync_events(dsidev); 3623f76ee892STomi Valkeinen dsi_config_blanking_modes(dsidev); 3624f76ee892STomi Valkeinen dsi_config_cmd_mode_interleaving(dsidev); 3625f76ee892STomi Valkeinen } 3626f76ee892STomi Valkeinen 3627f76ee892STomi Valkeinen dsi_vc_initial_config(dsidev, 0); 3628f76ee892STomi Valkeinen dsi_vc_initial_config(dsidev, 1); 3629f76ee892STomi Valkeinen dsi_vc_initial_config(dsidev, 2); 3630f76ee892STomi Valkeinen dsi_vc_initial_config(dsidev, 3); 3631f76ee892STomi Valkeinen 3632f76ee892STomi Valkeinen return 0; 3633f76ee892STomi Valkeinen } 3634f76ee892STomi Valkeinen 3635f76ee892STomi Valkeinen static void dsi_proto_timings(struct platform_device *dsidev) 3636f76ee892STomi Valkeinen { 3637f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3638f76ee892STomi Valkeinen unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail; 3639f76ee892STomi Valkeinen unsigned tclk_pre, tclk_post; 3640f76ee892STomi Valkeinen unsigned ths_prepare, ths_prepare_ths_zero, ths_zero; 3641f76ee892STomi Valkeinen unsigned ths_trail, ths_exit; 3642f76ee892STomi Valkeinen unsigned ddr_clk_pre, ddr_clk_post; 3643f76ee892STomi Valkeinen unsigned enter_hs_mode_lat, exit_hs_mode_lat; 3644f76ee892STomi Valkeinen unsigned ths_eot; 3645f76ee892STomi Valkeinen int ndl = dsi->num_lanes_used - 1; 3646f76ee892STomi Valkeinen u32 r; 3647f76ee892STomi Valkeinen 3648f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); 3649f76ee892STomi Valkeinen ths_prepare = FLD_GET(r, 31, 24); 3650f76ee892STomi Valkeinen ths_prepare_ths_zero = FLD_GET(r, 23, 16); 3651f76ee892STomi Valkeinen ths_zero = ths_prepare_ths_zero - ths_prepare; 3652f76ee892STomi Valkeinen ths_trail = FLD_GET(r, 15, 8); 3653f76ee892STomi Valkeinen ths_exit = FLD_GET(r, 7, 0); 3654f76ee892STomi Valkeinen 3655f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); 3656f76ee892STomi Valkeinen tlpx = FLD_GET(r, 20, 16) * 2; 3657f76ee892STomi Valkeinen tclk_trail = FLD_GET(r, 15, 8); 3658f76ee892STomi Valkeinen tclk_zero = FLD_GET(r, 7, 0); 3659f76ee892STomi Valkeinen 3660f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); 3661f76ee892STomi Valkeinen tclk_prepare = FLD_GET(r, 7, 0); 3662f76ee892STomi Valkeinen 3663f76ee892STomi Valkeinen /* min 8*UI */ 3664f76ee892STomi Valkeinen tclk_pre = 20; 3665f76ee892STomi Valkeinen /* min 60ns + 52*UI */ 3666f76ee892STomi Valkeinen tclk_post = ns2ddr(dsidev, 60) + 26; 3667f76ee892STomi Valkeinen 3668f76ee892STomi Valkeinen ths_eot = DIV_ROUND_UP(4, ndl); 3669f76ee892STomi Valkeinen 3670f76ee892STomi Valkeinen ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare, 3671f76ee892STomi Valkeinen 4); 3672f76ee892STomi Valkeinen ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot; 3673f76ee892STomi Valkeinen 3674f76ee892STomi Valkeinen BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); 3675f76ee892STomi Valkeinen BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); 3676f76ee892STomi Valkeinen 3677f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_CLK_TIMING); 3678f76ee892STomi Valkeinen r = FLD_MOD(r, ddr_clk_pre, 15, 8); 3679f76ee892STomi Valkeinen r = FLD_MOD(r, ddr_clk_post, 7, 0); 3680f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_CLK_TIMING, r); 3681f76ee892STomi Valkeinen 3682f76ee892STomi Valkeinen DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", 3683f76ee892STomi Valkeinen ddr_clk_pre, 3684f76ee892STomi Valkeinen ddr_clk_post); 3685f76ee892STomi Valkeinen 3686f76ee892STomi Valkeinen enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) + 3687f76ee892STomi Valkeinen DIV_ROUND_UP(ths_prepare, 4) + 3688f76ee892STomi Valkeinen DIV_ROUND_UP(ths_zero + 3, 4); 3689f76ee892STomi Valkeinen 3690f76ee892STomi Valkeinen exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot; 3691f76ee892STomi Valkeinen 3692f76ee892STomi Valkeinen r = FLD_VAL(enter_hs_mode_lat, 31, 16) | 3693f76ee892STomi Valkeinen FLD_VAL(exit_hs_mode_lat, 15, 0); 3694f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VM_TIMING7, r); 3695f76ee892STomi Valkeinen 3696f76ee892STomi Valkeinen DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", 3697f76ee892STomi Valkeinen enter_hs_mode_lat, exit_hs_mode_lat); 3698f76ee892STomi Valkeinen 3699f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { 3700f76ee892STomi Valkeinen /* TODO: Implement a video mode check_timings function */ 3701f76ee892STomi Valkeinen int hsa = dsi->vm_timings.hsa; 3702f76ee892STomi Valkeinen int hfp = dsi->vm_timings.hfp; 3703f76ee892STomi Valkeinen int hbp = dsi->vm_timings.hbp; 3704f76ee892STomi Valkeinen int vsa = dsi->vm_timings.vsa; 3705f76ee892STomi Valkeinen int vfp = dsi->vm_timings.vfp; 3706f76ee892STomi Valkeinen int vbp = dsi->vm_timings.vbp; 3707f76ee892STomi Valkeinen int window_sync = dsi->vm_timings.window_sync; 3708f76ee892STomi Valkeinen bool hsync_end; 3709f76ee892STomi Valkeinen struct omap_video_timings *timings = &dsi->timings; 3710f76ee892STomi Valkeinen int bpp = dsi_get_pixel_size(dsi->pix_fmt); 3711f76ee892STomi Valkeinen int tl, t_he, width_bytes; 3712f76ee892STomi Valkeinen 3713f76ee892STomi Valkeinen hsync_end = dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE; 3714f76ee892STomi Valkeinen t_he = hsync_end ? 3715f76ee892STomi Valkeinen ((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0; 3716f76ee892STomi Valkeinen 3717f76ee892STomi Valkeinen width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8); 3718f76ee892STomi Valkeinen 3719f76ee892STomi Valkeinen /* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */ 3720f76ee892STomi Valkeinen tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp + 3721f76ee892STomi Valkeinen DIV_ROUND_UP(width_bytes + 6, ndl) + hbp; 3722f76ee892STomi Valkeinen 3723f76ee892STomi Valkeinen DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp, 3724f76ee892STomi Valkeinen hfp, hsync_end ? hsa : 0, tl); 3725f76ee892STomi Valkeinen DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp, 3726f76ee892STomi Valkeinen vsa, timings->y_res); 3727f76ee892STomi Valkeinen 3728f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING1); 3729f76ee892STomi Valkeinen r = FLD_MOD(r, hbp, 11, 0); /* HBP */ 3730f76ee892STomi Valkeinen r = FLD_MOD(r, hfp, 23, 12); /* HFP */ 3731f76ee892STomi Valkeinen r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */ 3732f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VM_TIMING1, r); 3733f76ee892STomi Valkeinen 3734f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING2); 3735f76ee892STomi Valkeinen r = FLD_MOD(r, vbp, 7, 0); /* VBP */ 3736f76ee892STomi Valkeinen r = FLD_MOD(r, vfp, 15, 8); /* VFP */ 3737f76ee892STomi Valkeinen r = FLD_MOD(r, vsa, 23, 16); /* VSA */ 3738f76ee892STomi Valkeinen r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */ 3739f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VM_TIMING2, r); 3740f76ee892STomi Valkeinen 3741f76ee892STomi Valkeinen r = dsi_read_reg(dsidev, DSI_VM_TIMING3); 3742f76ee892STomi Valkeinen r = FLD_MOD(r, timings->y_res, 14, 0); /* VACT */ 3743f76ee892STomi Valkeinen r = FLD_MOD(r, tl, 31, 16); /* TL */ 3744f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VM_TIMING3, r); 3745f76ee892STomi Valkeinen } 3746f76ee892STomi Valkeinen } 3747f76ee892STomi Valkeinen 3748f76ee892STomi Valkeinen static int dsi_configure_pins(struct omap_dss_device *dssdev, 3749f76ee892STomi Valkeinen const struct omap_dsi_pin_config *pin_cfg) 3750f76ee892STomi Valkeinen { 3751f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 3752f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3753f76ee892STomi Valkeinen int num_pins; 3754f76ee892STomi Valkeinen const int *pins; 3755f76ee892STomi Valkeinen struct dsi_lane_config lanes[DSI_MAX_NR_LANES]; 3756f76ee892STomi Valkeinen int num_lanes; 3757f76ee892STomi Valkeinen int i; 3758f76ee892STomi Valkeinen 3759f76ee892STomi Valkeinen static const enum dsi_lane_function functions[] = { 3760f76ee892STomi Valkeinen DSI_LANE_CLK, 3761f76ee892STomi Valkeinen DSI_LANE_DATA1, 3762f76ee892STomi Valkeinen DSI_LANE_DATA2, 3763f76ee892STomi Valkeinen DSI_LANE_DATA3, 3764f76ee892STomi Valkeinen DSI_LANE_DATA4, 3765f76ee892STomi Valkeinen }; 3766f76ee892STomi Valkeinen 3767f76ee892STomi Valkeinen num_pins = pin_cfg->num_pins; 3768f76ee892STomi Valkeinen pins = pin_cfg->pins; 3769f76ee892STomi Valkeinen 3770f76ee892STomi Valkeinen if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2 3771f76ee892STomi Valkeinen || num_pins % 2 != 0) 3772f76ee892STomi Valkeinen return -EINVAL; 3773f76ee892STomi Valkeinen 3774f76ee892STomi Valkeinen for (i = 0; i < DSI_MAX_NR_LANES; ++i) 3775f76ee892STomi Valkeinen lanes[i].function = DSI_LANE_UNUSED; 3776f76ee892STomi Valkeinen 3777f76ee892STomi Valkeinen num_lanes = 0; 3778f76ee892STomi Valkeinen 3779f76ee892STomi Valkeinen for (i = 0; i < num_pins; i += 2) { 3780f76ee892STomi Valkeinen u8 lane, pol; 3781f76ee892STomi Valkeinen int dx, dy; 3782f76ee892STomi Valkeinen 3783f76ee892STomi Valkeinen dx = pins[i]; 3784f76ee892STomi Valkeinen dy = pins[i + 1]; 3785f76ee892STomi Valkeinen 3786f76ee892STomi Valkeinen if (dx < 0 || dx >= dsi->num_lanes_supported * 2) 3787f76ee892STomi Valkeinen return -EINVAL; 3788f76ee892STomi Valkeinen 3789f76ee892STomi Valkeinen if (dy < 0 || dy >= dsi->num_lanes_supported * 2) 3790f76ee892STomi Valkeinen return -EINVAL; 3791f76ee892STomi Valkeinen 3792f76ee892STomi Valkeinen if (dx & 1) { 3793f76ee892STomi Valkeinen if (dy != dx - 1) 3794f76ee892STomi Valkeinen return -EINVAL; 3795f76ee892STomi Valkeinen pol = 1; 3796f76ee892STomi Valkeinen } else { 3797f76ee892STomi Valkeinen if (dy != dx + 1) 3798f76ee892STomi Valkeinen return -EINVAL; 3799f76ee892STomi Valkeinen pol = 0; 3800f76ee892STomi Valkeinen } 3801f76ee892STomi Valkeinen 3802f76ee892STomi Valkeinen lane = dx / 2; 3803f76ee892STomi Valkeinen 3804f76ee892STomi Valkeinen lanes[lane].function = functions[i / 2]; 3805f76ee892STomi Valkeinen lanes[lane].polarity = pol; 3806f76ee892STomi Valkeinen num_lanes++; 3807f76ee892STomi Valkeinen } 3808f76ee892STomi Valkeinen 3809f76ee892STomi Valkeinen memcpy(dsi->lanes, lanes, sizeof(dsi->lanes)); 3810f76ee892STomi Valkeinen dsi->num_lanes_used = num_lanes; 3811f76ee892STomi Valkeinen 3812f76ee892STomi Valkeinen return 0; 3813f76ee892STomi Valkeinen } 3814f76ee892STomi Valkeinen 3815f76ee892STomi Valkeinen static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) 3816f76ee892STomi Valkeinen { 3817f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 3818f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3819f76ee892STomi Valkeinen struct omap_overlay_manager *mgr = dsi->output.manager; 3820f76ee892STomi Valkeinen int bpp = dsi_get_pixel_size(dsi->pix_fmt); 3821f76ee892STomi Valkeinen struct omap_dss_device *out = &dsi->output; 3822f76ee892STomi Valkeinen u8 data_type; 3823f76ee892STomi Valkeinen u16 word_count; 3824f76ee892STomi Valkeinen int r; 3825f76ee892STomi Valkeinen 3826f76ee892STomi Valkeinen if (out->manager == NULL) { 3827f76ee892STomi Valkeinen DSSERR("failed to enable display: no output/manager\n"); 3828f76ee892STomi Valkeinen return -ENODEV; 3829f76ee892STomi Valkeinen } 3830f76ee892STomi Valkeinen 3831f76ee892STomi Valkeinen r = dsi_display_init_dispc(dsidev, mgr); 3832f76ee892STomi Valkeinen if (r) 3833f76ee892STomi Valkeinen goto err_init_dispc; 3834f76ee892STomi Valkeinen 3835f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { 3836f76ee892STomi Valkeinen switch (dsi->pix_fmt) { 3837f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB888: 3838f76ee892STomi Valkeinen data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24; 3839f76ee892STomi Valkeinen break; 3840f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB666: 3841f76ee892STomi Valkeinen data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18; 3842f76ee892STomi Valkeinen break; 3843f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB666_PACKED: 3844f76ee892STomi Valkeinen data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18; 3845f76ee892STomi Valkeinen break; 3846f76ee892STomi Valkeinen case OMAP_DSS_DSI_FMT_RGB565: 3847f76ee892STomi Valkeinen data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16; 3848f76ee892STomi Valkeinen break; 3849f76ee892STomi Valkeinen default: 3850f76ee892STomi Valkeinen r = -EINVAL; 3851f76ee892STomi Valkeinen goto err_pix_fmt; 3852f76ee892STomi Valkeinen } 3853f76ee892STomi Valkeinen 3854f76ee892STomi Valkeinen dsi_if_enable(dsidev, false); 3855f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, false); 3856f76ee892STomi Valkeinen 3857f76ee892STomi Valkeinen /* MODE, 1 = video mode */ 3858f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4); 3859f76ee892STomi Valkeinen 3860f76ee892STomi Valkeinen word_count = DIV_ROUND_UP(dsi->timings.x_res * bpp, 8); 3861f76ee892STomi Valkeinen 3862f76ee892STomi Valkeinen dsi_vc_write_long_header(dsidev, channel, data_type, 3863f76ee892STomi Valkeinen word_count, 0); 3864f76ee892STomi Valkeinen 3865f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, true); 3866f76ee892STomi Valkeinen dsi_if_enable(dsidev, true); 3867f76ee892STomi Valkeinen } 3868f76ee892STomi Valkeinen 3869f76ee892STomi Valkeinen r = dss_mgr_enable(mgr); 3870f76ee892STomi Valkeinen if (r) 3871f76ee892STomi Valkeinen goto err_mgr_enable; 3872f76ee892STomi Valkeinen 3873f76ee892STomi Valkeinen return 0; 3874f76ee892STomi Valkeinen 3875f76ee892STomi Valkeinen err_mgr_enable: 3876f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { 3877f76ee892STomi Valkeinen dsi_if_enable(dsidev, false); 3878f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, false); 3879f76ee892STomi Valkeinen } 3880f76ee892STomi Valkeinen err_pix_fmt: 3881f76ee892STomi Valkeinen dsi_display_uninit_dispc(dsidev, mgr); 3882f76ee892STomi Valkeinen err_init_dispc: 3883f76ee892STomi Valkeinen return r; 3884f76ee892STomi Valkeinen } 3885f76ee892STomi Valkeinen 3886f76ee892STomi Valkeinen static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel) 3887f76ee892STomi Valkeinen { 3888f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 3889f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3890f76ee892STomi Valkeinen struct omap_overlay_manager *mgr = dsi->output.manager; 3891f76ee892STomi Valkeinen 3892f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { 3893f76ee892STomi Valkeinen dsi_if_enable(dsidev, false); 3894f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, false); 3895f76ee892STomi Valkeinen 3896f76ee892STomi Valkeinen /* MODE, 0 = command mode */ 3897f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4); 3898f76ee892STomi Valkeinen 3899f76ee892STomi Valkeinen dsi_vc_enable(dsidev, channel, true); 3900f76ee892STomi Valkeinen dsi_if_enable(dsidev, true); 3901f76ee892STomi Valkeinen } 3902f76ee892STomi Valkeinen 3903f76ee892STomi Valkeinen dss_mgr_disable(mgr); 3904f76ee892STomi Valkeinen 3905f76ee892STomi Valkeinen dsi_display_uninit_dispc(dsidev, mgr); 3906f76ee892STomi Valkeinen } 3907f76ee892STomi Valkeinen 3908f76ee892STomi Valkeinen static void dsi_update_screen_dispc(struct platform_device *dsidev) 3909f76ee892STomi Valkeinen { 3910f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 3911f76ee892STomi Valkeinen struct omap_overlay_manager *mgr = dsi->output.manager; 3912f76ee892STomi Valkeinen unsigned bytespp; 3913f76ee892STomi Valkeinen unsigned bytespl; 3914f76ee892STomi Valkeinen unsigned bytespf; 3915f76ee892STomi Valkeinen unsigned total_len; 3916f76ee892STomi Valkeinen unsigned packet_payload; 3917f76ee892STomi Valkeinen unsigned packet_len; 3918f76ee892STomi Valkeinen u32 l; 3919f76ee892STomi Valkeinen int r; 3920f76ee892STomi Valkeinen const unsigned channel = dsi->update_channel; 3921f76ee892STomi Valkeinen const unsigned line_buf_size = dsi->line_buffer_size; 3922f76ee892STomi Valkeinen u16 w = dsi->timings.x_res; 3923f76ee892STomi Valkeinen u16 h = dsi->timings.y_res; 3924f76ee892STomi Valkeinen 3925f76ee892STomi Valkeinen DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h); 3926f76ee892STomi Valkeinen 3927f76ee892STomi Valkeinen dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP); 3928f76ee892STomi Valkeinen 3929f76ee892STomi Valkeinen bytespp = dsi_get_pixel_size(dsi->pix_fmt) / 8; 3930f76ee892STomi Valkeinen bytespl = w * bytespp; 3931f76ee892STomi Valkeinen bytespf = bytespl * h; 3932f76ee892STomi Valkeinen 3933f76ee892STomi Valkeinen /* NOTE: packet_payload has to be equal to N * bytespl, where N is 3934f76ee892STomi Valkeinen * number of lines in a packet. See errata about VP_CLK_RATIO */ 3935f76ee892STomi Valkeinen 3936f76ee892STomi Valkeinen if (bytespf < line_buf_size) 3937f76ee892STomi Valkeinen packet_payload = bytespf; 3938f76ee892STomi Valkeinen else 3939f76ee892STomi Valkeinen packet_payload = (line_buf_size) / bytespl * bytespl; 3940f76ee892STomi Valkeinen 3941f76ee892STomi Valkeinen packet_len = packet_payload + 1; /* 1 byte for DCS cmd */ 3942f76ee892STomi Valkeinen total_len = (bytespf / packet_payload) * packet_len; 3943f76ee892STomi Valkeinen 3944f76ee892STomi Valkeinen if (bytespf % packet_payload) 3945f76ee892STomi Valkeinen total_len += (bytespf % packet_payload) + 1; 3946f76ee892STomi Valkeinen 3947f76ee892STomi Valkeinen l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ 3948f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VC_TE(channel), l); 3949f76ee892STomi Valkeinen 3950f76ee892STomi Valkeinen dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE, 3951f76ee892STomi Valkeinen packet_len, 0); 3952f76ee892STomi Valkeinen 3953f76ee892STomi Valkeinen if (dsi->te_enabled) 3954f76ee892STomi Valkeinen l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ 3955f76ee892STomi Valkeinen else 3956f76ee892STomi Valkeinen l = FLD_MOD(l, 1, 31, 31); /* TE_START */ 3957f76ee892STomi Valkeinen dsi_write_reg(dsidev, DSI_VC_TE(channel), l); 3958f76ee892STomi Valkeinen 3959f76ee892STomi Valkeinen /* We put SIDLEMODE to no-idle for the duration of the transfer, 3960f76ee892STomi Valkeinen * because DSS interrupts are not capable of waking up the CPU and the 3961f76ee892STomi Valkeinen * framedone interrupt could be delayed for quite a long time. I think 3962f76ee892STomi Valkeinen * the same goes for any DSS interrupts, but for some reason I have not 3963f76ee892STomi Valkeinen * seen the problem anywhere else than here. 3964f76ee892STomi Valkeinen */ 3965f76ee892STomi Valkeinen dispc_disable_sidle(); 3966f76ee892STomi Valkeinen 3967f76ee892STomi Valkeinen dsi_perf_mark_start(dsidev); 3968f76ee892STomi Valkeinen 3969f76ee892STomi Valkeinen r = schedule_delayed_work(&dsi->framedone_timeout_work, 3970f76ee892STomi Valkeinen msecs_to_jiffies(250)); 3971f76ee892STomi Valkeinen BUG_ON(r == 0); 3972f76ee892STomi Valkeinen 3973f76ee892STomi Valkeinen dss_mgr_set_timings(mgr, &dsi->timings); 3974f76ee892STomi Valkeinen 3975f76ee892STomi Valkeinen dss_mgr_start_update(mgr); 3976f76ee892STomi Valkeinen 3977f76ee892STomi Valkeinen if (dsi->te_enabled) { 3978f76ee892STomi Valkeinen /* disable LP_RX_TO, so that we can receive TE. Time to wait 3979f76ee892STomi Valkeinen * for TE is longer than the timer allows */ 3980f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ 3981f76ee892STomi Valkeinen 3982f76ee892STomi Valkeinen dsi_vc_send_bta(dsidev, channel); 3983f76ee892STomi Valkeinen 3984f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE 3985f76ee892STomi Valkeinen mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250)); 3986f76ee892STomi Valkeinen #endif 3987f76ee892STomi Valkeinen } 3988f76ee892STomi Valkeinen } 3989f76ee892STomi Valkeinen 3990f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE 3991*6c789357SKees Cook static void dsi_te_timeout(struct timer_list *unused) 3992f76ee892STomi Valkeinen { 3993f76ee892STomi Valkeinen DSSERR("TE not received for 250ms!\n"); 3994f76ee892STomi Valkeinen } 3995f76ee892STomi Valkeinen #endif 3996f76ee892STomi Valkeinen 3997f76ee892STomi Valkeinen static void dsi_handle_framedone(struct platform_device *dsidev, int error) 3998f76ee892STomi Valkeinen { 3999f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4000f76ee892STomi Valkeinen 4001f76ee892STomi Valkeinen /* SIDLEMODE back to smart-idle */ 4002f76ee892STomi Valkeinen dispc_enable_sidle(); 4003f76ee892STomi Valkeinen 4004f76ee892STomi Valkeinen if (dsi->te_enabled) { 4005f76ee892STomi Valkeinen /* enable LP_RX_TO again after the TE */ 4006f76ee892STomi Valkeinen REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ 4007f76ee892STomi Valkeinen } 4008f76ee892STomi Valkeinen 4009f76ee892STomi Valkeinen dsi->framedone_callback(error, dsi->framedone_data); 4010f76ee892STomi Valkeinen 4011f76ee892STomi Valkeinen if (!error) 4012f76ee892STomi Valkeinen dsi_perf_show(dsidev, "DISPC"); 4013f76ee892STomi Valkeinen } 4014f76ee892STomi Valkeinen 4015f76ee892STomi Valkeinen static void dsi_framedone_timeout_work_callback(struct work_struct *work) 4016f76ee892STomi Valkeinen { 4017f76ee892STomi Valkeinen struct dsi_data *dsi = container_of(work, struct dsi_data, 4018f76ee892STomi Valkeinen framedone_timeout_work.work); 4019f76ee892STomi Valkeinen /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after 4020f76ee892STomi Valkeinen * 250ms which would conflict with this timeout work. What should be 4021f76ee892STomi Valkeinen * done is first cancel the transfer on the HW, and then cancel the 4022f76ee892STomi Valkeinen * possibly scheduled framedone work. However, cancelling the transfer 4023f76ee892STomi Valkeinen * on the HW is buggy, and would probably require resetting the whole 4024f76ee892STomi Valkeinen * DSI */ 4025f76ee892STomi Valkeinen 4026f76ee892STomi Valkeinen DSSERR("Framedone not received for 250ms!\n"); 4027f76ee892STomi Valkeinen 4028f76ee892STomi Valkeinen dsi_handle_framedone(dsi->pdev, -ETIMEDOUT); 4029f76ee892STomi Valkeinen } 4030f76ee892STomi Valkeinen 4031f76ee892STomi Valkeinen static void dsi_framedone_irq_callback(void *data) 4032f76ee892STomi Valkeinen { 4033f76ee892STomi Valkeinen struct platform_device *dsidev = (struct platform_device *) data; 4034f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4035f76ee892STomi Valkeinen 4036f76ee892STomi Valkeinen /* Note: We get FRAMEDONE when DISPC has finished sending pixels and 4037f76ee892STomi Valkeinen * turns itself off. However, DSI still has the pixels in its buffers, 4038f76ee892STomi Valkeinen * and is sending the data. 4039f76ee892STomi Valkeinen */ 4040f76ee892STomi Valkeinen 4041f76ee892STomi Valkeinen cancel_delayed_work(&dsi->framedone_timeout_work); 4042f76ee892STomi Valkeinen 4043f76ee892STomi Valkeinen dsi_handle_framedone(dsidev, 0); 4044f76ee892STomi Valkeinen } 4045f76ee892STomi Valkeinen 4046f76ee892STomi Valkeinen static int dsi_update(struct omap_dss_device *dssdev, int channel, 4047f76ee892STomi Valkeinen void (*callback)(int, void *), void *data) 4048f76ee892STomi Valkeinen { 4049f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4050f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4051f76ee892STomi Valkeinen u16 dw, dh; 4052f76ee892STomi Valkeinen 4053f76ee892STomi Valkeinen dsi_perf_mark_setup(dsidev); 4054f76ee892STomi Valkeinen 4055f76ee892STomi Valkeinen dsi->update_channel = channel; 4056f76ee892STomi Valkeinen 4057f76ee892STomi Valkeinen dsi->framedone_callback = callback; 4058f76ee892STomi Valkeinen dsi->framedone_data = data; 4059f76ee892STomi Valkeinen 4060f76ee892STomi Valkeinen dw = dsi->timings.x_res; 4061f76ee892STomi Valkeinen dh = dsi->timings.y_res; 4062f76ee892STomi Valkeinen 4063f76ee892STomi Valkeinen #ifdef DSI_PERF_MEASURE 4064f76ee892STomi Valkeinen dsi->update_bytes = dw * dh * 4065f76ee892STomi Valkeinen dsi_get_pixel_size(dsi->pix_fmt) / 8; 4066f76ee892STomi Valkeinen #endif 4067f76ee892STomi Valkeinen dsi_update_screen_dispc(dsidev); 4068f76ee892STomi Valkeinen 4069f76ee892STomi Valkeinen return 0; 4070f76ee892STomi Valkeinen } 4071f76ee892STomi Valkeinen 4072f76ee892STomi Valkeinen /* Display funcs */ 4073f76ee892STomi Valkeinen 4074f76ee892STomi Valkeinen static int dsi_configure_dispc_clocks(struct platform_device *dsidev) 4075f76ee892STomi Valkeinen { 4076f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4077f76ee892STomi Valkeinen struct dispc_clock_info dispc_cinfo; 4078f76ee892STomi Valkeinen int r; 4079f76ee892STomi Valkeinen unsigned long fck; 4080f76ee892STomi Valkeinen 4081f76ee892STomi Valkeinen fck = dsi_get_pll_hsdiv_dispc_rate(dsidev); 4082f76ee892STomi Valkeinen 4083f76ee892STomi Valkeinen dispc_cinfo.lck_div = dsi->user_dispc_cinfo.lck_div; 4084f76ee892STomi Valkeinen dispc_cinfo.pck_div = dsi->user_dispc_cinfo.pck_div; 4085f76ee892STomi Valkeinen 4086f76ee892STomi Valkeinen r = dispc_calc_clock_rates(fck, &dispc_cinfo); 4087f76ee892STomi Valkeinen if (r) { 4088f76ee892STomi Valkeinen DSSERR("Failed to calc dispc clocks\n"); 4089f76ee892STomi Valkeinen return r; 4090f76ee892STomi Valkeinen } 4091f76ee892STomi Valkeinen 4092f76ee892STomi Valkeinen dsi->mgr_config.clock_info = dispc_cinfo; 4093f76ee892STomi Valkeinen 4094f76ee892STomi Valkeinen return 0; 4095f76ee892STomi Valkeinen } 4096f76ee892STomi Valkeinen 4097f76ee892STomi Valkeinen static int dsi_display_init_dispc(struct platform_device *dsidev, 4098f76ee892STomi Valkeinen struct omap_overlay_manager *mgr) 4099f76ee892STomi Valkeinen { 4100f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4101f76ee892STomi Valkeinen int r; 4102f76ee892STomi Valkeinen 4103f76ee892STomi Valkeinen dss_select_lcd_clk_source(mgr->id, dsi->module_id == 0 ? 4104f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC : 4105f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC); 4106f76ee892STomi Valkeinen 4107f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) { 4108f76ee892STomi Valkeinen r = dss_mgr_register_framedone_handler(mgr, 4109f76ee892STomi Valkeinen dsi_framedone_irq_callback, dsidev); 4110f76ee892STomi Valkeinen if (r) { 4111f76ee892STomi Valkeinen DSSERR("can't register FRAMEDONE handler\n"); 4112f76ee892STomi Valkeinen goto err; 4113f76ee892STomi Valkeinen } 4114f76ee892STomi Valkeinen 4115f76ee892STomi Valkeinen dsi->mgr_config.stallmode = true; 4116f76ee892STomi Valkeinen dsi->mgr_config.fifohandcheck = true; 4117f76ee892STomi Valkeinen } else { 4118f76ee892STomi Valkeinen dsi->mgr_config.stallmode = false; 4119f76ee892STomi Valkeinen dsi->mgr_config.fifohandcheck = false; 4120f76ee892STomi Valkeinen } 4121f76ee892STomi Valkeinen 4122f76ee892STomi Valkeinen /* 4123f76ee892STomi Valkeinen * override interlace, logic level and edge related parameters in 4124f76ee892STomi Valkeinen * omap_video_timings with default values 4125f76ee892STomi Valkeinen */ 4126f76ee892STomi Valkeinen dsi->timings.interlace = false; 4127f76ee892STomi Valkeinen dsi->timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; 4128f76ee892STomi Valkeinen dsi->timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; 4129f76ee892STomi Valkeinen dsi->timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 4130f76ee892STomi Valkeinen dsi->timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH; 4131f76ee892STomi Valkeinen dsi->timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; 4132f76ee892STomi Valkeinen 4133f76ee892STomi Valkeinen dss_mgr_set_timings(mgr, &dsi->timings); 4134f76ee892STomi Valkeinen 4135f76ee892STomi Valkeinen r = dsi_configure_dispc_clocks(dsidev); 4136f76ee892STomi Valkeinen if (r) 4137f76ee892STomi Valkeinen goto err1; 4138f76ee892STomi Valkeinen 4139f76ee892STomi Valkeinen dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; 4140f76ee892STomi Valkeinen dsi->mgr_config.video_port_width = 4141f76ee892STomi Valkeinen dsi_get_pixel_size(dsi->pix_fmt); 4142f76ee892STomi Valkeinen dsi->mgr_config.lcden_sig_polarity = 0; 4143f76ee892STomi Valkeinen 4144f76ee892STomi Valkeinen dss_mgr_set_lcd_config(mgr, &dsi->mgr_config); 4145f76ee892STomi Valkeinen 4146f76ee892STomi Valkeinen return 0; 4147f76ee892STomi Valkeinen err1: 4148f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) 4149f76ee892STomi Valkeinen dss_mgr_unregister_framedone_handler(mgr, 4150f76ee892STomi Valkeinen dsi_framedone_irq_callback, dsidev); 4151f76ee892STomi Valkeinen err: 4152f76ee892STomi Valkeinen dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK); 4153f76ee892STomi Valkeinen return r; 4154f76ee892STomi Valkeinen } 4155f76ee892STomi Valkeinen 4156f76ee892STomi Valkeinen static void dsi_display_uninit_dispc(struct platform_device *dsidev, 4157f76ee892STomi Valkeinen struct omap_overlay_manager *mgr) 4158f76ee892STomi Valkeinen { 4159f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4160f76ee892STomi Valkeinen 4161f76ee892STomi Valkeinen if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) 4162f76ee892STomi Valkeinen dss_mgr_unregister_framedone_handler(mgr, 4163f76ee892STomi Valkeinen dsi_framedone_irq_callback, dsidev); 4164f76ee892STomi Valkeinen 4165f76ee892STomi Valkeinen dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK); 4166f76ee892STomi Valkeinen } 4167f76ee892STomi Valkeinen 4168f76ee892STomi Valkeinen static int dsi_configure_dsi_clocks(struct platform_device *dsidev) 4169f76ee892STomi Valkeinen { 4170f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4171f76ee892STomi Valkeinen struct dss_pll_clock_info cinfo; 4172f76ee892STomi Valkeinen int r; 4173f76ee892STomi Valkeinen 4174f76ee892STomi Valkeinen cinfo = dsi->user_dsi_cinfo; 4175f76ee892STomi Valkeinen 4176f76ee892STomi Valkeinen r = dss_pll_set_config(&dsi->pll, &cinfo); 4177f76ee892STomi Valkeinen if (r) { 4178f76ee892STomi Valkeinen DSSERR("Failed to set dsi clocks\n"); 4179f76ee892STomi Valkeinen return r; 4180f76ee892STomi Valkeinen } 4181f76ee892STomi Valkeinen 4182f76ee892STomi Valkeinen return 0; 4183f76ee892STomi Valkeinen } 4184f76ee892STomi Valkeinen 4185f76ee892STomi Valkeinen static int dsi_display_init_dsi(struct platform_device *dsidev) 4186f76ee892STomi Valkeinen { 4187f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4188f76ee892STomi Valkeinen int r; 4189f76ee892STomi Valkeinen 4190f76ee892STomi Valkeinen r = dss_pll_enable(&dsi->pll); 4191f76ee892STomi Valkeinen if (r) 4192f76ee892STomi Valkeinen goto err0; 4193f76ee892STomi Valkeinen 4194f76ee892STomi Valkeinen r = dsi_configure_dsi_clocks(dsidev); 4195f76ee892STomi Valkeinen if (r) 4196f76ee892STomi Valkeinen goto err1; 4197f76ee892STomi Valkeinen 4198f76ee892STomi Valkeinen dss_select_dsi_clk_source(dsi->module_id, dsi->module_id == 0 ? 4199f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI : 4200f76ee892STomi Valkeinen OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI); 4201f76ee892STomi Valkeinen 4202f76ee892STomi Valkeinen DSSDBG("PLL OK\n"); 4203f76ee892STomi Valkeinen 4204f76ee892STomi Valkeinen r = dsi_cio_init(dsidev); 4205f76ee892STomi Valkeinen if (r) 4206f76ee892STomi Valkeinen goto err2; 4207f76ee892STomi Valkeinen 4208f76ee892STomi Valkeinen _dsi_print_reset_status(dsidev); 4209f76ee892STomi Valkeinen 4210f76ee892STomi Valkeinen dsi_proto_timings(dsidev); 4211f76ee892STomi Valkeinen dsi_set_lp_clk_divisor(dsidev); 4212f76ee892STomi Valkeinen 4213f76ee892STomi Valkeinen if (1) 4214f76ee892STomi Valkeinen _dsi_print_reset_status(dsidev); 4215f76ee892STomi Valkeinen 4216f76ee892STomi Valkeinen r = dsi_proto_config(dsidev); 4217f76ee892STomi Valkeinen if (r) 4218f76ee892STomi Valkeinen goto err3; 4219f76ee892STomi Valkeinen 4220f76ee892STomi Valkeinen /* enable interface */ 4221f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 0, 1); 4222f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 1, 1); 4223f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 2, 1); 4224f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 3, 1); 4225f76ee892STomi Valkeinen dsi_if_enable(dsidev, 1); 4226f76ee892STomi Valkeinen dsi_force_tx_stop_mode_io(dsidev); 4227f76ee892STomi Valkeinen 4228f76ee892STomi Valkeinen return 0; 4229f76ee892STomi Valkeinen err3: 4230f76ee892STomi Valkeinen dsi_cio_uninit(dsidev); 4231f76ee892STomi Valkeinen err2: 4232f76ee892STomi Valkeinen dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); 4233f76ee892STomi Valkeinen err1: 4234f76ee892STomi Valkeinen dss_pll_disable(&dsi->pll); 4235f76ee892STomi Valkeinen err0: 4236f76ee892STomi Valkeinen return r; 4237f76ee892STomi Valkeinen } 4238f76ee892STomi Valkeinen 4239f76ee892STomi Valkeinen static void dsi_display_uninit_dsi(struct platform_device *dsidev, 4240f76ee892STomi Valkeinen bool disconnect_lanes, bool enter_ulps) 4241f76ee892STomi Valkeinen { 4242f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4243f76ee892STomi Valkeinen 4244f76ee892STomi Valkeinen if (enter_ulps && !dsi->ulps_enabled) 4245f76ee892STomi Valkeinen dsi_enter_ulps(dsidev); 4246f76ee892STomi Valkeinen 4247f76ee892STomi Valkeinen /* disable interface */ 4248f76ee892STomi Valkeinen dsi_if_enable(dsidev, 0); 4249f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 0, 0); 4250f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 1, 0); 4251f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 2, 0); 4252f76ee892STomi Valkeinen dsi_vc_enable(dsidev, 3, 0); 4253f76ee892STomi Valkeinen 4254f76ee892STomi Valkeinen dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK); 4255f76ee892STomi Valkeinen dsi_cio_uninit(dsidev); 4256f76ee892STomi Valkeinen dsi_pll_uninit(dsidev, disconnect_lanes); 4257f76ee892STomi Valkeinen } 4258f76ee892STomi Valkeinen 4259f76ee892STomi Valkeinen static int dsi_display_enable(struct omap_dss_device *dssdev) 4260f76ee892STomi Valkeinen { 4261f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4262f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4263f76ee892STomi Valkeinen int r = 0; 4264f76ee892STomi Valkeinen 4265f76ee892STomi Valkeinen DSSDBG("dsi_display_enable\n"); 4266f76ee892STomi Valkeinen 4267f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 4268f76ee892STomi Valkeinen 4269f76ee892STomi Valkeinen mutex_lock(&dsi->lock); 4270f76ee892STomi Valkeinen 4271f76ee892STomi Valkeinen r = dsi_runtime_get(dsidev); 4272f76ee892STomi Valkeinen if (r) 4273f76ee892STomi Valkeinen goto err_get_dsi; 4274f76ee892STomi Valkeinen 4275f76ee892STomi Valkeinen _dsi_initialize_irq(dsidev); 4276f76ee892STomi Valkeinen 4277f76ee892STomi Valkeinen r = dsi_display_init_dsi(dsidev); 4278f76ee892STomi Valkeinen if (r) 4279f76ee892STomi Valkeinen goto err_init_dsi; 4280f76ee892STomi Valkeinen 4281f76ee892STomi Valkeinen mutex_unlock(&dsi->lock); 4282f76ee892STomi Valkeinen 4283f76ee892STomi Valkeinen return 0; 4284f76ee892STomi Valkeinen 4285f76ee892STomi Valkeinen err_init_dsi: 4286f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 4287f76ee892STomi Valkeinen err_get_dsi: 4288f76ee892STomi Valkeinen mutex_unlock(&dsi->lock); 4289f76ee892STomi Valkeinen DSSDBG("dsi_display_enable FAILED\n"); 4290f76ee892STomi Valkeinen return r; 4291f76ee892STomi Valkeinen } 4292f76ee892STomi Valkeinen 4293f76ee892STomi Valkeinen static void dsi_display_disable(struct omap_dss_device *dssdev, 4294f76ee892STomi Valkeinen bool disconnect_lanes, bool enter_ulps) 4295f76ee892STomi Valkeinen { 4296f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4297f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4298f76ee892STomi Valkeinen 4299f76ee892STomi Valkeinen DSSDBG("dsi_display_disable\n"); 4300f76ee892STomi Valkeinen 4301f76ee892STomi Valkeinen WARN_ON(!dsi_bus_is_locked(dsidev)); 4302f76ee892STomi Valkeinen 4303f76ee892STomi Valkeinen mutex_lock(&dsi->lock); 4304f76ee892STomi Valkeinen 4305f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 0); 4306f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 1); 4307f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 2); 4308f76ee892STomi Valkeinen dsi_sync_vc(dsidev, 3); 4309f76ee892STomi Valkeinen 4310f76ee892STomi Valkeinen dsi_display_uninit_dsi(dsidev, disconnect_lanes, enter_ulps); 4311f76ee892STomi Valkeinen 4312f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 4313f76ee892STomi Valkeinen 4314f76ee892STomi Valkeinen mutex_unlock(&dsi->lock); 4315f76ee892STomi Valkeinen } 4316f76ee892STomi Valkeinen 4317f76ee892STomi Valkeinen static int dsi_enable_te(struct omap_dss_device *dssdev, bool enable) 4318f76ee892STomi Valkeinen { 4319f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4320f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4321f76ee892STomi Valkeinen 4322f76ee892STomi Valkeinen dsi->te_enabled = enable; 4323f76ee892STomi Valkeinen return 0; 4324f76ee892STomi Valkeinen } 4325f76ee892STomi Valkeinen 4326f76ee892STomi Valkeinen #ifdef PRINT_VERBOSE_VM_TIMINGS 4327f76ee892STomi Valkeinen static void print_dsi_vm(const char *str, 4328f76ee892STomi Valkeinen const struct omap_dss_dsi_videomode_timings *t) 4329f76ee892STomi Valkeinen { 4330f76ee892STomi Valkeinen unsigned long byteclk = t->hsclk / 4; 4331f76ee892STomi Valkeinen int bl, wc, pps, tot; 4332f76ee892STomi Valkeinen 4333f76ee892STomi Valkeinen wc = DIV_ROUND_UP(t->hact * t->bitspp, 8); 4334f76ee892STomi Valkeinen pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */ 4335f76ee892STomi Valkeinen bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp; 4336f76ee892STomi Valkeinen tot = bl + pps; 4337f76ee892STomi Valkeinen 4338f76ee892STomi Valkeinen #define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk)) 4339f76ee892STomi Valkeinen 4340f76ee892STomi Valkeinen pr_debug("%s bck %lu, %u/%u/%u/%u/%u/%u = %u+%u = %u, " 4341f76ee892STomi Valkeinen "%u/%u/%u/%u/%u/%u = %u + %u = %u\n", 4342f76ee892STomi Valkeinen str, 4343f76ee892STomi Valkeinen byteclk, 4344f76ee892STomi Valkeinen t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp, 4345f76ee892STomi Valkeinen bl, pps, tot, 4346f76ee892STomi Valkeinen TO_DSI_T(t->hss), 4347f76ee892STomi Valkeinen TO_DSI_T(t->hsa), 4348f76ee892STomi Valkeinen TO_DSI_T(t->hse), 4349f76ee892STomi Valkeinen TO_DSI_T(t->hbp), 4350f76ee892STomi Valkeinen TO_DSI_T(pps), 4351f76ee892STomi Valkeinen TO_DSI_T(t->hfp), 4352f76ee892STomi Valkeinen 4353f76ee892STomi Valkeinen TO_DSI_T(bl), 4354f76ee892STomi Valkeinen TO_DSI_T(pps), 4355f76ee892STomi Valkeinen 4356f76ee892STomi Valkeinen TO_DSI_T(tot)); 4357f76ee892STomi Valkeinen #undef TO_DSI_T 4358f76ee892STomi Valkeinen } 4359f76ee892STomi Valkeinen 4360f76ee892STomi Valkeinen static void print_dispc_vm(const char *str, const struct omap_video_timings *t) 4361f76ee892STomi Valkeinen { 4362f76ee892STomi Valkeinen unsigned long pck = t->pixelclock; 4363f76ee892STomi Valkeinen int hact, bl, tot; 4364f76ee892STomi Valkeinen 4365f76ee892STomi Valkeinen hact = t->x_res; 4366f76ee892STomi Valkeinen bl = t->hsw + t->hbp + t->hfp; 4367f76ee892STomi Valkeinen tot = hact + bl; 4368f76ee892STomi Valkeinen 4369f76ee892STomi Valkeinen #define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck)) 4370f76ee892STomi Valkeinen 4371f76ee892STomi Valkeinen pr_debug("%s pck %lu, %u/%u/%u/%u = %u+%u = %u, " 4372f76ee892STomi Valkeinen "%u/%u/%u/%u = %u + %u = %u\n", 4373f76ee892STomi Valkeinen str, 4374f76ee892STomi Valkeinen pck, 4375f76ee892STomi Valkeinen t->hsw, t->hbp, hact, t->hfp, 4376f76ee892STomi Valkeinen bl, hact, tot, 4377f76ee892STomi Valkeinen TO_DISPC_T(t->hsw), 4378f76ee892STomi Valkeinen TO_DISPC_T(t->hbp), 4379f76ee892STomi Valkeinen TO_DISPC_T(hact), 4380f76ee892STomi Valkeinen TO_DISPC_T(t->hfp), 4381f76ee892STomi Valkeinen TO_DISPC_T(bl), 4382f76ee892STomi Valkeinen TO_DISPC_T(hact), 4383f76ee892STomi Valkeinen TO_DISPC_T(tot)); 4384f76ee892STomi Valkeinen #undef TO_DISPC_T 4385f76ee892STomi Valkeinen } 4386f76ee892STomi Valkeinen 4387f76ee892STomi Valkeinen /* note: this is not quite accurate */ 4388f76ee892STomi Valkeinen static void print_dsi_dispc_vm(const char *str, 4389f76ee892STomi Valkeinen const struct omap_dss_dsi_videomode_timings *t) 4390f76ee892STomi Valkeinen { 4391f76ee892STomi Valkeinen struct omap_video_timings vm = { 0 }; 4392f76ee892STomi Valkeinen unsigned long byteclk = t->hsclk / 4; 4393f76ee892STomi Valkeinen unsigned long pck; 4394f76ee892STomi Valkeinen u64 dsi_tput; 4395f76ee892STomi Valkeinen int dsi_hact, dsi_htot; 4396f76ee892STomi Valkeinen 4397f76ee892STomi Valkeinen dsi_tput = (u64)byteclk * t->ndl * 8; 4398f76ee892STomi Valkeinen pck = (u32)div64_u64(dsi_tput, t->bitspp); 4399f76ee892STomi Valkeinen dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl); 4400f76ee892STomi Valkeinen dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp; 4401f76ee892STomi Valkeinen 4402f76ee892STomi Valkeinen vm.pixelclock = pck; 4403f76ee892STomi Valkeinen vm.hsw = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk); 4404f76ee892STomi Valkeinen vm.hbp = div64_u64((u64)t->hbp * pck, byteclk); 4405f76ee892STomi Valkeinen vm.hfp = div64_u64((u64)t->hfp * pck, byteclk); 4406f76ee892STomi Valkeinen vm.x_res = t->hact; 4407f76ee892STomi Valkeinen 4408f76ee892STomi Valkeinen print_dispc_vm(str, &vm); 4409f76ee892STomi Valkeinen } 4410f76ee892STomi Valkeinen #endif /* PRINT_VERBOSE_VM_TIMINGS */ 4411f76ee892STomi Valkeinen 4412f76ee892STomi Valkeinen static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck, 4413f76ee892STomi Valkeinen unsigned long pck, void *data) 4414f76ee892STomi Valkeinen { 4415f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx = data; 4416f76ee892STomi Valkeinen struct omap_video_timings *t = &ctx->dispc_vm; 4417f76ee892STomi Valkeinen 4418f76ee892STomi Valkeinen ctx->dispc_cinfo.lck_div = lckd; 4419f76ee892STomi Valkeinen ctx->dispc_cinfo.pck_div = pckd; 4420f76ee892STomi Valkeinen ctx->dispc_cinfo.lck = lck; 4421f76ee892STomi Valkeinen ctx->dispc_cinfo.pck = pck; 4422f76ee892STomi Valkeinen 4423f76ee892STomi Valkeinen *t = *ctx->config->timings; 4424f76ee892STomi Valkeinen t->pixelclock = pck; 4425f76ee892STomi Valkeinen t->x_res = ctx->config->timings->x_res; 4426f76ee892STomi Valkeinen t->y_res = ctx->config->timings->y_res; 4427f76ee892STomi Valkeinen t->hsw = t->hfp = t->hbp = t->vsw = 1; 4428f76ee892STomi Valkeinen t->vfp = t->vbp = 0; 4429f76ee892STomi Valkeinen 4430f76ee892STomi Valkeinen return true; 4431f76ee892STomi Valkeinen } 4432f76ee892STomi Valkeinen 4433f76ee892STomi Valkeinen static bool dsi_cm_calc_hsdiv_cb(int m_dispc, unsigned long dispc, 4434f76ee892STomi Valkeinen void *data) 4435f76ee892STomi Valkeinen { 4436f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx = data; 4437f76ee892STomi Valkeinen 4438f76ee892STomi Valkeinen ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc; 4439f76ee892STomi Valkeinen ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc; 4440f76ee892STomi Valkeinen 4441f76ee892STomi Valkeinen return dispc_div_calc(dispc, ctx->req_pck_min, ctx->req_pck_max, 4442f76ee892STomi Valkeinen dsi_cm_calc_dispc_cb, ctx); 4443f76ee892STomi Valkeinen } 4444f76ee892STomi Valkeinen 4445f76ee892STomi Valkeinen static bool dsi_cm_calc_pll_cb(int n, int m, unsigned long fint, 4446f76ee892STomi Valkeinen unsigned long clkdco, void *data) 4447f76ee892STomi Valkeinen { 4448f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx = data; 4449f76ee892STomi Valkeinen 4450f76ee892STomi Valkeinen ctx->dsi_cinfo.n = n; 4451f76ee892STomi Valkeinen ctx->dsi_cinfo.m = m; 4452f76ee892STomi Valkeinen ctx->dsi_cinfo.fint = fint; 4453f76ee892STomi Valkeinen ctx->dsi_cinfo.clkdco = clkdco; 4454f76ee892STomi Valkeinen 4455f76ee892STomi Valkeinen return dss_pll_hsdiv_calc(ctx->pll, clkdco, ctx->req_pck_min, 4456f76ee892STomi Valkeinen dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), 4457f76ee892STomi Valkeinen dsi_cm_calc_hsdiv_cb, ctx); 4458f76ee892STomi Valkeinen } 4459f76ee892STomi Valkeinen 4460f76ee892STomi Valkeinen static bool dsi_cm_calc(struct dsi_data *dsi, 4461f76ee892STomi Valkeinen const struct omap_dss_dsi_config *cfg, 4462f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx) 4463f76ee892STomi Valkeinen { 4464f76ee892STomi Valkeinen unsigned long clkin; 4465f76ee892STomi Valkeinen int bitspp, ndl; 4466f76ee892STomi Valkeinen unsigned long pll_min, pll_max; 4467f76ee892STomi Valkeinen unsigned long pck, txbyteclk; 4468f76ee892STomi Valkeinen 4469f76ee892STomi Valkeinen clkin = clk_get_rate(dsi->pll.clkin); 4470f76ee892STomi Valkeinen bitspp = dsi_get_pixel_size(cfg->pixel_format); 4471f76ee892STomi Valkeinen ndl = dsi->num_lanes_used - 1; 4472f76ee892STomi Valkeinen 4473f76ee892STomi Valkeinen /* 4474f76ee892STomi Valkeinen * Here we should calculate minimum txbyteclk to be able to send the 4475f76ee892STomi Valkeinen * frame in time, and also to handle TE. That's not very simple, though, 4476f76ee892STomi Valkeinen * especially as we go to LP between each pixel packet due to HW 4477f76ee892STomi Valkeinen * "feature". So let's just estimate very roughly and multiply by 1.5. 4478f76ee892STomi Valkeinen */ 4479f76ee892STomi Valkeinen pck = cfg->timings->pixelclock; 4480f76ee892STomi Valkeinen pck = pck * 3 / 2; 4481f76ee892STomi Valkeinen txbyteclk = pck * bitspp / 8 / ndl; 4482f76ee892STomi Valkeinen 4483f76ee892STomi Valkeinen memset(ctx, 0, sizeof(*ctx)); 4484f76ee892STomi Valkeinen ctx->dsidev = dsi->pdev; 4485f76ee892STomi Valkeinen ctx->pll = &dsi->pll; 4486f76ee892STomi Valkeinen ctx->config = cfg; 4487f76ee892STomi Valkeinen ctx->req_pck_min = pck; 4488f76ee892STomi Valkeinen ctx->req_pck_nom = pck; 4489f76ee892STomi Valkeinen ctx->req_pck_max = pck * 3 / 2; 4490f76ee892STomi Valkeinen 4491f76ee892STomi Valkeinen pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4); 4492f76ee892STomi Valkeinen pll_max = cfg->hs_clk_max * 4; 4493f76ee892STomi Valkeinen 4494f76ee892STomi Valkeinen return dss_pll_calc(ctx->pll, clkin, 4495f76ee892STomi Valkeinen pll_min, pll_max, 4496f76ee892STomi Valkeinen dsi_cm_calc_pll_cb, ctx); 4497f76ee892STomi Valkeinen } 4498f76ee892STomi Valkeinen 4499f76ee892STomi Valkeinen static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx) 4500f76ee892STomi Valkeinen { 4501f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(ctx->dsidev); 4502f76ee892STomi Valkeinen const struct omap_dss_dsi_config *cfg = ctx->config; 4503f76ee892STomi Valkeinen int bitspp = dsi_get_pixel_size(cfg->pixel_format); 4504f76ee892STomi Valkeinen int ndl = dsi->num_lanes_used - 1; 4505f76ee892STomi Valkeinen unsigned long hsclk = ctx->dsi_cinfo.clkdco / 4; 4506f76ee892STomi Valkeinen unsigned long byteclk = hsclk / 4; 4507f76ee892STomi Valkeinen 4508f76ee892STomi Valkeinen unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max; 4509f76ee892STomi Valkeinen int xres; 4510f76ee892STomi Valkeinen int panel_htot, panel_hbl; /* pixels */ 4511f76ee892STomi Valkeinen int dispc_htot, dispc_hbl; /* pixels */ 4512f76ee892STomi Valkeinen int dsi_htot, dsi_hact, dsi_hbl, hss, hse; /* byteclks */ 4513f76ee892STomi Valkeinen int hfp, hsa, hbp; 4514f76ee892STomi Valkeinen const struct omap_video_timings *req_vm; 4515f76ee892STomi Valkeinen struct omap_video_timings *dispc_vm; 4516f76ee892STomi Valkeinen struct omap_dss_dsi_videomode_timings *dsi_vm; 4517f76ee892STomi Valkeinen u64 dsi_tput, dispc_tput; 4518f76ee892STomi Valkeinen 4519f76ee892STomi Valkeinen dsi_tput = (u64)byteclk * ndl * 8; 4520f76ee892STomi Valkeinen 4521f76ee892STomi Valkeinen req_vm = cfg->timings; 4522f76ee892STomi Valkeinen req_pck_min = ctx->req_pck_min; 4523f76ee892STomi Valkeinen req_pck_max = ctx->req_pck_max; 4524f76ee892STomi Valkeinen req_pck_nom = ctx->req_pck_nom; 4525f76ee892STomi Valkeinen 4526f76ee892STomi Valkeinen dispc_pck = ctx->dispc_cinfo.pck; 4527f76ee892STomi Valkeinen dispc_tput = (u64)dispc_pck * bitspp; 4528f76ee892STomi Valkeinen 4529f76ee892STomi Valkeinen xres = req_vm->x_res; 4530f76ee892STomi Valkeinen 4531f76ee892STomi Valkeinen panel_hbl = req_vm->hfp + req_vm->hbp + req_vm->hsw; 4532f76ee892STomi Valkeinen panel_htot = xres + panel_hbl; 4533f76ee892STomi Valkeinen 4534f76ee892STomi Valkeinen dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(xres * bitspp, 8) + 6, ndl); 4535f76ee892STomi Valkeinen 4536f76ee892STomi Valkeinen /* 4537f76ee892STomi Valkeinen * When there are no line buffers, DISPC and DSI must have the 4538f76ee892STomi Valkeinen * same tput. Otherwise DISPC tput needs to be higher than DSI's. 4539f76ee892STomi Valkeinen */ 4540f76ee892STomi Valkeinen if (dsi->line_buffer_size < xres * bitspp / 8) { 4541f76ee892STomi Valkeinen if (dispc_tput != dsi_tput) 4542f76ee892STomi Valkeinen return false; 4543f76ee892STomi Valkeinen } else { 4544f76ee892STomi Valkeinen if (dispc_tput < dsi_tput) 4545f76ee892STomi Valkeinen return false; 4546f76ee892STomi Valkeinen } 4547f76ee892STomi Valkeinen 4548f76ee892STomi Valkeinen /* DSI tput must be over the min requirement */ 4549f76ee892STomi Valkeinen if (dsi_tput < (u64)bitspp * req_pck_min) 4550f76ee892STomi Valkeinen return false; 4551f76ee892STomi Valkeinen 4552f76ee892STomi Valkeinen /* When non-burst mode, DSI tput must be below max requirement. */ 4553f76ee892STomi Valkeinen if (cfg->trans_mode != OMAP_DSS_DSI_BURST_MODE) { 4554f76ee892STomi Valkeinen if (dsi_tput > (u64)bitspp * req_pck_max) 4555f76ee892STomi Valkeinen return false; 4556f76ee892STomi Valkeinen } 4557f76ee892STomi Valkeinen 4558f76ee892STomi Valkeinen hss = DIV_ROUND_UP(4, ndl); 4559f76ee892STomi Valkeinen 4560f76ee892STomi Valkeinen if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) { 4561f76ee892STomi Valkeinen if (ndl == 3 && req_vm->hsw == 0) 4562f76ee892STomi Valkeinen hse = 1; 4563f76ee892STomi Valkeinen else 4564f76ee892STomi Valkeinen hse = DIV_ROUND_UP(4, ndl); 4565f76ee892STomi Valkeinen } else { 4566f76ee892STomi Valkeinen hse = 0; 4567f76ee892STomi Valkeinen } 4568f76ee892STomi Valkeinen 4569f76ee892STomi Valkeinen /* DSI htot to match the panel's nominal pck */ 4570f76ee892STomi Valkeinen dsi_htot = div64_u64((u64)panel_htot * byteclk, req_pck_nom); 4571f76ee892STomi Valkeinen 4572f76ee892STomi Valkeinen /* fail if there would be no time for blanking */ 4573f76ee892STomi Valkeinen if (dsi_htot < hss + hse + dsi_hact) 4574f76ee892STomi Valkeinen return false; 4575f76ee892STomi Valkeinen 4576f76ee892STomi Valkeinen /* total DSI blanking needed to achieve panel's TL */ 4577f76ee892STomi Valkeinen dsi_hbl = dsi_htot - dsi_hact; 4578f76ee892STomi Valkeinen 4579f76ee892STomi Valkeinen /* DISPC htot to match the DSI TL */ 4580f76ee892STomi Valkeinen dispc_htot = div64_u64((u64)dsi_htot * dispc_pck, byteclk); 4581f76ee892STomi Valkeinen 4582f76ee892STomi Valkeinen /* verify that the DSI and DISPC TLs are the same */ 4583f76ee892STomi Valkeinen if ((u64)dsi_htot * dispc_pck != (u64)dispc_htot * byteclk) 4584f76ee892STomi Valkeinen return false; 4585f76ee892STomi Valkeinen 4586f76ee892STomi Valkeinen dispc_hbl = dispc_htot - xres; 4587f76ee892STomi Valkeinen 4588f76ee892STomi Valkeinen /* setup DSI videomode */ 4589f76ee892STomi Valkeinen 4590f76ee892STomi Valkeinen dsi_vm = &ctx->dsi_vm; 4591f76ee892STomi Valkeinen memset(dsi_vm, 0, sizeof(*dsi_vm)); 4592f76ee892STomi Valkeinen 4593f76ee892STomi Valkeinen dsi_vm->hsclk = hsclk; 4594f76ee892STomi Valkeinen 4595f76ee892STomi Valkeinen dsi_vm->ndl = ndl; 4596f76ee892STomi Valkeinen dsi_vm->bitspp = bitspp; 4597f76ee892STomi Valkeinen 4598f76ee892STomi Valkeinen if (cfg->trans_mode != OMAP_DSS_DSI_PULSE_MODE) { 4599f76ee892STomi Valkeinen hsa = 0; 4600f76ee892STomi Valkeinen } else if (ndl == 3 && req_vm->hsw == 0) { 4601f76ee892STomi Valkeinen hsa = 0; 4602f76ee892STomi Valkeinen } else { 4603f76ee892STomi Valkeinen hsa = div64_u64((u64)req_vm->hsw * byteclk, req_pck_nom); 4604f76ee892STomi Valkeinen hsa = max(hsa - hse, 1); 4605f76ee892STomi Valkeinen } 4606f76ee892STomi Valkeinen 4607f76ee892STomi Valkeinen hbp = div64_u64((u64)req_vm->hbp * byteclk, req_pck_nom); 4608f76ee892STomi Valkeinen hbp = max(hbp, 1); 4609f76ee892STomi Valkeinen 4610f76ee892STomi Valkeinen hfp = dsi_hbl - (hss + hsa + hse + hbp); 4611f76ee892STomi Valkeinen if (hfp < 1) { 4612f76ee892STomi Valkeinen int t; 4613f76ee892STomi Valkeinen /* we need to take cycles from hbp */ 4614f76ee892STomi Valkeinen 4615f76ee892STomi Valkeinen t = 1 - hfp; 4616f76ee892STomi Valkeinen hbp = max(hbp - t, 1); 4617f76ee892STomi Valkeinen hfp = dsi_hbl - (hss + hsa + hse + hbp); 4618f76ee892STomi Valkeinen 4619f76ee892STomi Valkeinen if (hfp < 1 && hsa > 0) { 4620f76ee892STomi Valkeinen /* we need to take cycles from hsa */ 4621f76ee892STomi Valkeinen t = 1 - hfp; 4622f76ee892STomi Valkeinen hsa = max(hsa - t, 1); 4623f76ee892STomi Valkeinen hfp = dsi_hbl - (hss + hsa + hse + hbp); 4624f76ee892STomi Valkeinen } 4625f76ee892STomi Valkeinen } 4626f76ee892STomi Valkeinen 4627f76ee892STomi Valkeinen if (hfp < 1) 4628f76ee892STomi Valkeinen return false; 4629f76ee892STomi Valkeinen 4630f76ee892STomi Valkeinen dsi_vm->hss = hss; 4631f76ee892STomi Valkeinen dsi_vm->hsa = hsa; 4632f76ee892STomi Valkeinen dsi_vm->hse = hse; 4633f76ee892STomi Valkeinen dsi_vm->hbp = hbp; 4634f76ee892STomi Valkeinen dsi_vm->hact = xres; 4635f76ee892STomi Valkeinen dsi_vm->hfp = hfp; 4636f76ee892STomi Valkeinen 4637f76ee892STomi Valkeinen dsi_vm->vsa = req_vm->vsw; 4638f76ee892STomi Valkeinen dsi_vm->vbp = req_vm->vbp; 4639f76ee892STomi Valkeinen dsi_vm->vact = req_vm->y_res; 4640f76ee892STomi Valkeinen dsi_vm->vfp = req_vm->vfp; 4641f76ee892STomi Valkeinen 4642f76ee892STomi Valkeinen dsi_vm->trans_mode = cfg->trans_mode; 4643f76ee892STomi Valkeinen 4644f76ee892STomi Valkeinen dsi_vm->blanking_mode = 0; 4645f76ee892STomi Valkeinen dsi_vm->hsa_blanking_mode = 1; 4646f76ee892STomi Valkeinen dsi_vm->hfp_blanking_mode = 1; 4647f76ee892STomi Valkeinen dsi_vm->hbp_blanking_mode = 1; 4648f76ee892STomi Valkeinen 4649f76ee892STomi Valkeinen dsi_vm->ddr_clk_always_on = cfg->ddr_clk_always_on; 4650f76ee892STomi Valkeinen dsi_vm->window_sync = 4; 4651f76ee892STomi Valkeinen 4652f76ee892STomi Valkeinen /* setup DISPC videomode */ 4653f76ee892STomi Valkeinen 4654f76ee892STomi Valkeinen dispc_vm = &ctx->dispc_vm; 4655f76ee892STomi Valkeinen *dispc_vm = *req_vm; 4656f76ee892STomi Valkeinen dispc_vm->pixelclock = dispc_pck; 4657f76ee892STomi Valkeinen 4658f76ee892STomi Valkeinen if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) { 4659f76ee892STomi Valkeinen hsa = div64_u64((u64)req_vm->hsw * dispc_pck, 4660f76ee892STomi Valkeinen req_pck_nom); 4661f76ee892STomi Valkeinen hsa = max(hsa, 1); 4662f76ee892STomi Valkeinen } else { 4663f76ee892STomi Valkeinen hsa = 1; 4664f76ee892STomi Valkeinen } 4665f76ee892STomi Valkeinen 4666f76ee892STomi Valkeinen hbp = div64_u64((u64)req_vm->hbp * dispc_pck, req_pck_nom); 4667f76ee892STomi Valkeinen hbp = max(hbp, 1); 4668f76ee892STomi Valkeinen 4669f76ee892STomi Valkeinen hfp = dispc_hbl - hsa - hbp; 4670f76ee892STomi Valkeinen if (hfp < 1) { 4671f76ee892STomi Valkeinen int t; 4672f76ee892STomi Valkeinen /* we need to take cycles from hbp */ 4673f76ee892STomi Valkeinen 4674f76ee892STomi Valkeinen t = 1 - hfp; 4675f76ee892STomi Valkeinen hbp = max(hbp - t, 1); 4676f76ee892STomi Valkeinen hfp = dispc_hbl - hsa - hbp; 4677f76ee892STomi Valkeinen 4678f76ee892STomi Valkeinen if (hfp < 1) { 4679f76ee892STomi Valkeinen /* we need to take cycles from hsa */ 4680f76ee892STomi Valkeinen t = 1 - hfp; 4681f76ee892STomi Valkeinen hsa = max(hsa - t, 1); 4682f76ee892STomi Valkeinen hfp = dispc_hbl - hsa - hbp; 4683f76ee892STomi Valkeinen } 4684f76ee892STomi Valkeinen } 4685f76ee892STomi Valkeinen 4686f76ee892STomi Valkeinen if (hfp < 1) 4687f76ee892STomi Valkeinen return false; 4688f76ee892STomi Valkeinen 4689f76ee892STomi Valkeinen dispc_vm->hfp = hfp; 4690f76ee892STomi Valkeinen dispc_vm->hsw = hsa; 4691f76ee892STomi Valkeinen dispc_vm->hbp = hbp; 4692f76ee892STomi Valkeinen 4693f76ee892STomi Valkeinen return true; 4694f76ee892STomi Valkeinen } 4695f76ee892STomi Valkeinen 4696f76ee892STomi Valkeinen 4697f76ee892STomi Valkeinen static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck, 4698f76ee892STomi Valkeinen unsigned long pck, void *data) 4699f76ee892STomi Valkeinen { 4700f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx = data; 4701f76ee892STomi Valkeinen 4702f76ee892STomi Valkeinen ctx->dispc_cinfo.lck_div = lckd; 4703f76ee892STomi Valkeinen ctx->dispc_cinfo.pck_div = pckd; 4704f76ee892STomi Valkeinen ctx->dispc_cinfo.lck = lck; 4705f76ee892STomi Valkeinen ctx->dispc_cinfo.pck = pck; 4706f76ee892STomi Valkeinen 4707f76ee892STomi Valkeinen if (dsi_vm_calc_blanking(ctx) == false) 4708f76ee892STomi Valkeinen return false; 4709f76ee892STomi Valkeinen 4710f76ee892STomi Valkeinen #ifdef PRINT_VERBOSE_VM_TIMINGS 4711f76ee892STomi Valkeinen print_dispc_vm("dispc", &ctx->dispc_vm); 4712f76ee892STomi Valkeinen print_dsi_vm("dsi ", &ctx->dsi_vm); 4713f76ee892STomi Valkeinen print_dispc_vm("req ", ctx->config->timings); 4714f76ee892STomi Valkeinen print_dsi_dispc_vm("act ", &ctx->dsi_vm); 4715f76ee892STomi Valkeinen #endif 4716f76ee892STomi Valkeinen 4717f76ee892STomi Valkeinen return true; 4718f76ee892STomi Valkeinen } 4719f76ee892STomi Valkeinen 4720f76ee892STomi Valkeinen static bool dsi_vm_calc_hsdiv_cb(int m_dispc, unsigned long dispc, 4721f76ee892STomi Valkeinen void *data) 4722f76ee892STomi Valkeinen { 4723f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx = data; 4724f76ee892STomi Valkeinen unsigned long pck_max; 4725f76ee892STomi Valkeinen 4726f76ee892STomi Valkeinen ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc; 4727f76ee892STomi Valkeinen ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc; 4728f76ee892STomi Valkeinen 4729f76ee892STomi Valkeinen /* 4730f76ee892STomi Valkeinen * In burst mode we can let the dispc pck be arbitrarily high, but it 4731f76ee892STomi Valkeinen * limits our scaling abilities. So for now, don't aim too high. 4732f76ee892STomi Valkeinen */ 4733f76ee892STomi Valkeinen 4734f76ee892STomi Valkeinen if (ctx->config->trans_mode == OMAP_DSS_DSI_BURST_MODE) 4735f76ee892STomi Valkeinen pck_max = ctx->req_pck_max + 10000000; 4736f76ee892STomi Valkeinen else 4737f76ee892STomi Valkeinen pck_max = ctx->req_pck_max; 4738f76ee892STomi Valkeinen 4739f76ee892STomi Valkeinen return dispc_div_calc(dispc, ctx->req_pck_min, pck_max, 4740f76ee892STomi Valkeinen dsi_vm_calc_dispc_cb, ctx); 4741f76ee892STomi Valkeinen } 4742f76ee892STomi Valkeinen 4743f76ee892STomi Valkeinen static bool dsi_vm_calc_pll_cb(int n, int m, unsigned long fint, 4744f76ee892STomi Valkeinen unsigned long clkdco, void *data) 4745f76ee892STomi Valkeinen { 4746f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx = data; 4747f76ee892STomi Valkeinen 4748f76ee892STomi Valkeinen ctx->dsi_cinfo.n = n; 4749f76ee892STomi Valkeinen ctx->dsi_cinfo.m = m; 4750f76ee892STomi Valkeinen ctx->dsi_cinfo.fint = fint; 4751f76ee892STomi Valkeinen ctx->dsi_cinfo.clkdco = clkdco; 4752f76ee892STomi Valkeinen 4753f76ee892STomi Valkeinen return dss_pll_hsdiv_calc(ctx->pll, clkdco, ctx->req_pck_min, 4754f76ee892STomi Valkeinen dss_feat_get_param_max(FEAT_PARAM_DSS_FCK), 4755f76ee892STomi Valkeinen dsi_vm_calc_hsdiv_cb, ctx); 4756f76ee892STomi Valkeinen } 4757f76ee892STomi Valkeinen 4758f76ee892STomi Valkeinen static bool dsi_vm_calc(struct dsi_data *dsi, 4759f76ee892STomi Valkeinen const struct omap_dss_dsi_config *cfg, 4760f76ee892STomi Valkeinen struct dsi_clk_calc_ctx *ctx) 4761f76ee892STomi Valkeinen { 4762f76ee892STomi Valkeinen const struct omap_video_timings *t = cfg->timings; 4763f76ee892STomi Valkeinen unsigned long clkin; 4764f76ee892STomi Valkeinen unsigned long pll_min; 4765f76ee892STomi Valkeinen unsigned long pll_max; 4766f76ee892STomi Valkeinen int ndl = dsi->num_lanes_used - 1; 4767f76ee892STomi Valkeinen int bitspp = dsi_get_pixel_size(cfg->pixel_format); 4768f76ee892STomi Valkeinen unsigned long byteclk_min; 4769f76ee892STomi Valkeinen 4770f76ee892STomi Valkeinen clkin = clk_get_rate(dsi->pll.clkin); 4771f76ee892STomi Valkeinen 4772f76ee892STomi Valkeinen memset(ctx, 0, sizeof(*ctx)); 4773f76ee892STomi Valkeinen ctx->dsidev = dsi->pdev; 4774f76ee892STomi Valkeinen ctx->pll = &dsi->pll; 4775f76ee892STomi Valkeinen ctx->config = cfg; 4776f76ee892STomi Valkeinen 4777f76ee892STomi Valkeinen /* these limits should come from the panel driver */ 4778f76ee892STomi Valkeinen ctx->req_pck_min = t->pixelclock - 1000; 4779f76ee892STomi Valkeinen ctx->req_pck_nom = t->pixelclock; 4780f76ee892STomi Valkeinen ctx->req_pck_max = t->pixelclock + 1000; 4781f76ee892STomi Valkeinen 4782f76ee892STomi Valkeinen byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8); 4783f76ee892STomi Valkeinen pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4); 4784f76ee892STomi Valkeinen 4785f76ee892STomi Valkeinen if (cfg->trans_mode == OMAP_DSS_DSI_BURST_MODE) { 4786f76ee892STomi Valkeinen pll_max = cfg->hs_clk_max * 4; 4787f76ee892STomi Valkeinen } else { 4788f76ee892STomi Valkeinen unsigned long byteclk_max; 4789f76ee892STomi Valkeinen byteclk_max = div64_u64((u64)ctx->req_pck_max * bitspp, 4790f76ee892STomi Valkeinen ndl * 8); 4791f76ee892STomi Valkeinen 4792f76ee892STomi Valkeinen pll_max = byteclk_max * 4 * 4; 4793f76ee892STomi Valkeinen } 4794f76ee892STomi Valkeinen 4795f76ee892STomi Valkeinen return dss_pll_calc(ctx->pll, clkin, 4796f76ee892STomi Valkeinen pll_min, pll_max, 4797f76ee892STomi Valkeinen dsi_vm_calc_pll_cb, ctx); 4798f76ee892STomi Valkeinen } 4799f76ee892STomi Valkeinen 4800f76ee892STomi Valkeinen static int dsi_set_config(struct omap_dss_device *dssdev, 4801f76ee892STomi Valkeinen const struct omap_dss_dsi_config *config) 4802f76ee892STomi Valkeinen { 4803f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4804f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4805f76ee892STomi Valkeinen struct dsi_clk_calc_ctx ctx; 4806f76ee892STomi Valkeinen bool ok; 4807f76ee892STomi Valkeinen int r; 4808f76ee892STomi Valkeinen 4809f76ee892STomi Valkeinen mutex_lock(&dsi->lock); 4810f76ee892STomi Valkeinen 4811f76ee892STomi Valkeinen dsi->pix_fmt = config->pixel_format; 4812f76ee892STomi Valkeinen dsi->mode = config->mode; 4813f76ee892STomi Valkeinen 4814f76ee892STomi Valkeinen if (config->mode == OMAP_DSS_DSI_VIDEO_MODE) 4815f76ee892STomi Valkeinen ok = dsi_vm_calc(dsi, config, &ctx); 4816f76ee892STomi Valkeinen else 4817f76ee892STomi Valkeinen ok = dsi_cm_calc(dsi, config, &ctx); 4818f76ee892STomi Valkeinen 4819f76ee892STomi Valkeinen if (!ok) { 4820f76ee892STomi Valkeinen DSSERR("failed to find suitable DSI clock settings\n"); 4821f76ee892STomi Valkeinen r = -EINVAL; 4822f76ee892STomi Valkeinen goto err; 4823f76ee892STomi Valkeinen } 4824f76ee892STomi Valkeinen 4825f76ee892STomi Valkeinen dsi_pll_calc_dsi_fck(&ctx.dsi_cinfo); 4826f76ee892STomi Valkeinen 4827f76ee892STomi Valkeinen r = dsi_lp_clock_calc(ctx.dsi_cinfo.clkout[HSDIV_DSI], 4828f76ee892STomi Valkeinen config->lp_clk_min, config->lp_clk_max, &dsi->user_lp_cinfo); 4829f76ee892STomi Valkeinen if (r) { 4830f76ee892STomi Valkeinen DSSERR("failed to find suitable DSI LP clock settings\n"); 4831f76ee892STomi Valkeinen goto err; 4832f76ee892STomi Valkeinen } 4833f76ee892STomi Valkeinen 4834f76ee892STomi Valkeinen dsi->user_dsi_cinfo = ctx.dsi_cinfo; 4835f76ee892STomi Valkeinen dsi->user_dispc_cinfo = ctx.dispc_cinfo; 4836f76ee892STomi Valkeinen 4837f76ee892STomi Valkeinen dsi->timings = ctx.dispc_vm; 4838f76ee892STomi Valkeinen dsi->vm_timings = ctx.dsi_vm; 4839f76ee892STomi Valkeinen 4840f76ee892STomi Valkeinen mutex_unlock(&dsi->lock); 4841f76ee892STomi Valkeinen 4842f76ee892STomi Valkeinen return 0; 4843f76ee892STomi Valkeinen err: 4844f76ee892STomi Valkeinen mutex_unlock(&dsi->lock); 4845f76ee892STomi Valkeinen 4846f76ee892STomi Valkeinen return r; 4847f76ee892STomi Valkeinen } 4848f76ee892STomi Valkeinen 4849f76ee892STomi Valkeinen /* 4850f76ee892STomi Valkeinen * Return a hardcoded channel for the DSI output. This should work for 4851f76ee892STomi Valkeinen * current use cases, but this can be later expanded to either resolve 4852f76ee892STomi Valkeinen * the channel in some more dynamic manner, or get the channel as a user 4853f76ee892STomi Valkeinen * parameter. 4854f76ee892STomi Valkeinen */ 4855f76ee892STomi Valkeinen static enum omap_channel dsi_get_channel(int module_id) 4856f76ee892STomi Valkeinen { 4857f76ee892STomi Valkeinen switch (omapdss_get_version()) { 4858f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP24xx: 4859f76ee892STomi Valkeinen case OMAPDSS_VER_AM43xx: 4860f76ee892STomi Valkeinen DSSWARN("DSI not supported\n"); 4861f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD; 4862f76ee892STomi Valkeinen 4863f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP34xx_ES1: 4864f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP34xx_ES3: 4865f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP3630: 4866f76ee892STomi Valkeinen case OMAPDSS_VER_AM35xx: 4867f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD; 4868f76ee892STomi Valkeinen 4869f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP4430_ES1: 4870f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP4430_ES2: 4871f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP4: 4872f76ee892STomi Valkeinen switch (module_id) { 4873f76ee892STomi Valkeinen case 0: 4874f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD; 4875f76ee892STomi Valkeinen case 1: 4876f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD2; 4877f76ee892STomi Valkeinen default: 4878f76ee892STomi Valkeinen DSSWARN("unsupported module id\n"); 4879f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD; 4880f76ee892STomi Valkeinen } 4881f76ee892STomi Valkeinen 4882f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP5: 4883f76ee892STomi Valkeinen switch (module_id) { 4884f76ee892STomi Valkeinen case 0: 4885f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD; 4886f76ee892STomi Valkeinen case 1: 4887f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD3; 4888f76ee892STomi Valkeinen default: 4889f76ee892STomi Valkeinen DSSWARN("unsupported module id\n"); 4890f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD; 4891f76ee892STomi Valkeinen } 4892f76ee892STomi Valkeinen 4893f76ee892STomi Valkeinen default: 4894f76ee892STomi Valkeinen DSSWARN("unsupported DSS version\n"); 4895f76ee892STomi Valkeinen return OMAP_DSS_CHANNEL_LCD; 4896f76ee892STomi Valkeinen } 4897f76ee892STomi Valkeinen } 4898f76ee892STomi Valkeinen 4899f76ee892STomi Valkeinen static int dsi_request_vc(struct omap_dss_device *dssdev, int *channel) 4900f76ee892STomi Valkeinen { 4901f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4902f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4903f76ee892STomi Valkeinen int i; 4904f76ee892STomi Valkeinen 4905f76ee892STomi Valkeinen for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { 4906f76ee892STomi Valkeinen if (!dsi->vc[i].dssdev) { 4907f76ee892STomi Valkeinen dsi->vc[i].dssdev = dssdev; 4908f76ee892STomi Valkeinen *channel = i; 4909f76ee892STomi Valkeinen return 0; 4910f76ee892STomi Valkeinen } 4911f76ee892STomi Valkeinen } 4912f76ee892STomi Valkeinen 4913f76ee892STomi Valkeinen DSSERR("cannot get VC for display %s", dssdev->name); 4914f76ee892STomi Valkeinen return -ENOSPC; 4915f76ee892STomi Valkeinen } 4916f76ee892STomi Valkeinen 4917f76ee892STomi Valkeinen static int dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id) 4918f76ee892STomi Valkeinen { 4919f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4920f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4921f76ee892STomi Valkeinen 4922f76ee892STomi Valkeinen if (vc_id < 0 || vc_id > 3) { 4923f76ee892STomi Valkeinen DSSERR("VC ID out of range\n"); 4924f76ee892STomi Valkeinen return -EINVAL; 4925f76ee892STomi Valkeinen } 4926f76ee892STomi Valkeinen 4927f76ee892STomi Valkeinen if (channel < 0 || channel > 3) { 4928f76ee892STomi Valkeinen DSSERR("Virtual Channel out of range\n"); 4929f76ee892STomi Valkeinen return -EINVAL; 4930f76ee892STomi Valkeinen } 4931f76ee892STomi Valkeinen 4932f76ee892STomi Valkeinen if (dsi->vc[channel].dssdev != dssdev) { 4933f76ee892STomi Valkeinen DSSERR("Virtual Channel not allocated to display %s\n", 4934f76ee892STomi Valkeinen dssdev->name); 4935f76ee892STomi Valkeinen return -EINVAL; 4936f76ee892STomi Valkeinen } 4937f76ee892STomi Valkeinen 4938f76ee892STomi Valkeinen dsi->vc[channel].vc_id = vc_id; 4939f76ee892STomi Valkeinen 4940f76ee892STomi Valkeinen return 0; 4941f76ee892STomi Valkeinen } 4942f76ee892STomi Valkeinen 4943f76ee892STomi Valkeinen static void dsi_release_vc(struct omap_dss_device *dssdev, int channel) 4944f76ee892STomi Valkeinen { 4945f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4946f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4947f76ee892STomi Valkeinen 4948f76ee892STomi Valkeinen if ((channel >= 0 && channel <= 3) && 4949f76ee892STomi Valkeinen dsi->vc[channel].dssdev == dssdev) { 4950f76ee892STomi Valkeinen dsi->vc[channel].dssdev = NULL; 4951f76ee892STomi Valkeinen dsi->vc[channel].vc_id = 0; 4952f76ee892STomi Valkeinen } 4953f76ee892STomi Valkeinen } 4954f76ee892STomi Valkeinen 4955f76ee892STomi Valkeinen 4956f76ee892STomi Valkeinen static int dsi_get_clocks(struct platform_device *dsidev) 4957f76ee892STomi Valkeinen { 4958f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 4959f76ee892STomi Valkeinen struct clk *clk; 4960f76ee892STomi Valkeinen 4961f76ee892STomi Valkeinen clk = devm_clk_get(&dsidev->dev, "fck"); 4962f76ee892STomi Valkeinen if (IS_ERR(clk)) { 4963f76ee892STomi Valkeinen DSSERR("can't get fck\n"); 4964f76ee892STomi Valkeinen return PTR_ERR(clk); 4965f76ee892STomi Valkeinen } 4966f76ee892STomi Valkeinen 4967f76ee892STomi Valkeinen dsi->dss_clk = clk; 4968f76ee892STomi Valkeinen 4969f76ee892STomi Valkeinen return 0; 4970f76ee892STomi Valkeinen } 4971f76ee892STomi Valkeinen 4972f76ee892STomi Valkeinen static int dsi_connect(struct omap_dss_device *dssdev, 4973f76ee892STomi Valkeinen struct omap_dss_device *dst) 4974f76ee892STomi Valkeinen { 4975f76ee892STomi Valkeinen struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); 4976f76ee892STomi Valkeinen struct omap_overlay_manager *mgr; 4977f76ee892STomi Valkeinen int r; 4978f76ee892STomi Valkeinen 4979f76ee892STomi Valkeinen r = dsi_regulator_init(dsidev); 4980f76ee892STomi Valkeinen if (r) 4981f76ee892STomi Valkeinen return r; 4982f76ee892STomi Valkeinen 4983f76ee892STomi Valkeinen mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); 4984f76ee892STomi Valkeinen if (!mgr) 4985f76ee892STomi Valkeinen return -ENODEV; 4986f76ee892STomi Valkeinen 4987f76ee892STomi Valkeinen r = dss_mgr_connect(mgr, dssdev); 4988f76ee892STomi Valkeinen if (r) 4989f76ee892STomi Valkeinen return r; 4990f76ee892STomi Valkeinen 4991f76ee892STomi Valkeinen r = omapdss_output_set_device(dssdev, dst); 4992f76ee892STomi Valkeinen if (r) { 4993f76ee892STomi Valkeinen DSSERR("failed to connect output to new device: %s\n", 4994f76ee892STomi Valkeinen dssdev->name); 4995f76ee892STomi Valkeinen dss_mgr_disconnect(mgr, dssdev); 4996f76ee892STomi Valkeinen return r; 4997f76ee892STomi Valkeinen } 4998f76ee892STomi Valkeinen 4999f76ee892STomi Valkeinen return 0; 5000f76ee892STomi Valkeinen } 5001f76ee892STomi Valkeinen 5002f76ee892STomi Valkeinen static void dsi_disconnect(struct omap_dss_device *dssdev, 5003f76ee892STomi Valkeinen struct omap_dss_device *dst) 5004f76ee892STomi Valkeinen { 5005f76ee892STomi Valkeinen WARN_ON(dst != dssdev->dst); 5006f76ee892STomi Valkeinen 5007f76ee892STomi Valkeinen if (dst != dssdev->dst) 5008f76ee892STomi Valkeinen return; 5009f76ee892STomi Valkeinen 5010f76ee892STomi Valkeinen omapdss_output_unset_device(dssdev); 5011f76ee892STomi Valkeinen 5012f76ee892STomi Valkeinen if (dssdev->manager) 5013f76ee892STomi Valkeinen dss_mgr_disconnect(dssdev->manager, dssdev); 5014f76ee892STomi Valkeinen } 5015f76ee892STomi Valkeinen 5016f76ee892STomi Valkeinen static const struct omapdss_dsi_ops dsi_ops = { 5017f76ee892STomi Valkeinen .connect = dsi_connect, 5018f76ee892STomi Valkeinen .disconnect = dsi_disconnect, 5019f76ee892STomi Valkeinen 5020f76ee892STomi Valkeinen .bus_lock = dsi_bus_lock, 5021f76ee892STomi Valkeinen .bus_unlock = dsi_bus_unlock, 5022f76ee892STomi Valkeinen 5023f76ee892STomi Valkeinen .enable = dsi_display_enable, 5024f76ee892STomi Valkeinen .disable = dsi_display_disable, 5025f76ee892STomi Valkeinen 5026f76ee892STomi Valkeinen .enable_hs = dsi_vc_enable_hs, 5027f76ee892STomi Valkeinen 5028f76ee892STomi Valkeinen .configure_pins = dsi_configure_pins, 5029f76ee892STomi Valkeinen .set_config = dsi_set_config, 5030f76ee892STomi Valkeinen 5031f76ee892STomi Valkeinen .enable_video_output = dsi_enable_video_output, 5032f76ee892STomi Valkeinen .disable_video_output = dsi_disable_video_output, 5033f76ee892STomi Valkeinen 5034f76ee892STomi Valkeinen .update = dsi_update, 5035f76ee892STomi Valkeinen 5036f76ee892STomi Valkeinen .enable_te = dsi_enable_te, 5037f76ee892STomi Valkeinen 5038f76ee892STomi Valkeinen .request_vc = dsi_request_vc, 5039f76ee892STomi Valkeinen .set_vc_id = dsi_set_vc_id, 5040f76ee892STomi Valkeinen .release_vc = dsi_release_vc, 5041f76ee892STomi Valkeinen 5042f76ee892STomi Valkeinen .dcs_write = dsi_vc_dcs_write, 5043f76ee892STomi Valkeinen .dcs_write_nosync = dsi_vc_dcs_write_nosync, 5044f76ee892STomi Valkeinen .dcs_read = dsi_vc_dcs_read, 5045f76ee892STomi Valkeinen 5046f76ee892STomi Valkeinen .gen_write = dsi_vc_generic_write, 5047f76ee892STomi Valkeinen .gen_write_nosync = dsi_vc_generic_write_nosync, 5048f76ee892STomi Valkeinen .gen_read = dsi_vc_generic_read, 5049f76ee892STomi Valkeinen 5050f76ee892STomi Valkeinen .bta_sync = dsi_vc_send_bta_sync, 5051f76ee892STomi Valkeinen 5052f76ee892STomi Valkeinen .set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size, 5053f76ee892STomi Valkeinen }; 5054f76ee892STomi Valkeinen 5055f76ee892STomi Valkeinen static void dsi_init_output(struct platform_device *dsidev) 5056f76ee892STomi Valkeinen { 5057f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 5058f76ee892STomi Valkeinen struct omap_dss_device *out = &dsi->output; 5059f76ee892STomi Valkeinen 5060f76ee892STomi Valkeinen out->dev = &dsidev->dev; 5061f76ee892STomi Valkeinen out->id = dsi->module_id == 0 ? 5062f76ee892STomi Valkeinen OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2; 5063f76ee892STomi Valkeinen 5064f76ee892STomi Valkeinen out->output_type = OMAP_DISPLAY_TYPE_DSI; 5065f76ee892STomi Valkeinen out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1"; 5066f76ee892STomi Valkeinen out->dispc_channel = dsi_get_channel(dsi->module_id); 5067f76ee892STomi Valkeinen out->ops.dsi = &dsi_ops; 5068f76ee892STomi Valkeinen out->owner = THIS_MODULE; 5069f76ee892STomi Valkeinen 5070f76ee892STomi Valkeinen omapdss_register_output(out); 5071f76ee892STomi Valkeinen } 5072f76ee892STomi Valkeinen 5073f76ee892STomi Valkeinen static void dsi_uninit_output(struct platform_device *dsidev) 5074f76ee892STomi Valkeinen { 5075f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 5076f76ee892STomi Valkeinen struct omap_dss_device *out = &dsi->output; 5077f76ee892STomi Valkeinen 5078f76ee892STomi Valkeinen omapdss_unregister_output(out); 5079f76ee892STomi Valkeinen } 5080f76ee892STomi Valkeinen 5081f76ee892STomi Valkeinen static int dsi_probe_of(struct platform_device *pdev) 5082f76ee892STomi Valkeinen { 5083f76ee892STomi Valkeinen struct device_node *node = pdev->dev.of_node; 5084f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); 5085f76ee892STomi Valkeinen struct property *prop; 5086f76ee892STomi Valkeinen u32 lane_arr[10]; 5087f76ee892STomi Valkeinen int len, num_pins; 5088f76ee892STomi Valkeinen int r, i; 5089f76ee892STomi Valkeinen struct device_node *ep; 5090f76ee892STomi Valkeinen struct omap_dsi_pin_config pin_cfg; 5091f76ee892STomi Valkeinen 5092f76ee892STomi Valkeinen ep = omapdss_of_get_first_endpoint(node); 5093f76ee892STomi Valkeinen if (!ep) 5094f76ee892STomi Valkeinen return 0; 5095f76ee892STomi Valkeinen 5096f76ee892STomi Valkeinen prop = of_find_property(ep, "lanes", &len); 5097f76ee892STomi Valkeinen if (prop == NULL) { 5098f76ee892STomi Valkeinen dev_err(&pdev->dev, "failed to find lane data\n"); 5099f76ee892STomi Valkeinen r = -EINVAL; 5100f76ee892STomi Valkeinen goto err; 5101f76ee892STomi Valkeinen } 5102f76ee892STomi Valkeinen 5103f76ee892STomi Valkeinen num_pins = len / sizeof(u32); 5104f76ee892STomi Valkeinen 5105f76ee892STomi Valkeinen if (num_pins < 4 || num_pins % 2 != 0 || 5106f76ee892STomi Valkeinen num_pins > dsi->num_lanes_supported * 2) { 5107f76ee892STomi Valkeinen dev_err(&pdev->dev, "bad number of lanes\n"); 5108f76ee892STomi Valkeinen r = -EINVAL; 5109f76ee892STomi Valkeinen goto err; 5110f76ee892STomi Valkeinen } 5111f76ee892STomi Valkeinen 5112f76ee892STomi Valkeinen r = of_property_read_u32_array(ep, "lanes", lane_arr, num_pins); 5113f76ee892STomi Valkeinen if (r) { 5114f76ee892STomi Valkeinen dev_err(&pdev->dev, "failed to read lane data\n"); 5115f76ee892STomi Valkeinen goto err; 5116f76ee892STomi Valkeinen } 5117f76ee892STomi Valkeinen 5118f76ee892STomi Valkeinen pin_cfg.num_pins = num_pins; 5119f76ee892STomi Valkeinen for (i = 0; i < num_pins; ++i) 5120f76ee892STomi Valkeinen pin_cfg.pins[i] = (int)lane_arr[i]; 5121f76ee892STomi Valkeinen 5122f76ee892STomi Valkeinen r = dsi_configure_pins(&dsi->output, &pin_cfg); 5123f76ee892STomi Valkeinen if (r) { 5124f76ee892STomi Valkeinen dev_err(&pdev->dev, "failed to configure pins"); 5125f76ee892STomi Valkeinen goto err; 5126f76ee892STomi Valkeinen } 5127f76ee892STomi Valkeinen 5128f76ee892STomi Valkeinen of_node_put(ep); 5129f76ee892STomi Valkeinen 5130f76ee892STomi Valkeinen return 0; 5131f76ee892STomi Valkeinen 5132f76ee892STomi Valkeinen err: 5133f76ee892STomi Valkeinen of_node_put(ep); 5134f76ee892STomi Valkeinen return r; 5135f76ee892STomi Valkeinen } 5136f76ee892STomi Valkeinen 5137f76ee892STomi Valkeinen static const struct dss_pll_ops dsi_pll_ops = { 5138f76ee892STomi Valkeinen .enable = dsi_pll_enable, 5139f76ee892STomi Valkeinen .disable = dsi_pll_disable, 5140f76ee892STomi Valkeinen .set_config = dss_pll_write_config_type_a, 5141f76ee892STomi Valkeinen }; 5142f76ee892STomi Valkeinen 5143f76ee892STomi Valkeinen static const struct dss_pll_hw dss_omap3_dsi_pll_hw = { 5144f76ee892STomi Valkeinen .n_max = (1 << 7) - 1, 5145f76ee892STomi Valkeinen .m_max = (1 << 11) - 1, 5146f76ee892STomi Valkeinen .mX_max = (1 << 4) - 1, 5147f76ee892STomi Valkeinen .fint_min = 750000, 5148f76ee892STomi Valkeinen .fint_max = 2100000, 5149f76ee892STomi Valkeinen .clkdco_low = 1000000000, 5150f76ee892STomi Valkeinen .clkdco_max = 1800000000, 5151f76ee892STomi Valkeinen 5152f76ee892STomi Valkeinen .n_msb = 7, 5153f76ee892STomi Valkeinen .n_lsb = 1, 5154f76ee892STomi Valkeinen .m_msb = 18, 5155f76ee892STomi Valkeinen .m_lsb = 8, 5156f76ee892STomi Valkeinen 5157f76ee892STomi Valkeinen .mX_msb[0] = 22, 5158f76ee892STomi Valkeinen .mX_lsb[0] = 19, 5159f76ee892STomi Valkeinen .mX_msb[1] = 26, 5160f76ee892STomi Valkeinen .mX_lsb[1] = 23, 5161f76ee892STomi Valkeinen 5162f76ee892STomi Valkeinen .has_stopmode = true, 5163f76ee892STomi Valkeinen .has_freqsel = true, 5164f76ee892STomi Valkeinen .has_selfreqdco = false, 5165f76ee892STomi Valkeinen .has_refsel = false, 5166f76ee892STomi Valkeinen }; 5167f76ee892STomi Valkeinen 5168f76ee892STomi Valkeinen static const struct dss_pll_hw dss_omap4_dsi_pll_hw = { 5169f76ee892STomi Valkeinen .n_max = (1 << 8) - 1, 5170f76ee892STomi Valkeinen .m_max = (1 << 12) - 1, 5171f76ee892STomi Valkeinen .mX_max = (1 << 5) - 1, 5172f76ee892STomi Valkeinen .fint_min = 500000, 5173f76ee892STomi Valkeinen .fint_max = 2500000, 5174f76ee892STomi Valkeinen .clkdco_low = 1000000000, 5175f76ee892STomi Valkeinen .clkdco_max = 1800000000, 5176f76ee892STomi Valkeinen 5177f76ee892STomi Valkeinen .n_msb = 8, 5178f76ee892STomi Valkeinen .n_lsb = 1, 5179f76ee892STomi Valkeinen .m_msb = 20, 5180f76ee892STomi Valkeinen .m_lsb = 9, 5181f76ee892STomi Valkeinen 5182f76ee892STomi Valkeinen .mX_msb[0] = 25, 5183f76ee892STomi Valkeinen .mX_lsb[0] = 21, 5184f76ee892STomi Valkeinen .mX_msb[1] = 30, 5185f76ee892STomi Valkeinen .mX_lsb[1] = 26, 5186f76ee892STomi Valkeinen 5187f76ee892STomi Valkeinen .has_stopmode = true, 5188f76ee892STomi Valkeinen .has_freqsel = false, 5189f76ee892STomi Valkeinen .has_selfreqdco = false, 5190f76ee892STomi Valkeinen .has_refsel = false, 5191f76ee892STomi Valkeinen }; 5192f76ee892STomi Valkeinen 5193f76ee892STomi Valkeinen static const struct dss_pll_hw dss_omap5_dsi_pll_hw = { 5194f76ee892STomi Valkeinen .n_max = (1 << 8) - 1, 5195f76ee892STomi Valkeinen .m_max = (1 << 12) - 1, 5196f76ee892STomi Valkeinen .mX_max = (1 << 5) - 1, 5197f76ee892STomi Valkeinen .fint_min = 150000, 5198f76ee892STomi Valkeinen .fint_max = 52000000, 5199f76ee892STomi Valkeinen .clkdco_low = 1000000000, 5200f76ee892STomi Valkeinen .clkdco_max = 1800000000, 5201f76ee892STomi Valkeinen 5202f76ee892STomi Valkeinen .n_msb = 8, 5203f76ee892STomi Valkeinen .n_lsb = 1, 5204f76ee892STomi Valkeinen .m_msb = 20, 5205f76ee892STomi Valkeinen .m_lsb = 9, 5206f76ee892STomi Valkeinen 5207f76ee892STomi Valkeinen .mX_msb[0] = 25, 5208f76ee892STomi Valkeinen .mX_lsb[0] = 21, 5209f76ee892STomi Valkeinen .mX_msb[1] = 30, 5210f76ee892STomi Valkeinen .mX_lsb[1] = 26, 5211f76ee892STomi Valkeinen 5212f76ee892STomi Valkeinen .has_stopmode = true, 5213f76ee892STomi Valkeinen .has_freqsel = false, 5214f76ee892STomi Valkeinen .has_selfreqdco = true, 5215f76ee892STomi Valkeinen .has_refsel = true, 5216f76ee892STomi Valkeinen }; 5217f76ee892STomi Valkeinen 5218f76ee892STomi Valkeinen static int dsi_init_pll_data(struct platform_device *dsidev) 5219f76ee892STomi Valkeinen { 5220f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 5221f76ee892STomi Valkeinen struct dss_pll *pll = &dsi->pll; 5222f76ee892STomi Valkeinen struct clk *clk; 5223f76ee892STomi Valkeinen int r; 5224f76ee892STomi Valkeinen 5225f76ee892STomi Valkeinen clk = devm_clk_get(&dsidev->dev, "sys_clk"); 5226f76ee892STomi Valkeinen if (IS_ERR(clk)) { 5227f76ee892STomi Valkeinen DSSERR("can't get sys_clk\n"); 5228f76ee892STomi Valkeinen return PTR_ERR(clk); 5229f76ee892STomi Valkeinen } 5230f76ee892STomi Valkeinen 5231f76ee892STomi Valkeinen pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1"; 5232f76ee892STomi Valkeinen pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2; 5233f76ee892STomi Valkeinen pll->clkin = clk; 5234f76ee892STomi Valkeinen pll->base = dsi->pll_base; 5235f76ee892STomi Valkeinen 5236f76ee892STomi Valkeinen switch (omapdss_get_version()) { 5237f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP34xx_ES1: 5238f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP34xx_ES3: 5239f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP3630: 5240f76ee892STomi Valkeinen case OMAPDSS_VER_AM35xx: 5241f76ee892STomi Valkeinen pll->hw = &dss_omap3_dsi_pll_hw; 5242f76ee892STomi Valkeinen break; 5243f76ee892STomi Valkeinen 5244f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP4430_ES1: 5245f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP4430_ES2: 5246f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP4: 5247f76ee892STomi Valkeinen pll->hw = &dss_omap4_dsi_pll_hw; 5248f76ee892STomi Valkeinen break; 5249f76ee892STomi Valkeinen 5250f76ee892STomi Valkeinen case OMAPDSS_VER_OMAP5: 5251f76ee892STomi Valkeinen pll->hw = &dss_omap5_dsi_pll_hw; 5252f76ee892STomi Valkeinen break; 5253f76ee892STomi Valkeinen 5254f76ee892STomi Valkeinen default: 5255f76ee892STomi Valkeinen return -ENODEV; 5256f76ee892STomi Valkeinen } 5257f76ee892STomi Valkeinen 5258f76ee892STomi Valkeinen pll->ops = &dsi_pll_ops; 5259f76ee892STomi Valkeinen 5260f76ee892STomi Valkeinen r = dss_pll_register(pll); 5261f76ee892STomi Valkeinen if (r) 5262f76ee892STomi Valkeinen return r; 5263f76ee892STomi Valkeinen 5264f76ee892STomi Valkeinen return 0; 5265f76ee892STomi Valkeinen } 5266f76ee892STomi Valkeinen 5267f76ee892STomi Valkeinen /* DSI1 HW IP initialisation */ 5268f76ee892STomi Valkeinen static int dsi_bind(struct device *dev, struct device *master, void *data) 5269f76ee892STomi Valkeinen { 5270f76ee892STomi Valkeinen struct platform_device *dsidev = to_platform_device(dev); 5271f76ee892STomi Valkeinen u32 rev; 5272f76ee892STomi Valkeinen int r, i; 5273f76ee892STomi Valkeinen struct dsi_data *dsi; 5274f76ee892STomi Valkeinen struct resource *dsi_mem; 5275f76ee892STomi Valkeinen struct resource *res; 5276f76ee892STomi Valkeinen struct resource temp_res; 5277f76ee892STomi Valkeinen 5278f76ee892STomi Valkeinen dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL); 5279f76ee892STomi Valkeinen if (!dsi) 5280f76ee892STomi Valkeinen return -ENOMEM; 5281f76ee892STomi Valkeinen 5282f76ee892STomi Valkeinen dsi->pdev = dsidev; 5283f76ee892STomi Valkeinen dev_set_drvdata(&dsidev->dev, dsi); 5284f76ee892STomi Valkeinen 5285f76ee892STomi Valkeinen spin_lock_init(&dsi->irq_lock); 5286f76ee892STomi Valkeinen spin_lock_init(&dsi->errors_lock); 5287f76ee892STomi Valkeinen dsi->errors = 0; 5288f76ee892STomi Valkeinen 528935b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 5290f76ee892STomi Valkeinen spin_lock_init(&dsi->irq_stats_lock); 5291f76ee892STomi Valkeinen dsi->irq_stats.last_reset = jiffies; 5292f76ee892STomi Valkeinen #endif 5293f76ee892STomi Valkeinen 5294f76ee892STomi Valkeinen mutex_init(&dsi->lock); 5295f76ee892STomi Valkeinen sema_init(&dsi->bus_lock, 1); 5296f76ee892STomi Valkeinen 5297f76ee892STomi Valkeinen INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work, 5298f76ee892STomi Valkeinen dsi_framedone_timeout_work_callback); 5299f76ee892STomi Valkeinen 5300f76ee892STomi Valkeinen #ifdef DSI_CATCH_MISSING_TE 5301*6c789357SKees Cook timer_setup(&dsi->te_timer, dsi_te_timeout, 0); 5302f76ee892STomi Valkeinen #endif 5303f76ee892STomi Valkeinen 5304f76ee892STomi Valkeinen res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto"); 5305f76ee892STomi Valkeinen if (!res) { 5306f76ee892STomi Valkeinen res = platform_get_resource(dsidev, IORESOURCE_MEM, 0); 5307f76ee892STomi Valkeinen if (!res) { 5308f76ee892STomi Valkeinen DSSERR("can't get IORESOURCE_MEM DSI\n"); 5309f76ee892STomi Valkeinen return -EINVAL; 5310f76ee892STomi Valkeinen } 5311f76ee892STomi Valkeinen 5312f76ee892STomi Valkeinen temp_res.start = res->start; 5313f76ee892STomi Valkeinen temp_res.end = temp_res.start + DSI_PROTO_SZ - 1; 5314f76ee892STomi Valkeinen res = &temp_res; 5315f76ee892STomi Valkeinen } 5316f76ee892STomi Valkeinen 5317f76ee892STomi Valkeinen dsi_mem = res; 5318f76ee892STomi Valkeinen 5319f76ee892STomi Valkeinen dsi->proto_base = devm_ioremap(&dsidev->dev, res->start, 5320f76ee892STomi Valkeinen resource_size(res)); 5321f76ee892STomi Valkeinen if (!dsi->proto_base) { 5322f76ee892STomi Valkeinen DSSERR("can't ioremap DSI protocol engine\n"); 5323f76ee892STomi Valkeinen return -ENOMEM; 5324f76ee892STomi Valkeinen } 5325f76ee892STomi Valkeinen 5326f76ee892STomi Valkeinen res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "phy"); 5327f76ee892STomi Valkeinen if (!res) { 5328f76ee892STomi Valkeinen res = platform_get_resource(dsidev, IORESOURCE_MEM, 0); 5329f76ee892STomi Valkeinen if (!res) { 5330f76ee892STomi Valkeinen DSSERR("can't get IORESOURCE_MEM DSI\n"); 5331f76ee892STomi Valkeinen return -EINVAL; 5332f76ee892STomi Valkeinen } 5333f76ee892STomi Valkeinen 5334f76ee892STomi Valkeinen temp_res.start = res->start + DSI_PHY_OFFSET; 5335f76ee892STomi Valkeinen temp_res.end = temp_res.start + DSI_PHY_SZ - 1; 5336f76ee892STomi Valkeinen res = &temp_res; 5337f76ee892STomi Valkeinen } 5338f76ee892STomi Valkeinen 5339f76ee892STomi Valkeinen dsi->phy_base = devm_ioremap(&dsidev->dev, res->start, 5340f76ee892STomi Valkeinen resource_size(res)); 534143da7575SWei Yongjun if (!dsi->phy_base) { 5342f76ee892STomi Valkeinen DSSERR("can't ioremap DSI PHY\n"); 5343f76ee892STomi Valkeinen return -ENOMEM; 5344f76ee892STomi Valkeinen } 5345f76ee892STomi Valkeinen 5346f76ee892STomi Valkeinen res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "pll"); 5347f76ee892STomi Valkeinen if (!res) { 5348f76ee892STomi Valkeinen res = platform_get_resource(dsidev, IORESOURCE_MEM, 0); 5349f76ee892STomi Valkeinen if (!res) { 5350f76ee892STomi Valkeinen DSSERR("can't get IORESOURCE_MEM DSI\n"); 5351f76ee892STomi Valkeinen return -EINVAL; 5352f76ee892STomi Valkeinen } 5353f76ee892STomi Valkeinen 5354f76ee892STomi Valkeinen temp_res.start = res->start + DSI_PLL_OFFSET; 5355f76ee892STomi Valkeinen temp_res.end = temp_res.start + DSI_PLL_SZ - 1; 5356f76ee892STomi Valkeinen res = &temp_res; 5357f76ee892STomi Valkeinen } 5358f76ee892STomi Valkeinen 5359f76ee892STomi Valkeinen dsi->pll_base = devm_ioremap(&dsidev->dev, res->start, 5360f76ee892STomi Valkeinen resource_size(res)); 536143da7575SWei Yongjun if (!dsi->pll_base) { 5362f76ee892STomi Valkeinen DSSERR("can't ioremap DSI PLL\n"); 5363f76ee892STomi Valkeinen return -ENOMEM; 5364f76ee892STomi Valkeinen } 5365f76ee892STomi Valkeinen 5366f76ee892STomi Valkeinen dsi->irq = platform_get_irq(dsi->pdev, 0); 5367f76ee892STomi Valkeinen if (dsi->irq < 0) { 5368f76ee892STomi Valkeinen DSSERR("platform_get_irq failed\n"); 5369f76ee892STomi Valkeinen return -ENODEV; 5370f76ee892STomi Valkeinen } 5371f76ee892STomi Valkeinen 5372f76ee892STomi Valkeinen r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler, 5373f76ee892STomi Valkeinen IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev); 5374f76ee892STomi Valkeinen if (r < 0) { 5375f76ee892STomi Valkeinen DSSERR("request_irq failed\n"); 5376f76ee892STomi Valkeinen return r; 5377f76ee892STomi Valkeinen } 5378f76ee892STomi Valkeinen 5379f76ee892STomi Valkeinen if (dsidev->dev.of_node) { 5380f76ee892STomi Valkeinen const struct of_device_id *match; 5381f76ee892STomi Valkeinen const struct dsi_module_id_data *d; 5382f76ee892STomi Valkeinen 5383f76ee892STomi Valkeinen match = of_match_node(dsi_of_match, dsidev->dev.of_node); 5384f76ee892STomi Valkeinen if (!match) { 5385f76ee892STomi Valkeinen DSSERR("unsupported DSI module\n"); 5386f76ee892STomi Valkeinen return -ENODEV; 5387f76ee892STomi Valkeinen } 5388f76ee892STomi Valkeinen 5389f76ee892STomi Valkeinen d = match->data; 5390f76ee892STomi Valkeinen 5391f76ee892STomi Valkeinen while (d->address != 0 && d->address != dsi_mem->start) 5392f76ee892STomi Valkeinen d++; 5393f76ee892STomi Valkeinen 5394f76ee892STomi Valkeinen if (d->address == 0) { 5395f76ee892STomi Valkeinen DSSERR("unsupported DSI module\n"); 5396f76ee892STomi Valkeinen return -ENODEV; 5397f76ee892STomi Valkeinen } 5398f76ee892STomi Valkeinen 5399f76ee892STomi Valkeinen dsi->module_id = d->id; 5400f76ee892STomi Valkeinen } else { 5401f76ee892STomi Valkeinen dsi->module_id = dsidev->id; 5402f76ee892STomi Valkeinen } 5403f76ee892STomi Valkeinen 5404f76ee892STomi Valkeinen /* DSI VCs initialization */ 5405f76ee892STomi Valkeinen for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { 5406f76ee892STomi Valkeinen dsi->vc[i].source = DSI_VC_SOURCE_L4; 5407f76ee892STomi Valkeinen dsi->vc[i].dssdev = NULL; 5408f76ee892STomi Valkeinen dsi->vc[i].vc_id = 0; 5409f76ee892STomi Valkeinen } 5410f76ee892STomi Valkeinen 5411f76ee892STomi Valkeinen r = dsi_get_clocks(dsidev); 5412f76ee892STomi Valkeinen if (r) 5413f76ee892STomi Valkeinen return r; 5414f76ee892STomi Valkeinen 5415f76ee892STomi Valkeinen dsi_init_pll_data(dsidev); 5416f76ee892STomi Valkeinen 5417f76ee892STomi Valkeinen pm_runtime_enable(&dsidev->dev); 5418f76ee892STomi Valkeinen 5419f76ee892STomi Valkeinen r = dsi_runtime_get(dsidev); 5420f76ee892STomi Valkeinen if (r) 5421f76ee892STomi Valkeinen goto err_runtime_get; 5422f76ee892STomi Valkeinen 5423f76ee892STomi Valkeinen rev = dsi_read_reg(dsidev, DSI_REVISION); 5424f76ee892STomi Valkeinen dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n", 5425f76ee892STomi Valkeinen FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); 5426f76ee892STomi Valkeinen 5427f76ee892STomi Valkeinen /* DSI on OMAP3 doesn't have register DSI_GNQ, set number 5428f76ee892STomi Valkeinen * of data to 3 by default */ 5429f76ee892STomi Valkeinen if (dss_has_feature(FEAT_DSI_GNQ)) 5430f76ee892STomi Valkeinen /* NB_DATA_LANES */ 5431f76ee892STomi Valkeinen dsi->num_lanes_supported = 1 + REG_GET(dsidev, DSI_GNQ, 11, 9); 5432f76ee892STomi Valkeinen else 5433f76ee892STomi Valkeinen dsi->num_lanes_supported = 3; 5434f76ee892STomi Valkeinen 5435f76ee892STomi Valkeinen dsi->line_buffer_size = dsi_get_line_buf_size(dsidev); 5436f76ee892STomi Valkeinen 5437f76ee892STomi Valkeinen dsi_init_output(dsidev); 5438f76ee892STomi Valkeinen 5439f76ee892STomi Valkeinen if (dsidev->dev.of_node) { 5440f76ee892STomi Valkeinen r = dsi_probe_of(dsidev); 5441f76ee892STomi Valkeinen if (r) { 5442f76ee892STomi Valkeinen DSSERR("Invalid DSI DT data\n"); 5443f76ee892STomi Valkeinen goto err_probe_of; 5444f76ee892STomi Valkeinen } 5445f76ee892STomi Valkeinen 5446f76ee892STomi Valkeinen r = of_platform_populate(dsidev->dev.of_node, NULL, NULL, 5447f76ee892STomi Valkeinen &dsidev->dev); 5448f76ee892STomi Valkeinen if (r) 5449f76ee892STomi Valkeinen DSSERR("Failed to populate DSI child devices: %d\n", r); 5450f76ee892STomi Valkeinen } 5451f76ee892STomi Valkeinen 5452f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 5453f76ee892STomi Valkeinen 5454f76ee892STomi Valkeinen if (dsi->module_id == 0) 5455f76ee892STomi Valkeinen dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs); 5456f76ee892STomi Valkeinen else if (dsi->module_id == 1) 5457f76ee892STomi Valkeinen dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs); 5458f76ee892STomi Valkeinen 545935b522cfSTomi Valkeinen #ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS 5460f76ee892STomi Valkeinen if (dsi->module_id == 0) 5461f76ee892STomi Valkeinen dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs); 5462f76ee892STomi Valkeinen else if (dsi->module_id == 1) 5463f76ee892STomi Valkeinen dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs); 5464f76ee892STomi Valkeinen #endif 5465f76ee892STomi Valkeinen 5466f76ee892STomi Valkeinen return 0; 5467f76ee892STomi Valkeinen 5468f76ee892STomi Valkeinen err_probe_of: 5469f76ee892STomi Valkeinen dsi_uninit_output(dsidev); 5470f76ee892STomi Valkeinen dsi_runtime_put(dsidev); 5471f76ee892STomi Valkeinen 5472f76ee892STomi Valkeinen err_runtime_get: 5473f76ee892STomi Valkeinen pm_runtime_disable(&dsidev->dev); 5474f76ee892STomi Valkeinen return r; 5475f76ee892STomi Valkeinen } 5476f76ee892STomi Valkeinen 5477f76ee892STomi Valkeinen static void dsi_unbind(struct device *dev, struct device *master, void *data) 5478f76ee892STomi Valkeinen { 5479f76ee892STomi Valkeinen struct platform_device *dsidev = to_platform_device(dev); 5480f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); 5481f76ee892STomi Valkeinen 5482f76ee892STomi Valkeinen of_platform_depopulate(&dsidev->dev); 5483f76ee892STomi Valkeinen 5484f76ee892STomi Valkeinen WARN_ON(dsi->scp_clk_refcount > 0); 5485f76ee892STomi Valkeinen 5486f76ee892STomi Valkeinen dss_pll_unregister(&dsi->pll); 5487f76ee892STomi Valkeinen 5488f76ee892STomi Valkeinen dsi_uninit_output(dsidev); 5489f76ee892STomi Valkeinen 5490f76ee892STomi Valkeinen pm_runtime_disable(&dsidev->dev); 5491f76ee892STomi Valkeinen 5492f76ee892STomi Valkeinen if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) { 5493f76ee892STomi Valkeinen regulator_disable(dsi->vdds_dsi_reg); 5494f76ee892STomi Valkeinen dsi->vdds_dsi_enabled = false; 5495f76ee892STomi Valkeinen } 5496f76ee892STomi Valkeinen } 5497f76ee892STomi Valkeinen 5498f76ee892STomi Valkeinen static const struct component_ops dsi_component_ops = { 5499f76ee892STomi Valkeinen .bind = dsi_bind, 5500f76ee892STomi Valkeinen .unbind = dsi_unbind, 5501f76ee892STomi Valkeinen }; 5502f76ee892STomi Valkeinen 5503f76ee892STomi Valkeinen static int dsi_probe(struct platform_device *pdev) 5504f76ee892STomi Valkeinen { 5505f76ee892STomi Valkeinen return component_add(&pdev->dev, &dsi_component_ops); 5506f76ee892STomi Valkeinen } 5507f76ee892STomi Valkeinen 5508f76ee892STomi Valkeinen static int dsi_remove(struct platform_device *pdev) 5509f76ee892STomi Valkeinen { 5510f76ee892STomi Valkeinen component_del(&pdev->dev, &dsi_component_ops); 5511f76ee892STomi Valkeinen return 0; 5512f76ee892STomi Valkeinen } 5513f76ee892STomi Valkeinen 5514f76ee892STomi Valkeinen static int dsi_runtime_suspend(struct device *dev) 5515f76ee892STomi Valkeinen { 5516f76ee892STomi Valkeinen struct platform_device *pdev = to_platform_device(dev); 5517f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); 5518f76ee892STomi Valkeinen 5519f76ee892STomi Valkeinen dsi->is_enabled = false; 5520f76ee892STomi Valkeinen /* ensure the irq handler sees the is_enabled value */ 5521f76ee892STomi Valkeinen smp_wmb(); 5522f76ee892STomi Valkeinen /* wait for current handler to finish before turning the DSI off */ 5523f76ee892STomi Valkeinen synchronize_irq(dsi->irq); 5524f76ee892STomi Valkeinen 5525f76ee892STomi Valkeinen dispc_runtime_put(); 5526f76ee892STomi Valkeinen 5527f76ee892STomi Valkeinen return 0; 5528f76ee892STomi Valkeinen } 5529f76ee892STomi Valkeinen 5530f76ee892STomi Valkeinen static int dsi_runtime_resume(struct device *dev) 5531f76ee892STomi Valkeinen { 5532f76ee892STomi Valkeinen struct platform_device *pdev = to_platform_device(dev); 5533f76ee892STomi Valkeinen struct dsi_data *dsi = dsi_get_dsidrv_data(pdev); 5534f76ee892STomi Valkeinen int r; 5535f76ee892STomi Valkeinen 5536f76ee892STomi Valkeinen r = dispc_runtime_get(); 5537f76ee892STomi Valkeinen if (r) 5538f76ee892STomi Valkeinen return r; 5539f76ee892STomi Valkeinen 5540f76ee892STomi Valkeinen dsi->is_enabled = true; 5541f76ee892STomi Valkeinen /* ensure the irq handler sees the is_enabled value */ 5542f76ee892STomi Valkeinen smp_wmb(); 5543f76ee892STomi Valkeinen 5544f76ee892STomi Valkeinen return 0; 5545f76ee892STomi Valkeinen } 5546f76ee892STomi Valkeinen 5547f76ee892STomi Valkeinen static const struct dev_pm_ops dsi_pm_ops = { 5548f76ee892STomi Valkeinen .runtime_suspend = dsi_runtime_suspend, 5549f76ee892STomi Valkeinen .runtime_resume = dsi_runtime_resume, 5550f76ee892STomi Valkeinen }; 5551f76ee892STomi Valkeinen 5552f76ee892STomi Valkeinen static const struct dsi_module_id_data dsi_of_data_omap3[] = { 5553f76ee892STomi Valkeinen { .address = 0x4804fc00, .id = 0, }, 5554f76ee892STomi Valkeinen { }, 5555f76ee892STomi Valkeinen }; 5556f76ee892STomi Valkeinen 5557f76ee892STomi Valkeinen static const struct dsi_module_id_data dsi_of_data_omap4[] = { 5558f76ee892STomi Valkeinen { .address = 0x58004000, .id = 0, }, 5559f76ee892STomi Valkeinen { .address = 0x58005000, .id = 1, }, 5560f76ee892STomi Valkeinen { }, 5561f76ee892STomi Valkeinen }; 5562f76ee892STomi Valkeinen 5563f76ee892STomi Valkeinen static const struct dsi_module_id_data dsi_of_data_omap5[] = { 5564f76ee892STomi Valkeinen { .address = 0x58004000, .id = 0, }, 5565f76ee892STomi Valkeinen { .address = 0x58009000, .id = 1, }, 5566f76ee892STomi Valkeinen { }, 5567f76ee892STomi Valkeinen }; 5568f76ee892STomi Valkeinen 5569f76ee892STomi Valkeinen static const struct of_device_id dsi_of_match[] = { 5570f76ee892STomi Valkeinen { .compatible = "ti,omap3-dsi", .data = dsi_of_data_omap3, }, 5571f76ee892STomi Valkeinen { .compatible = "ti,omap4-dsi", .data = dsi_of_data_omap4, }, 5572f76ee892STomi Valkeinen { .compatible = "ti,omap5-dsi", .data = dsi_of_data_omap5, }, 5573f76ee892STomi Valkeinen {}, 5574f76ee892STomi Valkeinen }; 5575f76ee892STomi Valkeinen 5576f76ee892STomi Valkeinen static struct platform_driver omap_dsihw_driver = { 5577f76ee892STomi Valkeinen .probe = dsi_probe, 5578f76ee892STomi Valkeinen .remove = dsi_remove, 5579f76ee892STomi Valkeinen .driver = { 5580f76ee892STomi Valkeinen .name = "omapdss_dsi", 5581f76ee892STomi Valkeinen .pm = &dsi_pm_ops, 5582f76ee892STomi Valkeinen .of_match_table = dsi_of_match, 5583f76ee892STomi Valkeinen .suppress_bind_attrs = true, 5584f76ee892STomi Valkeinen }, 5585f76ee892STomi Valkeinen }; 5586f76ee892STomi Valkeinen 5587f76ee892STomi Valkeinen int __init dsi_init_platform_driver(void) 5588f76ee892STomi Valkeinen { 5589f76ee892STomi Valkeinen return platform_driver_register(&omap_dsihw_driver); 5590f76ee892STomi Valkeinen } 5591f76ee892STomi Valkeinen 5592f76ee892STomi Valkeinen void dsi_uninit_platform_driver(void) 5593f76ee892STomi Valkeinen { 5594f76ee892STomi Valkeinen platform_driver_unregister(&omap_dsihw_driver); 5595f76ee892STomi Valkeinen } 5596