2009-10-28 16:59:56 +07:00
|
|
|
/*
|
|
|
|
* linux/drivers/video/omap2/dss/dsi.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009 Nokia Corporation
|
|
|
|
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
|
|
* the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DSS_SUBSYS_NAME "DSI"
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/mutex.h>
|
2011-07-04 03:17:28 +07:00
|
|
|
#include <linux/module.h>
|
2010-01-11 21:33:56 +07:00
|
|
|
#include <linux/semaphore.h>
|
2009-10-28 16:59:56 +07:00
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
#include <linux/wait.h>
|
2010-01-12 19:16:41 +07:00
|
|
|
#include <linux/workqueue.h>
|
2010-07-28 19:53:38 +07:00
|
|
|
#include <linux/sched.h>
|
2011-05-12 18:56:27 +07:00
|
|
|
#include <linux/slab.h>
|
2011-05-12 18:56:29 +07:00
|
|
|
#include <linux/debugfs.h>
|
2011-05-27 14:52:19 +07:00
|
|
|
#include <linux/pm_runtime.h>
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-11 18:05:07 +07:00
|
|
|
#include <video/omapdss.h>
|
2011-08-25 19:55:03 +07:00
|
|
|
#include <video/mipi_display.h>
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
#include "dss.h"
|
2011-03-01 13:24:00 +07:00
|
|
|
#include "dss_features.h"
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
#define DSI_CATCH_MISSING_TE
|
|
|
|
|
|
|
|
struct dsi_reg { u16 idx; };
|
|
|
|
|
|
|
|
#define DSI_REG(idx) ((const struct dsi_reg) { idx })
|
|
|
|
|
|
|
|
#define DSI_SZ_REGS SZ_1K
|
|
|
|
/* DSI Protocol Engine */
|
|
|
|
|
|
|
|
#define DSI_REVISION DSI_REG(0x0000)
|
|
|
|
#define DSI_SYSCONFIG DSI_REG(0x0010)
|
|
|
|
#define DSI_SYSSTATUS DSI_REG(0x0014)
|
|
|
|
#define DSI_IRQSTATUS DSI_REG(0x0018)
|
|
|
|
#define DSI_IRQENABLE DSI_REG(0x001C)
|
|
|
|
#define DSI_CTRL DSI_REG(0x0040)
|
2011-05-16 16:47:08 +07:00
|
|
|
#define DSI_GNQ DSI_REG(0x0044)
|
2009-10-28 16:59:56 +07:00
|
|
|
#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048)
|
|
|
|
#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C)
|
|
|
|
#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050)
|
|
|
|
#define DSI_CLK_CTRL DSI_REG(0x0054)
|
|
|
|
#define DSI_TIMING1 DSI_REG(0x0058)
|
|
|
|
#define DSI_TIMING2 DSI_REG(0x005C)
|
|
|
|
#define DSI_VM_TIMING1 DSI_REG(0x0060)
|
|
|
|
#define DSI_VM_TIMING2 DSI_REG(0x0064)
|
|
|
|
#define DSI_VM_TIMING3 DSI_REG(0x0068)
|
|
|
|
#define DSI_CLK_TIMING DSI_REG(0x006C)
|
|
|
|
#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070)
|
|
|
|
#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074)
|
|
|
|
#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078)
|
|
|
|
#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C)
|
|
|
|
#define DSI_VM_TIMING4 DSI_REG(0x0080)
|
|
|
|
#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084)
|
|
|
|
#define DSI_VM_TIMING5 DSI_REG(0x0088)
|
|
|
|
#define DSI_VM_TIMING6 DSI_REG(0x008C)
|
|
|
|
#define DSI_VM_TIMING7 DSI_REG(0x0090)
|
|
|
|
#define DSI_STOPCLK_TIMING DSI_REG(0x0094)
|
|
|
|
#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20))
|
|
|
|
#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20))
|
|
|
|
#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20))
|
|
|
|
#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20))
|
|
|
|
#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20))
|
|
|
|
#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20))
|
|
|
|
#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20))
|
|
|
|
|
|
|
|
/* DSIPHY_SCP */
|
|
|
|
|
|
|
|
#define DSI_DSIPHY_CFG0 DSI_REG(0x200 + 0x0000)
|
|
|
|
#define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004)
|
|
|
|
#define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008)
|
|
|
|
#define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014)
|
2010-07-27 15:11:48 +07:00
|
|
|
#define DSI_DSIPHY_CFG10 DSI_REG(0x200 + 0x0028)
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* DSI_PLL_CTRL_SCP */
|
|
|
|
|
|
|
|
#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000)
|
|
|
|
#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004)
|
|
|
|
#define DSI_PLL_GO DSI_REG(0x300 + 0x0008)
|
|
|
|
#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C)
|
|
|
|
#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010)
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
#define REG_GET(dsidev, idx, start, end) \
|
|
|
|
FLD_GET(dsi_read_reg(dsidev, idx), start, end)
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
#define REG_FLD_MOD(dsidev, idx, val, start, end) \
|
|
|
|
dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end))
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* Global interrupts */
|
|
|
|
#define DSI_IRQ_VC0 (1 << 0)
|
|
|
|
#define DSI_IRQ_VC1 (1 << 1)
|
|
|
|
#define DSI_IRQ_VC2 (1 << 2)
|
|
|
|
#define DSI_IRQ_VC3 (1 << 3)
|
|
|
|
#define DSI_IRQ_WAKEUP (1 << 4)
|
|
|
|
#define DSI_IRQ_RESYNC (1 << 5)
|
|
|
|
#define DSI_IRQ_PLL_LOCK (1 << 7)
|
|
|
|
#define DSI_IRQ_PLL_UNLOCK (1 << 8)
|
|
|
|
#define DSI_IRQ_PLL_RECALL (1 << 9)
|
|
|
|
#define DSI_IRQ_COMPLEXIO_ERR (1 << 10)
|
|
|
|
#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14)
|
|
|
|
#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15)
|
|
|
|
#define DSI_IRQ_TE_TRIGGER (1 << 16)
|
|
|
|
#define DSI_IRQ_ACK_TRIGGER (1 << 17)
|
|
|
|
#define DSI_IRQ_SYNC_LOST (1 << 18)
|
|
|
|
#define DSI_IRQ_LDO_POWER_GOOD (1 << 19)
|
|
|
|
#define DSI_IRQ_TA_TIMEOUT (1 << 20)
|
|
|
|
#define DSI_IRQ_ERROR_MASK \
|
|
|
|
(DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
|
2011-09-05 18:18:27 +07:00
|
|
|
DSI_IRQ_TA_TIMEOUT | DSI_IRQ_SYNC_LOST)
|
2009-10-28 16:59:56 +07:00
|
|
|
#define DSI_IRQ_CHANNEL_MASK 0xf
|
|
|
|
|
|
|
|
/* Virtual channel interrupts */
|
|
|
|
#define DSI_VC_IRQ_CS (1 << 0)
|
|
|
|
#define DSI_VC_IRQ_ECC_CORR (1 << 1)
|
|
|
|
#define DSI_VC_IRQ_PACKET_SENT (1 << 2)
|
|
|
|
#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3)
|
|
|
|
#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4)
|
|
|
|
#define DSI_VC_IRQ_BTA (1 << 5)
|
|
|
|
#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6)
|
|
|
|
#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7)
|
|
|
|
#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8)
|
|
|
|
#define DSI_VC_IRQ_ERROR_MASK \
|
|
|
|
(DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \
|
|
|
|
DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \
|
|
|
|
DSI_VC_IRQ_FIFO_TX_UDF)
|
|
|
|
|
|
|
|
/* ComplexIO interrupts */
|
|
|
|
#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0)
|
|
|
|
#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1)
|
|
|
|
#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2)
|
2011-03-24 21:30:17 +07:00
|
|
|
#define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3)
|
|
|
|
#define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4)
|
2009-10-28 16:59:56 +07:00
|
|
|
#define DSI_CIO_IRQ_ERRESC1 (1 << 5)
|
|
|
|
#define DSI_CIO_IRQ_ERRESC2 (1 << 6)
|
|
|
|
#define DSI_CIO_IRQ_ERRESC3 (1 << 7)
|
2011-03-24 21:30:17 +07:00
|
|
|
#define DSI_CIO_IRQ_ERRESC4 (1 << 8)
|
|
|
|
#define DSI_CIO_IRQ_ERRESC5 (1 << 9)
|
2009-10-28 16:59:56 +07:00
|
|
|
#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12)
|
2011-03-24 21:30:17 +07:00
|
|
|
#define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14)
|
2009-10-28 16:59:56 +07:00
|
|
|
#define DSI_CIO_IRQ_STATEULPS1 (1 << 15)
|
|
|
|
#define DSI_CIO_IRQ_STATEULPS2 (1 << 16)
|
|
|
|
#define DSI_CIO_IRQ_STATEULPS3 (1 << 17)
|
2011-03-24 21:30:17 +07:00
|
|
|
#define DSI_CIO_IRQ_STATEULPS4 (1 << 18)
|
|
|
|
#define DSI_CIO_IRQ_STATEULPS5 (1 << 19)
|
2009-10-28 16:59:56 +07:00
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
|
2011-03-24 21:30:17 +07:00
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28)
|
|
|
|
#define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29)
|
2009-10-28 16:59:56 +07:00
|
|
|
#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
|
|
|
|
#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
|
2010-05-10 18:35:33 +07:00
|
|
|
#define DSI_CIO_IRQ_ERROR_MASK \
|
|
|
|
(DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \
|
2011-03-24 21:30:17 +07:00
|
|
|
DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \
|
|
|
|
DSI_CIO_IRQ_ERRSYNCESC5 | \
|
|
|
|
DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \
|
|
|
|
DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \
|
|
|
|
DSI_CIO_IRQ_ERRESC5 | \
|
|
|
|
DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \
|
|
|
|
DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \
|
|
|
|
DSI_CIO_IRQ_ERRCONTROL5 | \
|
2010-05-10 18:35:33 +07:00
|
|
|
DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \
|
|
|
|
DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \
|
2011-03-24 21:30:17 +07:00
|
|
|
DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \
|
|
|
|
DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \
|
|
|
|
DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5)
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
|
|
|
|
|
2013-02-22 17:58:35 +07:00
|
|
|
static int dsi_display_init_dispc(struct platform_device *dsidev,
|
|
|
|
struct omap_overlay_manager *mgr);
|
|
|
|
static void dsi_display_uninit_dispc(struct platform_device *dsidev,
|
|
|
|
struct omap_overlay_manager *mgr);
|
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
#define DSI_MAX_NR_ISRS 2
|
2011-10-13 15:22:06 +07:00
|
|
|
#define DSI_MAX_NR_LANES 5
|
|
|
|
|
|
|
|
enum dsi_lane_function {
|
|
|
|
DSI_LANE_UNUSED = 0,
|
|
|
|
DSI_LANE_CLK,
|
|
|
|
DSI_LANE_DATA1,
|
|
|
|
DSI_LANE_DATA2,
|
|
|
|
DSI_LANE_DATA3,
|
|
|
|
DSI_LANE_DATA4,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct dsi_lane_config {
|
|
|
|
enum dsi_lane_function function;
|
|
|
|
u8 polarity;
|
|
|
|
};
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
struct dsi_isr_data {
|
|
|
|
omap_dsi_isr_t isr;
|
|
|
|
void *arg;
|
|
|
|
u32 mask;
|
|
|
|
};
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
enum fifo_size {
|
|
|
|
DSI_FIFO_SIZE_0 = 0,
|
|
|
|
DSI_FIFO_SIZE_32 = 1,
|
|
|
|
DSI_FIFO_SIZE_64 = 2,
|
|
|
|
DSI_FIFO_SIZE_96 = 3,
|
|
|
|
DSI_FIFO_SIZE_128 = 4,
|
|
|
|
};
|
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
enum dsi_vc_source {
|
|
|
|
DSI_VC_SOURCE_L4 = 0,
|
|
|
|
DSI_VC_SOURCE_VP,
|
2009-10-28 16:59:56 +07:00
|
|
|
};
|
|
|
|
|
2009-12-17 19:35:21 +07:00
|
|
|
struct dsi_irq_stats {
|
|
|
|
unsigned long last_reset;
|
|
|
|
unsigned irq_count;
|
|
|
|
unsigned dsi_irqs[32];
|
|
|
|
unsigned vc_irqs[4][32];
|
|
|
|
unsigned cio_irqs[32];
|
|
|
|
};
|
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
struct dsi_isr_tables {
|
|
|
|
struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS];
|
|
|
|
struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS];
|
|
|
|
struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS];
|
|
|
|
};
|
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
struct dsi_clk_calc_ctx {
|
|
|
|
struct platform_device *dsidev;
|
|
|
|
|
|
|
|
/* inputs */
|
|
|
|
|
|
|
|
const struct omap_dss_dsi_config *config;
|
|
|
|
|
|
|
|
unsigned long req_pck_min, req_pck_nom, req_pck_max;
|
|
|
|
|
|
|
|
/* outputs */
|
|
|
|
|
|
|
|
struct dsi_clock_info dsi_cinfo;
|
|
|
|
struct dispc_clock_info dispc_cinfo;
|
|
|
|
|
|
|
|
struct omap_video_timings dispc_vm;
|
|
|
|
struct omap_dss_dsi_videomode_timings dsi_vm;
|
|
|
|
};
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data {
|
2011-01-24 13:22:02 +07:00
|
|
|
struct platform_device *pdev;
|
2009-10-28 16:59:56 +07:00
|
|
|
void __iomem *base;
|
2011-05-27 14:52:19 +07:00
|
|
|
|
2012-03-09 21:07:39 +07:00
|
|
|
int module_id;
|
|
|
|
|
2011-02-23 15:41:03 +07:00
|
|
|
int irq;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
struct clk *dss_clk;
|
|
|
|
struct clk *sys_clk;
|
|
|
|
|
2012-11-27 22:05:54 +07:00
|
|
|
struct dispc_clock_info user_dispc_cinfo;
|
|
|
|
struct dsi_clock_info user_dsi_cinfo;
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
struct dsi_clock_info current_cinfo;
|
|
|
|
|
2010-07-30 16:39:34 +07:00
|
|
|
bool vdds_dsi_enabled;
|
2009-10-28 16:59:56 +07:00
|
|
|
struct regulator *vdds_dsi_reg;
|
|
|
|
|
|
|
|
struct {
|
2011-08-22 13:28:08 +07:00
|
|
|
enum dsi_vc_source source;
|
2009-10-28 16:59:56 +07:00
|
|
|
struct omap_dss_device *dssdev;
|
|
|
|
enum fifo_size fifo_size;
|
2011-03-02 14:05:53 +07:00
|
|
|
int vc_id;
|
2009-10-28 16:59:56 +07:00
|
|
|
} vc[4];
|
|
|
|
|
|
|
|
struct mutex lock;
|
2010-01-11 21:33:56 +07:00
|
|
|
struct semaphore bus_lock;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
unsigned pll_locked;
|
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
spinlock_t irq_lock;
|
|
|
|
struct dsi_isr_tables isr_tables;
|
|
|
|
/* space for a copy used by the interrupt handler */
|
|
|
|
struct dsi_isr_tables isr_tables_copy;
|
|
|
|
|
2010-01-12 19:16:41 +07:00
|
|
|
int update_channel;
|
2011-11-03 21:34:20 +07:00
|
|
|
#ifdef DEBUG
|
|
|
|
unsigned update_bytes;
|
|
|
|
#endif
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
bool te_enabled;
|
2010-07-28 19:53:38 +07:00
|
|
|
bool ulps_enabled;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-01-12 19:16:41 +07:00
|
|
|
void (*framedone_callback)(int, void *);
|
|
|
|
void *framedone_data;
|
|
|
|
|
|
|
|
struct delayed_work framedone_timeout_work;
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
|
|
|
struct timer_list te_timer;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
unsigned long cache_req_pck;
|
|
|
|
unsigned long cache_clk_freq;
|
|
|
|
struct dsi_clock_info cache_cinfo;
|
|
|
|
|
|
|
|
u32 errors;
|
|
|
|
spinlock_t errors_lock;
|
|
|
|
#ifdef DEBUG
|
|
|
|
ktime_t perf_setup_time;
|
|
|
|
ktime_t perf_start_time;
|
|
|
|
#endif
|
|
|
|
int debug_read;
|
|
|
|
int debug_write;
|
2009-12-17 19:35:21 +07:00
|
|
|
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
|
|
|
spinlock_t irq_stats_lock;
|
|
|
|
struct dsi_irq_stats irq_stats;
|
|
|
|
#endif
|
2011-03-15 11:28:23 +07:00
|
|
|
/* DSI PLL Parameter Ranges */
|
|
|
|
unsigned long regm_max, regn_max;
|
|
|
|
unsigned long regm_dispc_max, regm_dsi_max;
|
|
|
|
unsigned long fint_min, fint_max;
|
|
|
|
unsigned long lpdiv_max;
|
2011-04-13 21:12:52 +07:00
|
|
|
|
2011-10-12 19:05:59 +07:00
|
|
|
unsigned num_lanes_supported;
|
2013-03-05 15:37:02 +07:00
|
|
|
unsigned line_buffer_size;
|
2011-05-16 16:47:08 +07:00
|
|
|
|
2011-10-13 15:22:06 +07:00
|
|
|
struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
|
|
|
|
unsigned num_lanes_used;
|
2011-05-16 16:47:08 +07:00
|
|
|
|
2011-04-13 21:12:52 +07:00
|
|
|
unsigned scp_clk_refcount;
|
2012-06-29 16:01:07 +07:00
|
|
|
|
|
|
|
struct dss_lcd_mgr_config mgr_config;
|
2012-08-13 15:47:30 +07:00
|
|
|
struct omap_video_timings timings;
|
2012-08-10 16:31:33 +07:00
|
|
|
enum omap_dss_dsi_pixel_format pix_fmt;
|
2012-08-16 19:32:00 +07:00
|
|
|
enum omap_dss_dsi_mode mode;
|
2012-08-13 23:43:39 +07:00
|
|
|
struct omap_dss_dsi_videomode_timings vm_timings;
|
2012-09-26 18:00:49 +07:00
|
|
|
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
struct omap_dss_device output;
|
2011-05-12 18:56:27 +07:00
|
|
|
};
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:28 +07:00
|
|
|
struct dsi_packet_sent_handler_data {
|
|
|
|
struct platform_device *dsidev;
|
|
|
|
struct completion *completion;
|
|
|
|
};
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
#ifdef DEBUG
|
2012-01-13 06:02:20 +07:00
|
|
|
static bool dsi_perf;
|
|
|
|
module_param(dsi_perf, bool, 0644);
|
2009-10-28 16:59:56 +07:00
|
|
|
#endif
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
return dev_get_drvdata(&dsidev->dev);
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev)
|
|
|
|
{
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
return to_platform_device(dssdev->output->dev);
|
2011-05-12 18:56:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct platform_device *dsi_get_dsidev_from_id(int module)
|
|
|
|
{
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
struct omap_dss_device *out;
|
2012-07-04 15:18:34 +07:00
|
|
|
enum omap_dss_output_id id;
|
|
|
|
|
2012-10-15 16:48:11 +07:00
|
|
|
switch (module) {
|
|
|
|
case 0:
|
|
|
|
id = OMAP_DSS_OUTPUT_DSI1;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
id = OMAP_DSS_OUTPUT_DSI2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-07-04 15:18:34 +07:00
|
|
|
|
|
|
|
out = omap_dss_get_output(id);
|
|
|
|
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
return out ? to_platform_device(out->dev) : NULL;
|
2011-05-12 18:56:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dsi_write_reg(struct platform_device *dsidev,
|
|
|
|
const struct dsi_reg idx, u32 val)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
__raw_writel(val, dsi->base + idx.idx);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline u32 dsi_read_reg(struct platform_device *dsidev,
|
|
|
|
const struct dsi_reg idx)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
return __raw_readl(dsi->base + idx.idx);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
void dsi_bus_lock(struct omap_dss_device *dssdev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
down(&dsi->bus_lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_bus_lock);
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
void dsi_bus_unlock(struct omap_dss_device *dssdev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
up(&dsi->bus_lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_bus_unlock);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static bool dsi_bus_is_locked(struct platform_device *dsidev)
|
2010-01-18 21:27:52 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
return dsi->bus_lock.count == 0;
|
2010-01-18 21:27:52 +07:00
|
|
|
}
|
|
|
|
|
2011-03-02 19:48:41 +07:00
|
|
|
static void dsi_completion_handler(void *data, u32 mask)
|
|
|
|
{
|
|
|
|
complete((struct completion *)data);
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline int wait_for_bit_change(struct platform_device *dsidev,
|
|
|
|
const struct dsi_reg idx, int bitnum, int value)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-10-13 23:06:49 +07:00
|
|
|
unsigned long timeout;
|
|
|
|
ktime_t wait;
|
|
|
|
int t;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-10-13 23:06:49 +07:00
|
|
|
/* first busyloop to see if the bit changes right away */
|
|
|
|
t = 100;
|
|
|
|
while (t-- > 0) {
|
|
|
|
if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
|
|
|
|
return value;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-10-13 23:06:49 +07:00
|
|
|
/* then loop for 500ms, sleeping for 1ms in between */
|
|
|
|
timeout = jiffies + msecs_to_jiffies(500);
|
|
|
|
while (time_before(jiffies, timeout)) {
|
|
|
|
if (REG_GET(dsidev, idx, bitnum, bitnum) == value)
|
|
|
|
return value;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-10-13 23:06:49 +07:00
|
|
|
wait = ns_to_ktime(1000 * 1000);
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-10-13 23:06:49 +07:00
|
|
|
return !value;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-09-08 20:12:16 +07:00
|
|
|
u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt)
|
|
|
|
{
|
|
|
|
switch (fmt) {
|
|
|
|
case OMAP_DSS_DSI_FMT_RGB888:
|
|
|
|
case OMAP_DSS_DSI_FMT_RGB666:
|
|
|
|
return 24;
|
|
|
|
case OMAP_DSS_DSI_FMT_RGB666_PACKED:
|
|
|
|
return 18;
|
|
|
|
case OMAP_DSS_DSI_FMT_RGB565:
|
|
|
|
return 16;
|
|
|
|
default:
|
|
|
|
BUG();
|
2012-05-18 15:47:02 +07:00
|
|
|
return 0;
|
2011-09-08 20:12:16 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
#ifdef DEBUG
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_perf_mark_setup(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
dsi->perf_setup_time = ktime_get();
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_perf_mark_start(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
dsi->perf_start_time = ktime_get();
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_perf_show(struct platform_device *dsidev, const char *name)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
ktime_t t, setup_time, trans_time;
|
|
|
|
u32 total_bytes;
|
|
|
|
u32 setup_us, trans_us, total_us;
|
|
|
|
|
|
|
|
if (!dsi_perf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
t = ktime_get();
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time);
|
2009-10-28 16:59:56 +07:00
|
|
|
setup_us = (u32)ktime_to_us(setup_time);
|
|
|
|
if (setup_us == 0)
|
|
|
|
setup_us = 1;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
trans_time = ktime_sub(t, dsi->perf_start_time);
|
2009-10-28 16:59:56 +07:00
|
|
|
trans_us = (u32)ktime_to_us(trans_time);
|
|
|
|
if (trans_us == 0)
|
|
|
|
trans_us = 1;
|
|
|
|
|
|
|
|
total_us = setup_us + trans_us;
|
|
|
|
|
2011-11-03 21:34:20 +07:00
|
|
|
total_bytes = dsi->update_bytes;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-01-11 21:41:10 +07:00
|
|
|
printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
|
|
|
|
"%u bytes, %u kbytes/sec\n",
|
|
|
|
name,
|
|
|
|
setup_us,
|
|
|
|
trans_us,
|
|
|
|
total_us,
|
|
|
|
1000*1000 / total_us,
|
|
|
|
total_bytes,
|
|
|
|
total_bytes * 1000 / total_us);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
#else
|
2011-05-23 20:36:09 +07:00
|
|
|
static inline void dsi_perf_mark_setup(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dsi_perf_mark_start(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dsi_perf_show(struct platform_device *dsidev,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
#endif
|
|
|
|
|
2012-09-29 14:03:05 +07:00
|
|
|
static int verbose_irq;
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
static void print_irq_status(u32 status)
|
|
|
|
{
|
2011-03-02 20:53:07 +07:00
|
|
|
if (status == 0)
|
|
|
|
return;
|
|
|
|
|
2012-09-29 14:03:05 +07:00
|
|
|
if (!verbose_irq && (status & ~DSI_IRQ_CHANNEL_MASK) == 0)
|
2009-10-28 16:59:56 +07:00
|
|
|
return;
|
|
|
|
|
2012-09-29 14:03:05 +07:00
|
|
|
#define PIS(x) (status & DSI_IRQ_##x) ? (#x " ") : ""
|
|
|
|
|
|
|
|
pr_debug("DSI IRQ: 0x%x: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
|
|
|
|
status,
|
|
|
|
verbose_irq ? PIS(VC0) : "",
|
|
|
|
verbose_irq ? PIS(VC1) : "",
|
|
|
|
verbose_irq ? PIS(VC2) : "",
|
|
|
|
verbose_irq ? PIS(VC3) : "",
|
|
|
|
PIS(WAKEUP),
|
|
|
|
PIS(RESYNC),
|
|
|
|
PIS(PLL_LOCK),
|
|
|
|
PIS(PLL_UNLOCK),
|
|
|
|
PIS(PLL_RECALL),
|
|
|
|
PIS(COMPLEXIO_ERR),
|
|
|
|
PIS(HS_TX_TIMEOUT),
|
|
|
|
PIS(LP_RX_TIMEOUT),
|
|
|
|
PIS(TE_TRIGGER),
|
|
|
|
PIS(ACK_TRIGGER),
|
|
|
|
PIS(SYNC_LOST),
|
|
|
|
PIS(LDO_POWER_GOOD),
|
|
|
|
PIS(TA_TIMEOUT));
|
|
|
|
#undef PIS
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void print_irq_status_vc(int channel, u32 status)
|
|
|
|
{
|
2011-03-02 20:53:07 +07:00
|
|
|
if (status == 0)
|
|
|
|
return;
|
|
|
|
|
2012-09-29 14:03:05 +07:00
|
|
|
if (!verbose_irq && (status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
|
2009-10-28 16:59:56 +07:00
|
|
|
return;
|
2012-09-29 14:03:05 +07:00
|
|
|
|
|
|
|
#define PIS(x) (status & DSI_VC_IRQ_##x) ? (#x " ") : ""
|
|
|
|
|
|
|
|
pr_debug("DSI VC(%d) IRQ 0x%x: %s%s%s%s%s%s%s%s%s\n",
|
|
|
|
channel,
|
|
|
|
status,
|
|
|
|
PIS(CS),
|
|
|
|
PIS(ECC_CORR),
|
|
|
|
PIS(ECC_NO_CORR),
|
|
|
|
verbose_irq ? PIS(PACKET_SENT) : "",
|
|
|
|
PIS(BTA),
|
|
|
|
PIS(FIFO_TX_OVF),
|
|
|
|
PIS(FIFO_RX_OVF),
|
|
|
|
PIS(FIFO_TX_UDF),
|
|
|
|
PIS(PP_BUSY_CHANGE));
|
2009-10-28 16:59:56 +07:00
|
|
|
#undef PIS
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_irq_status_cio(u32 status)
|
|
|
|
{
|
2011-03-02 20:53:07 +07:00
|
|
|
if (status == 0)
|
|
|
|
return;
|
|
|
|
|
2012-09-29 14:03:05 +07:00
|
|
|
#define PIS(x) (status & DSI_CIO_IRQ_##x) ? (#x " ") : ""
|
|
|
|
|
|
|
|
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",
|
|
|
|
status,
|
|
|
|
PIS(ERRSYNCESC1),
|
|
|
|
PIS(ERRSYNCESC2),
|
|
|
|
PIS(ERRSYNCESC3),
|
|
|
|
PIS(ERRESC1),
|
|
|
|
PIS(ERRESC2),
|
|
|
|
PIS(ERRESC3),
|
|
|
|
PIS(ERRCONTROL1),
|
|
|
|
PIS(ERRCONTROL2),
|
|
|
|
PIS(ERRCONTROL3),
|
|
|
|
PIS(STATEULPS1),
|
|
|
|
PIS(STATEULPS2),
|
|
|
|
PIS(STATEULPS3),
|
|
|
|
PIS(ERRCONTENTIONLP0_1),
|
|
|
|
PIS(ERRCONTENTIONLP1_1),
|
|
|
|
PIS(ERRCONTENTIONLP0_2),
|
|
|
|
PIS(ERRCONTENTIONLP1_2),
|
|
|
|
PIS(ERRCONTENTIONLP0_3),
|
|
|
|
PIS(ERRCONTENTIONLP1_3),
|
|
|
|
PIS(ULPSACTIVENOT_ALL0),
|
|
|
|
PIS(ULPSACTIVENOT_ALL1));
|
2009-10-28 16:59:56 +07:00
|
|
|
#undef PIS
|
|
|
|
}
|
|
|
|
|
2011-03-02 19:44:27 +07:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus,
|
|
|
|
u32 *vcstatus, u32 ciostatus)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int i;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock(&dsi->irq_stats_lock);
|
2011-03-02 19:44:27 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->irq_stats.irq_count++;
|
|
|
|
dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs);
|
2011-03-02 19:44:27 +07:00
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i)
|
2011-05-12 18:56:27 +07:00
|
|
|
dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]);
|
2011-03-02 19:44:27 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs);
|
2011-03-02 19:44:27 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock(&dsi->irq_stats_lock);
|
2011-03-02 19:44:27 +07:00
|
|
|
}
|
|
|
|
#else
|
2011-05-12 18:56:26 +07:00
|
|
|
#define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus)
|
2009-12-17 19:35:21 +07:00
|
|
|
#endif
|
|
|
|
|
2011-03-02 19:44:27 +07:00
|
|
|
static int debug_irq;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus,
|
|
|
|
u32 *vcstatus, u32 ciostatus)
|
2011-03-02 19:44:27 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:44:27 +07:00
|
|
|
int i;
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
if (irqstatus & DSI_IRQ_ERROR_MASK) {
|
|
|
|
DSSERR("DSI error, irqstatus %x\n", irqstatus);
|
|
|
|
print_irq_status(irqstatus);
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock(&dsi->errors_lock);
|
|
|
|
dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK;
|
|
|
|
spin_unlock(&dsi->errors_lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
} else if (debug_irq) {
|
|
|
|
print_irq_status(irqstatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
2011-03-02 19:44:27 +07:00
|
|
|
if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) {
|
|
|
|
DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
|
|
|
|
i, vcstatus[i]);
|
|
|
|
print_irq_status_vc(i, vcstatus[i]);
|
|
|
|
} else if (debug_irq) {
|
|
|
|
print_irq_status_vc(i, vcstatus[i]);
|
|
|
|
}
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:44:27 +07:00
|
|
|
if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) {
|
|
|
|
DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
|
|
|
|
print_irq_status_cio(ciostatus);
|
|
|
|
} else if (debug_irq) {
|
|
|
|
print_irq_status_cio(ciostatus);
|
|
|
|
}
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
static void dsi_call_isrs(struct dsi_isr_data *isr_array,
|
|
|
|
unsigned isr_array_size, u32 irqstatus)
|
|
|
|
{
|
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
|
|
|
if (isr_data->isr && isr_data->mask & irqstatus)
|
|
|
|
isr_data->isr(isr_data->arg, irqstatus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables,
|
|
|
|
u32 irqstatus, u32 *vcstatus, u32 ciostatus)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dsi_call_isrs(isr_tables->isr_table,
|
|
|
|
ARRAY_SIZE(isr_tables->isr_table),
|
|
|
|
irqstatus);
|
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
if (vcstatus[i] == 0)
|
|
|
|
continue;
|
|
|
|
dsi_call_isrs(isr_tables->isr_table_vc[i],
|
|
|
|
ARRAY_SIZE(isr_tables->isr_table_vc[i]),
|
|
|
|
vcstatus[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ciostatus != 0)
|
|
|
|
dsi_call_isrs(isr_tables->isr_table_cio,
|
|
|
|
ARRAY_SIZE(isr_tables->isr_table_cio),
|
|
|
|
ciostatus);
|
|
|
|
}
|
|
|
|
|
2011-03-02 19:44:27 +07:00
|
|
|
static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
|
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev;
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi;
|
2011-03-02 19:44:27 +07:00
|
|
|
u32 irqstatus, vcstatus[4], ciostatus;
|
|
|
|
int i;
|
2009-12-17 19:35:21 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsidev = (struct platform_device *) arg;
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi = dsi_get_dsidrv_data(dsidev);
|
2011-05-12 18:56:26 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock(&dsi->irq_lock);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:44:27 +07:00
|
|
|
/* IRQ is not for us */
|
2011-03-02 19:47:04 +07:00
|
|
|
if (!irqstatus) {
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock(&dsi->irq_lock);
|
2011-03-02 19:44:27 +07:00
|
|
|
return IRQ_NONE;
|
2011-03-02 19:47:04 +07:00
|
|
|
}
|
2010-06-09 19:31:01 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
|
2011-03-02 19:44:27 +07:00
|
|
|
/* flush posted write */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_read_reg(dsidev, DSI_IRQSTATUS);
|
2011-03-02 19:44:27 +07:00
|
|
|
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
|
|
if ((irqstatus & (1 << i)) == 0) {
|
|
|
|
vcstatus[i] = 0;
|
|
|
|
continue;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
|
2011-03-02 19:44:27 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]);
|
2009-10-28 16:59:56 +07:00
|
|
|
/* flush posted write */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i));
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
|
2011-05-12 18:56:26 +07:00
|
|
|
ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
|
2009-10-28 16:59:56 +07:00
|
|
|
/* flush posted write */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS);
|
2011-03-02 19:44:27 +07:00
|
|
|
} else {
|
|
|
|
ciostatus = 0;
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:44:27 +07:00
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
|
|
|
if (irqstatus & DSI_IRQ_TE_TRIGGER)
|
2011-05-12 18:56:27 +07:00
|
|
|
del_timer(&dsi->te_timer);
|
2011-03-02 19:44:27 +07:00
|
|
|
#endif
|
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
/* make a copy and unlock, so that isrs can unregister
|
|
|
|
* themselves */
|
2011-05-12 18:56:27 +07:00
|
|
|
memcpy(&dsi->isr_tables_copy, &dsi->isr_tables,
|
|
|
|
sizeof(dsi->isr_tables));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock(&dsi->irq_lock);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus);
|
2011-03-02 19:44:27 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus);
|
2009-12-17 19:35:21 +07:00
|
|
|
|
2011-02-23 15:41:03 +07:00
|
|
|
return IRQ_HANDLED;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2011-05-12 18:56:26 +07:00
|
|
|
static void _omap_dsi_configure_irqs(struct platform_device *dsidev,
|
|
|
|
struct dsi_isr_data *isr_array,
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned isr_array_size, u32 default_mask,
|
|
|
|
const struct dsi_reg enable_reg,
|
|
|
|
const struct dsi_reg status_reg)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-03-02 19:47:04 +07:00
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
u32 mask;
|
|
|
|
u32 old_mask;
|
2009-10-28 16:59:56 +07:00
|
|
|
int i;
|
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
mask = default_mask;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
if (isr_data->isr == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mask |= isr_data->mask;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
old_mask = dsi_read_reg(dsidev, enable_reg);
|
2011-03-02 19:47:04 +07:00
|
|
|
/* clear the irqstatus for newly enabled irqs */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask);
|
|
|
|
dsi_write_reg(dsidev, enable_reg, mask);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
/* flush posted writes */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_read_reg(dsidev, enable_reg);
|
|
|
|
dsi_read_reg(dsidev, status_reg);
|
2011-03-02 19:47:04 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2011-05-12 18:56:26 +07:00
|
|
|
static void _omap_dsi_set_irqs(struct platform_device *dsidev)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
u32 mask = DSI_IRQ_ERROR_MASK;
|
2009-10-28 16:59:56 +07:00
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
2011-03-02 19:47:04 +07:00
|
|
|
mask |= DSI_IRQ_TE_TRIGGER;
|
2009-10-28 16:59:56 +07:00
|
|
|
#endif
|
2011-05-12 18:56:27 +07:00
|
|
|
_omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table), mask,
|
2011-03-02 19:47:04 +07:00
|
|
|
DSI_IRQENABLE, DSI_IRQSTATUS);
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2011-05-12 18:56:26 +07:00
|
|
|
static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
_omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc],
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]),
|
2011-03-02 19:47:04 +07:00
|
|
|
DSI_VC_IRQ_ERROR_MASK,
|
|
|
|
DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc));
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
/* dsi->irq_lock has to be locked by the caller */
|
2011-05-12 18:56:26 +07:00
|
|
|
static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
_omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_cio),
|
2011-03-02 19:47:04 +07:00
|
|
|
DSI_CIO_IRQ_ERROR_MASK,
|
|
|
|
DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS);
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void _dsi_initialize_irq(struct platform_device *dsidev)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int vc;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
for (vc = 0; vc < 4; ++vc)
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs_vc(dsidev, vc);
|
|
|
|
_omap_dsi_set_irqs_cio(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 19:47:04 +07:00
|
|
|
static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
|
|
|
|
struct dsi_isr_data *isr_array, unsigned isr_array_size)
|
|
|
|
{
|
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
int free_idx;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
BUG_ON(isr == NULL);
|
|
|
|
|
|
|
|
/* check for duplicate entry and find a free slot */
|
|
|
|
free_idx = -1;
|
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
|
|
|
|
|
|
|
if (isr_data->isr == isr && isr_data->arg == arg &&
|
|
|
|
isr_data->mask == mask) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isr_data->isr == NULL && free_idx == -1)
|
|
|
|
free_idx = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (free_idx == -1)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
isr_data = &isr_array[free_idx];
|
|
|
|
isr_data->isr = isr;
|
|
|
|
isr_data->arg = arg;
|
|
|
|
isr_data->mask = mask;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
|
|
|
|
struct dsi_isr_data *isr_array, unsigned isr_array_size)
|
|
|
|
{
|
|
|
|
struct dsi_isr_data *isr_data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < isr_array_size; i++) {
|
|
|
|
isr_data = &isr_array[i];
|
|
|
|
if (isr_data->isr != isr || isr_data->arg != arg ||
|
|
|
|
isr_data->mask != mask)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
isr_data->isr = NULL;
|
|
|
|
isr_data->arg = NULL;
|
|
|
|
isr_data->mask = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr,
|
|
|
|
void *arg, u32 mask)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
if (r == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_unregister_isr(struct platform_device *dsidev,
|
|
|
|
omap_dsi_isr_t isr, void *arg, u32 mask)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
if (r == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_register_isr_vc(struct platform_device *dsidev, int channel,
|
|
|
|
omap_dsi_isr_t isr, void *arg, u32 mask)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
r = _dsi_register_isr(isr, arg, mask,
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->isr_tables.isr_table_vc[channel],
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
if (r == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs_vc(dsidev, channel);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel,
|
|
|
|
omap_dsi_isr_t isr, void *arg, u32 mask)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
r = _dsi_unregister_isr(isr, arg, mask,
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->isr_tables.isr_table_vc[channel],
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel]));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
if (r == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs_vc(dsidev, channel);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_register_isr_cio(struct platform_device *dsidev,
|
|
|
|
omap_dsi_isr_t isr, void *arg, u32 mask)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
if (r == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs_cio(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_unregister_isr_cio(struct platform_device *dsidev,
|
|
|
|
omap_dsi_isr_t isr, void *arg, u32 mask)
|
2011-03-02 19:47:04 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio,
|
|
|
|
ARRAY_SIZE(dsi->isr_tables.isr_table_cio));
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
if (r == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
_omap_dsi_set_irqs_cio(dsidev);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_lock, flags);
|
2011-03-02 19:47:04 +07:00
|
|
|
|
|
|
|
return r;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static u32 dsi_get_errors(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
unsigned long flags;
|
|
|
|
u32 e;
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->errors_lock, flags);
|
|
|
|
e = dsi->errors;
|
|
|
|
dsi->errors = 0;
|
|
|
|
spin_unlock_irqrestore(&dsi->errors_lock, flags);
|
2009-10-28 16:59:56 +07:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
int dsi_runtime_get(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-27 14:52:19 +07:00
|
|
|
int r;
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
DSSDBG("dsi_runtime_get\n");
|
|
|
|
|
|
|
|
r = pm_runtime_get_sync(&dsi->pdev->dev);
|
|
|
|
WARN_ON(r < 0);
|
|
|
|
return r < 0 ? r : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dsi_runtime_put(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int r;
|
|
|
|
|
|
|
|
DSSDBG("dsi_runtime_put\n");
|
|
|
|
|
2012-01-23 18:23:08 +07:00
|
|
|
r = pm_runtime_put_sync(&dsi->pdev->dev);
|
2012-06-27 20:37:18 +07:00
|
|
|
WARN_ON(r < 0 && r != -ENOSYS);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2013-05-03 17:42:24 +07:00
|
|
|
static int dsi_regulator_init(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
struct regulator *vdds_dsi;
|
|
|
|
|
|
|
|
if (dsi->vdds_dsi_reg != NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "vdds_dsi");
|
|
|
|
|
|
|
|
/* DT HACK: try VCXIO to make omapdss work for o4 sdp/panda */
|
|
|
|
if (IS_ERR(vdds_dsi))
|
|
|
|
vdds_dsi = devm_regulator_get(&dsi->pdev->dev, "VCXIO");
|
|
|
|
|
|
|
|
if (IS_ERR(vdds_dsi)) {
|
|
|
|
DSSERR("can't get VDDS_DSI regulator\n");
|
|
|
|
return PTR_ERR(vdds_dsi);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->vdds_dsi_reg = vdds_dsi;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
/* source clock for DSI PLL. this could also be PCLKFREE */
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
|
|
|
|
bool enable)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
if (enable)
|
2012-06-27 15:51:26 +07:00
|
|
|
clk_prepare_enable(dsi->sys_clk);
|
2009-10-28 16:59:56 +07:00
|
|
|
else
|
2012-06-27 15:51:26 +07:00
|
|
|
clk_disable_unprepare(dsi->sys_clk);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (enable && dsi->pll_locked) {
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("cannot lock PLL when enabling clocks\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void _dsi_print_reset_status(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
u32 l;
|
2010-10-07 17:27:42 +07:00
|
|
|
int b0, b1, b2;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* A dummy read using the SCP interface to any DSIPHY register is
|
|
|
|
* required after DSIPHY reset to complete the reset of the DSI complex
|
|
|
|
* I/O. */
|
2011-05-12 18:56:26 +07:00
|
|
|
l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-10-07 17:27:42 +07:00
|
|
|
if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) {
|
|
|
|
b0 = 28;
|
|
|
|
b1 = 27;
|
|
|
|
b2 = 26;
|
|
|
|
} else {
|
|
|
|
b0 = 24;
|
|
|
|
b1 = 25;
|
|
|
|
b2 = 26;
|
|
|
|
}
|
|
|
|
|
2012-09-29 14:03:05 +07:00
|
|
|
#define DSI_FLD_GET(fld, start, end)\
|
|
|
|
FLD_GET(dsi_read_reg(dsidev, DSI_##fld), start, end)
|
|
|
|
|
|
|
|
pr_debug("DSI resets: PLL (%d) CIO (%d) PHY (%x%x%x, %d, %d, %d)\n",
|
|
|
|
DSI_FLD_GET(PLL_STATUS, 0, 0),
|
|
|
|
DSI_FLD_GET(COMPLEXIO_CFG1, 29, 29),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, b0, b0),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, b1, b1),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, b2, b2),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, 29, 29),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, 30, 30),
|
|
|
|
DSI_FLD_GET(DSIPHY_CFG5, 31, 31));
|
|
|
|
|
|
|
|
#undef DSI_FLD_GET
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline int dsi_if_enable(struct platform_device *dsidev, bool enable)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
DSSDBG("dsi_if_enable(%d)\n", enable);
|
|
|
|
|
|
|
|
enable = enable ? 1 : 0;
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("Failed to set dsi_if_enable to %d\n", enable);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
return dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
return dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
return dsi->current_cinfo.clkin4ddr / 16;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static unsigned long dsi_fclk_rate(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
unsigned long r;
|
2011-05-27 14:52:19 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-03-09 21:07:39 +07:00
|
|
|
if (dss_get_dsi_clk_source(dsi->module_id) == OMAP_DSS_CLK_SRC_FCK) {
|
2011-02-24 15:47:30 +07:00
|
|
|
/* DSI FCLK source is DSS_CLK_FCK */
|
2011-05-27 14:52:19 +07:00
|
|
|
r = clk_get_rate(dsi->dss_clk);
|
2009-10-28 16:59:56 +07:00
|
|
|
} else {
|
2011-02-24 15:47:30 +07:00
|
|
|
/* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_get_pll_hsdiv_dsi_rate(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
static int dsi_lp_clock_calc(struct dsi_clock_info *cinfo,
|
|
|
|
unsigned long lp_clk_min, unsigned long lp_clk_max)
|
|
|
|
{
|
|
|
|
unsigned long dsi_fclk = cinfo->dsi_pll_hsdiv_dsi_clk;
|
|
|
|
unsigned lp_clk_div;
|
|
|
|
unsigned long lp_clk;
|
|
|
|
|
|
|
|
lp_clk_div = DIV_ROUND_UP(dsi_fclk, lp_clk_max * 2);
|
|
|
|
lp_clk = dsi_fclk / 2 / lp_clk_div;
|
|
|
|
|
|
|
|
if (lp_clk < lp_clk_min || lp_clk > lp_clk_max)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cinfo->lp_clk_div = lp_clk_div;
|
|
|
|
cinfo->lp_clk = lp_clk;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static int dsi_set_lp_clk_divisor(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
unsigned long dsi_fclk;
|
|
|
|
unsigned lp_clk_div;
|
|
|
|
unsigned long lp_clk;
|
|
|
|
|
2012-11-27 22:05:54 +07:00
|
|
|
lp_clk_div = dsi->user_dsi_cinfo.lp_clk_div;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (lp_clk_div == 0 || lp_clk_div > dsi->lpdiv_max)
|
2009-10-28 16:59:56 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_fclk = dsi_fclk_rate(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
lp_clk = dsi_fclk / 2 / lp_clk_div;
|
|
|
|
|
|
|
|
DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->current_cinfo.lp_clk = lp_clk;
|
|
|
|
dsi->current_cinfo.lp_clk_div = lp_clk_div;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
/* LP_CLK_DIVISOR */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
/* LP_RX_SYNCHRO_ENABLE */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_enable_scp_clk(struct platform_device *dsidev)
|
2011-04-13 21:12:52 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
if (dsi->scp_clk_refcount++ == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */
|
2011-04-13 21:12:52 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_disable_scp_clk(struct platform_device *dsidev)
|
2011-04-13 21:12:52 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
WARN_ON(dsi->scp_clk_refcount == 0);
|
|
|
|
if (--dsi->scp_clk_refcount == 0)
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */
|
2011-04-13 21:12:52 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
enum dsi_pll_power_state {
|
|
|
|
DSI_PLL_POWER_OFF = 0x0,
|
|
|
|
DSI_PLL_POWER_ON_HSCLK = 0x1,
|
|
|
|
DSI_PLL_POWER_ON_ALL = 0x2,
|
|
|
|
DSI_PLL_POWER_ON_DIV = 0x3,
|
|
|
|
};
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_pll_power(struct platform_device *dsidev,
|
|
|
|
enum dsi_pll_power_state state)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
int t = 0;
|
|
|
|
|
2011-04-15 14:42:59 +07:00
|
|
|
/* DSI-PLL power command 0x3 is not working */
|
|
|
|
if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) &&
|
|
|
|
state == DSI_PLL_POWER_ON_DIV)
|
|
|
|
state = DSI_PLL_POWER_ON_ALL;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
/* PLL_PWR_CMD */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* PLL_PWR_STATUS */
|
2011-05-12 18:56:26 +07:00
|
|
|
while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) {
|
2010-01-07 19:19:48 +07:00
|
|
|
if (++t > 1000) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("Failed to set DSI PLL power mode to %d\n",
|
|
|
|
state);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2010-01-07 19:19:48 +07:00
|
|
|
udelay(1);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-05 21:39:00 +07:00
|
|
|
unsigned long dsi_get_pll_clkin(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
return clk_get_rate(dsi->sys_clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dsi_hsdiv_calc(struct platform_device *dsidev, unsigned long pll,
|
|
|
|
unsigned long out_min, dsi_hsdiv_calc_func func, void *data)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int regm, regm_start, regm_stop;
|
|
|
|
unsigned long out_max;
|
|
|
|
unsigned long out;
|
|
|
|
|
|
|
|
out_min = out_min ? out_min : 1;
|
|
|
|
out_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
|
|
|
|
|
|
|
|
regm_start = max(DIV_ROUND_UP(pll, out_max), 1ul);
|
|
|
|
regm_stop = min(pll / out_min, dsi->regm_dispc_max);
|
|
|
|
|
|
|
|
for (regm = regm_start; regm <= regm_stop; ++regm) {
|
|
|
|
out = pll / regm;
|
|
|
|
|
|
|
|
if (func(regm, out, data))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool dsi_pll_calc(struct platform_device *dsidev, unsigned long clkin,
|
|
|
|
unsigned long pll_min, unsigned long pll_max,
|
|
|
|
dsi_pll_calc_func func, void *data)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int regn, regn_start, regn_stop;
|
|
|
|
int regm, regm_start, regm_stop;
|
|
|
|
unsigned long fint, pll;
|
|
|
|
const unsigned long pll_hw_max = 1800000000;
|
|
|
|
unsigned long fint_hw_min, fint_hw_max;
|
|
|
|
|
|
|
|
fint_hw_min = dsi->fint_min;
|
|
|
|
fint_hw_max = dsi->fint_max;
|
|
|
|
|
|
|
|
regn_start = max(DIV_ROUND_UP(clkin, fint_hw_max), 1ul);
|
|
|
|
regn_stop = min(clkin / fint_hw_min, dsi->regn_max);
|
|
|
|
|
|
|
|
pll_max = pll_max ? pll_max : ULONG_MAX;
|
|
|
|
|
|
|
|
for (regn = regn_start; regn <= regn_stop; ++regn) {
|
|
|
|
fint = clkin / regn;
|
|
|
|
|
|
|
|
regm_start = max(DIV_ROUND_UP(DIV_ROUND_UP(pll_min, fint), 2),
|
|
|
|
1ul);
|
|
|
|
regm_stop = min3(pll_max / fint / 2,
|
|
|
|
pll_hw_max / fint / 2,
|
|
|
|
dsi->regm_max);
|
|
|
|
|
|
|
|
for (regm = regm_start; regm <= regm_stop; ++regm) {
|
|
|
|
pll = 2 * regm * fint;
|
|
|
|
|
|
|
|
if (func(regn, regm, fint, pll, data))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
/* calculate clock rates using dividers in cinfo */
|
2012-03-15 20:22:58 +07:00
|
|
|
static int dsi_calc_clock_rates(struct platform_device *dsidev,
|
2010-12-02 18:27:11 +07:00
|
|
|
struct dsi_clock_info *cinfo)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max)
|
2009-10-28 16:59:56 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (cinfo->regm == 0 || cinfo->regm > dsi->regm_max)
|
2009-10-28 16:59:56 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (cinfo->regm_dispc > dsi->regm_dispc_max)
|
2009-10-28 16:59:56 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (cinfo->regm_dsi > dsi->regm_dsi_max)
|
2009-10-28 16:59:56 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-03-15 20:22:58 +07:00
|
|
|
cinfo->clkin = clk_get_rate(dsi->sys_clk);
|
|
|
|
cinfo->fint = cinfo->clkin / cinfo->regn;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min)
|
2009-10-28 16:59:56 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint;
|
|
|
|
|
|
|
|
if (cinfo->clkin4ddr > 1800 * 1000 * 1000)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-02-24 15:47:30 +07:00
|
|
|
if (cinfo->regm_dispc > 0)
|
|
|
|
cinfo->dsi_pll_hsdiv_dispc_clk =
|
|
|
|
cinfo->clkin4ddr / cinfo->regm_dispc;
|
2009-10-28 16:59:56 +07:00
|
|
|
else
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dispc_clk = 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-02-24 15:47:30 +07:00
|
|
|
if (cinfo->regm_dsi > 0)
|
|
|
|
cinfo->dsi_pll_hsdiv_dsi_clk =
|
|
|
|
cinfo->clkin4ddr / cinfo->regm_dsi;
|
2009-10-28 16:59:56 +07:00
|
|
|
else
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dsi_clk = 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
static void dsi_pll_calc_dsi_fck(struct dsi_clock_info *cinfo)
|
2012-09-24 19:15:06 +07:00
|
|
|
{
|
|
|
|
unsigned long max_dsi_fck;
|
|
|
|
|
|
|
|
max_dsi_fck = dss_feat_get_param_max(FEAT_PARAM_DSI_FCK);
|
|
|
|
|
|
|
|
cinfo->regm_dsi = DIV_ROUND_UP(cinfo->clkin4ddr, max_dsi_fck);
|
|
|
|
cinfo->dsi_pll_hsdiv_dsi_clk = cinfo->clkin4ddr / cinfo->regm_dsi;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
int dsi_pll_set_clock_div(struct platform_device *dsidev,
|
|
|
|
struct dsi_clock_info *cinfo)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int r = 0;
|
|
|
|
u32 l;
|
2011-03-22 18:33:36 +07:00
|
|
|
int f = 0;
|
2011-03-15 11:28:23 +07:00
|
|
|
u8 regn_start, regn_end, regm_start, regm_end;
|
|
|
|
u8 regm_dispc_start, regm_dispc_end, regm_dsi_start, regm_dsi_end;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-09-24 18:42:58 +07:00
|
|
|
DSSDBG("DSI PLL clock config starts");
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-03-15 20:22:58 +07:00
|
|
|
dsi->current_cinfo.clkin = cinfo->clkin;
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->current_cinfo.fint = cinfo->fint;
|
|
|
|
dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr;
|
|
|
|
dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk =
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dispc_clk;
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk =
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dsi_clk;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->current_cinfo.regn = cinfo->regn;
|
|
|
|
dsi->current_cinfo.regm = cinfo->regm;
|
|
|
|
dsi->current_cinfo.regm_dispc = cinfo->regm_dispc;
|
|
|
|
dsi->current_cinfo.regm_dsi = cinfo->regm_dsi;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("DSI Fint %ld\n", cinfo->fint);
|
|
|
|
|
2012-03-15 20:22:58 +07:00
|
|
|
DSSDBG("clkin rate %ld\n", cinfo->clkin);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* DSIPHY == CLKIN4DDR */
|
2012-03-15 20:22:58 +07:00
|
|
|
DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu = %lu\n",
|
2009-10-28 16:59:56 +07:00
|
|
|
cinfo->regm,
|
|
|
|
cinfo->regn,
|
|
|
|
cinfo->clkin,
|
|
|
|
cinfo->clkin4ddr);
|
|
|
|
|
|
|
|
DSSDBG("Data rate on 1 DSI lane %ld Mbps\n",
|
|
|
|
cinfo->clkin4ddr / 1000 / 1000 / 2);
|
|
|
|
|
|
|
|
DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4);
|
|
|
|
|
2011-02-24 15:47:30 +07:00
|
|
|
DSSDBG("regm_dispc = %d, %s (%s) = %lu\n", cinfo->regm_dispc,
|
2011-04-12 15:22:23 +07:00
|
|
|
dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC),
|
|
|
|
dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC),
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dispc_clk);
|
|
|
|
DSSDBG("regm_dsi = %d, %s (%s) = %lu\n", cinfo->regm_dsi,
|
2011-04-12 15:22:23 +07:00
|
|
|
dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI),
|
|
|
|
dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI),
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dsi_clk);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-15 11:28:23 +07:00
|
|
|
dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGN, ®n_start, ®n_end);
|
|
|
|
dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM, ®m_start, ®m_end);
|
|
|
|
dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DISPC, ®m_dispc_start,
|
|
|
|
®m_dispc_end);
|
|
|
|
dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DSI, ®m_dsi_start,
|
|
|
|
®m_dsi_end);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
/* DSI_PLL_AUTOMODE = manual */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_PLL_CONTROL, 0, 0, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION1);
|
2009-10-28 16:59:56 +07:00
|
|
|
l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */
|
2011-03-15 11:28:23 +07:00
|
|
|
/* DSI_PLL_REGN */
|
|
|
|
l = FLD_MOD(l, cinfo->regn - 1, regn_start, regn_end);
|
|
|
|
/* DSI_PLL_REGM */
|
|
|
|
l = FLD_MOD(l, cinfo->regm, regm_start, regm_end);
|
|
|
|
/* DSI_CLOCK_DIV */
|
2011-02-24 15:47:30 +07:00
|
|
|
l = FLD_MOD(l, cinfo->regm_dispc > 0 ? cinfo->regm_dispc - 1 : 0,
|
2011-03-15 11:28:23 +07:00
|
|
|
regm_dispc_start, regm_dispc_end);
|
|
|
|
/* DSIPROTO_CLOCK_DIV */
|
2011-02-24 15:47:30 +07:00
|
|
|
l = FLD_MOD(l, cinfo->regm_dsi > 0 ? cinfo->regm_dsi - 1 : 0,
|
2011-03-15 11:28:23 +07:00
|
|
|
regm_dsi_start, regm_dsi_end);
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION1, l);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
BUG_ON(cinfo->fint < dsi->fint_min || cinfo->fint > dsi->fint_max);
|
2011-03-22 18:33:36 +07:00
|
|
|
|
2012-08-22 20:00:31 +07:00
|
|
|
l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2);
|
|
|
|
|
2011-03-22 18:33:36 +07:00
|
|
|
if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) {
|
|
|
|
f = cinfo->fint < 1000000 ? 0x3 :
|
|
|
|
cinfo->fint < 1250000 ? 0x4 :
|
|
|
|
cinfo->fint < 1500000 ? 0x5 :
|
|
|
|
cinfo->fint < 1750000 ? 0x6 :
|
|
|
|
0x7;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-22 18:33:36 +07:00
|
|
|
l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */
|
2012-08-22 20:00:31 +07:00
|
|
|
} else if (dss_has_feature(FEAT_DSI_PLL_SELFREQDCO)) {
|
|
|
|
f = cinfo->clkin4ddr < 1000000000 ? 0x2 : 0x4;
|
|
|
|
|
|
|
|
l = FLD_MOD(l, f, 4, 1); /* PLL_SELFREQDCO */
|
|
|
|
}
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
|
|
|
|
l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */
|
|
|
|
l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */
|
2012-08-22 20:00:40 +07:00
|
|
|
if (dss_has_feature(FEAT_DSI_PLL_REFSEL))
|
|
|
|
l = FLD_MOD(l, 3, 22, 21); /* REF_SYSCLK = sysclk */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_PLL_GO, 0, 0) != 0) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("dsi pll go bit not going down.\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("cannot lock PLL\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->pll_locked = 1;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2);
|
2009-10-28 16:59:56 +07:00
|
|
|
l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */
|
|
|
|
l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */
|
|
|
|
l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */
|
|
|
|
l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */
|
|
|
|
l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */
|
|
|
|
l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */
|
|
|
|
l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
|
|
|
|
l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */
|
|
|
|
l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */
|
|
|
|
l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */
|
|
|
|
l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */
|
|
|
|
l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */
|
|
|
|
l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */
|
|
|
|
l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("PLL config done\n");
|
|
|
|
err:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
|
|
|
|
bool enable_hsdiv)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int r = 0;
|
|
|
|
enum dsi_pll_power_state pwstate;
|
|
|
|
|
|
|
|
DSSDBG("PLL init\n");
|
|
|
|
|
2012-10-12 20:27:28 +07:00
|
|
|
/*
|
|
|
|
* It seems that on many OMAPs we need to enable both to have a
|
|
|
|
* functional HSDivider.
|
|
|
|
*/
|
|
|
|
enable_hsclk = enable_hsdiv = true;
|
|
|
|
|
2013-05-03 17:42:24 +07:00
|
|
|
r = dsi_regulator_init(dsidev);
|
|
|
|
if (r)
|
|
|
|
return r;
|
2011-03-02 15:06:48 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_enable_pll_clock(dsidev, 1);
|
2011-04-13 21:12:52 +07:00
|
|
|
/*
|
|
|
|
* Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
|
|
|
|
*/
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_enable_scp_clk(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (!dsi->vdds_dsi_enabled) {
|
|
|
|
r = regulator_enable(dsi->vdds_dsi_reg);
|
2010-07-30 16:39:34 +07:00
|
|
|
if (r)
|
|
|
|
goto err0;
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->vdds_dsi_enabled = true;
|
2010-07-30 16:39:34 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* XXX PLL does not come out of reset without this... */
|
|
|
|
dispc_pck_free_enable(1);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("PLL not coming out of reset.\n");
|
|
|
|
r = -ENODEV;
|
2010-04-23 03:50:04 +07:00
|
|
|
dispc_pck_free_enable(0);
|
2009-10-28 16:59:56 +07:00
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX ... but if left on, we get problems when planes do not
|
|
|
|
* fill the whole display. No idea about this */
|
|
|
|
dispc_pck_free_enable(0);
|
|
|
|
|
|
|
|
if (enable_hsclk && enable_hsdiv)
|
|
|
|
pwstate = DSI_PLL_POWER_ON_ALL;
|
|
|
|
else if (enable_hsclk)
|
|
|
|
pwstate = DSI_PLL_POWER_ON_HSCLK;
|
|
|
|
else if (enable_hsdiv)
|
|
|
|
pwstate = DSI_PLL_POWER_ON_DIV;
|
|
|
|
else
|
|
|
|
pwstate = DSI_PLL_POWER_OFF;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_pll_power(dsidev, pwstate);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
if (r)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
DSSDBG("PLL init done\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
err1:
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->vdds_dsi_enabled) {
|
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
|
|
|
dsi->vdds_dsi_enabled = false;
|
2010-07-30 16:39:34 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
err0:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_disable_scp_clk(dsidev);
|
|
|
|
dsi_enable_pll_clock(dsidev, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
dsi->pll_locked = 0;
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_pll_power(dsidev, DSI_PLL_POWER_OFF);
|
2010-07-30 16:39:34 +07:00
|
|
|
if (disconnect_lanes) {
|
2011-05-12 18:56:27 +07:00
|
|
|
WARN_ON(!dsi->vdds_dsi_enabled);
|
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
|
|
|
dsi->vdds_dsi_enabled = false;
|
2010-07-30 16:39:34 +07:00
|
|
|
}
|
2011-04-13 21:12:52 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_disable_scp_clk(dsidev);
|
|
|
|
dsi_enable_pll_clock(dsidev, 0);
|
2011-04-13 21:12:52 +07:00
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("PLL uninit done\n");
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:29 +07:00
|
|
|
static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,
|
|
|
|
struct seq_file *s)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
struct dsi_clock_info *cinfo = &dsi->current_cinfo;
|
2011-04-12 15:22:23 +07:00
|
|
|
enum omap_dss_clk_source dispc_clk_src, dsi_clk_src;
|
2012-03-09 21:07:39 +07:00
|
|
|
int dsi_module = dsi->module_id;
|
2011-03-02 13:27:25 +07:00
|
|
|
|
|
|
|
dispc_clk_src = dss_get_dispc_clk_source();
|
2011-05-12 18:56:29 +07:00
|
|
|
dsi_clk_src = dss_get_dsi_clk_source(dsi_module);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
if (dsi_runtime_get(dsidev))
|
|
|
|
return;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:29 +07:00
|
|
|
seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-03-15 20:22:58 +07:00
|
|
|
seq_printf(s, "dsi pll clkin\t%lu\n", cinfo->clkin);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn);
|
|
|
|
|
|
|
|
seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n",
|
|
|
|
cinfo->clkin4ddr, cinfo->regm);
|
|
|
|
|
2011-12-12 13:17:41 +07:00
|
|
|
seq_printf(s, "DSI_PLL_HSDIV_DISPC (%s)\t%-16luregm_dispc %u\t(%s)\n",
|
|
|
|
dss_feat_get_clk_source_name(dsi_module == 0 ?
|
|
|
|
OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
|
|
|
|
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC),
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dispc_clk,
|
|
|
|
cinfo->regm_dispc,
|
2011-04-12 15:22:23 +07:00
|
|
|
dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ?
|
2010-02-23 22:40:00 +07:00
|
|
|
"off" : "on");
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-12-12 13:17:41 +07:00
|
|
|
seq_printf(s, "DSI_PLL_HSDIV_DSI (%s)\t%-16luregm_dsi %u\t(%s)\n",
|
|
|
|
dss_feat_get_clk_source_name(dsi_module == 0 ?
|
|
|
|
OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI :
|
|
|
|
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI),
|
2011-02-24 15:47:30 +07:00
|
|
|
cinfo->dsi_pll_hsdiv_dsi_clk,
|
|
|
|
cinfo->regm_dsi,
|
2011-04-12 15:22:23 +07:00
|
|
|
dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ?
|
2010-02-23 22:40:00 +07:00
|
|
|
"off" : "on");
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:29 +07:00
|
|
|
seq_printf(s, "- DSI%d -\n", dsi_module + 1);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 13:27:25 +07:00
|
|
|
seq_printf(s, "dsi fclk source = %s (%s)\n",
|
|
|
|
dss_get_generic_clk_source_name(dsi_clk_src),
|
|
|
|
dss_feat_get_clk_source_name(dsi_clk_src));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
seq_printf(s, "DDR_CLK\t\t%lu\n",
|
|
|
|
cinfo->clkin4ddr / 4);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk);
|
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
dsi_runtime_put(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:29 +07:00
|
|
|
void dsi_dump_clocks(struct seq_file *s)
|
|
|
|
{
|
|
|
|
struct platform_device *dsidev;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_NUM_DSI; i++) {
|
|
|
|
dsidev = dsi_get_dsidev_from_id(i);
|
|
|
|
if (dsidev)
|
|
|
|
dsi_dump_dsidev_clocks(dsidev, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-17 19:35:21 +07:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
2011-05-12 18:56:29 +07:00
|
|
|
static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
|
|
|
|
struct seq_file *s)
|
2009-12-17 19:35:21 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-12-17 19:35:21 +07:00
|
|
|
unsigned long flags;
|
|
|
|
struct dsi_irq_stats stats;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_irqsave(&dsi->irq_stats_lock, flags);
|
2009-12-17 19:35:21 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
stats = dsi->irq_stats;
|
|
|
|
memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats));
|
|
|
|
dsi->irq_stats.last_reset = jiffies;
|
2009-12-17 19:35:21 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_unlock_irqrestore(&dsi->irq_stats_lock, flags);
|
2009-12-17 19:35:21 +07:00
|
|
|
|
|
|
|
seq_printf(s, "period %u ms\n",
|
|
|
|
jiffies_to_msecs(jiffies - stats.last_reset));
|
|
|
|
|
|
|
|
seq_printf(s, "irqs %d\n", stats.irq_count);
|
|
|
|
#define PIS(x) \
|
|
|
|
seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]);
|
|
|
|
|
2012-03-09 21:07:39 +07:00
|
|
|
seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1);
|
2009-12-17 19:35:21 +07:00
|
|
|
PIS(VC0);
|
|
|
|
PIS(VC1);
|
|
|
|
PIS(VC2);
|
|
|
|
PIS(VC3);
|
|
|
|
PIS(WAKEUP);
|
|
|
|
PIS(RESYNC);
|
|
|
|
PIS(PLL_LOCK);
|
|
|
|
PIS(PLL_UNLOCK);
|
|
|
|
PIS(PLL_RECALL);
|
|
|
|
PIS(COMPLEXIO_ERR);
|
|
|
|
PIS(HS_TX_TIMEOUT);
|
|
|
|
PIS(LP_RX_TIMEOUT);
|
|
|
|
PIS(TE_TRIGGER);
|
|
|
|
PIS(ACK_TRIGGER);
|
|
|
|
PIS(SYNC_LOST);
|
|
|
|
PIS(LDO_POWER_GOOD);
|
|
|
|
PIS(TA_TIMEOUT);
|
|
|
|
#undef PIS
|
|
|
|
|
|
|
|
#define PIS(x) \
|
|
|
|
seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
|
|
|
|
stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
|
|
|
|
stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
|
|
|
|
stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
|
|
|
|
stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
|
|
|
|
|
|
|
|
seq_printf(s, "-- VC interrupts --\n");
|
|
|
|
PIS(CS);
|
|
|
|
PIS(ECC_CORR);
|
|
|
|
PIS(PACKET_SENT);
|
|
|
|
PIS(FIFO_TX_OVF);
|
|
|
|
PIS(FIFO_RX_OVF);
|
|
|
|
PIS(BTA);
|
|
|
|
PIS(ECC_NO_CORR);
|
|
|
|
PIS(FIFO_TX_UDF);
|
|
|
|
PIS(PP_BUSY_CHANGE);
|
|
|
|
#undef PIS
|
|
|
|
|
|
|
|
#define PIS(x) \
|
|
|
|
seq_printf(s, "%-20s %10d\n", #x, \
|
|
|
|
stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
|
|
|
|
|
|
|
|
seq_printf(s, "-- CIO interrupts --\n");
|
|
|
|
PIS(ERRSYNCESC1);
|
|
|
|
PIS(ERRSYNCESC2);
|
|
|
|
PIS(ERRSYNCESC3);
|
|
|
|
PIS(ERRESC1);
|
|
|
|
PIS(ERRESC2);
|
|
|
|
PIS(ERRESC3);
|
|
|
|
PIS(ERRCONTROL1);
|
|
|
|
PIS(ERRCONTROL2);
|
|
|
|
PIS(ERRCONTROL3);
|
|
|
|
PIS(STATEULPS1);
|
|
|
|
PIS(STATEULPS2);
|
|
|
|
PIS(STATEULPS3);
|
|
|
|
PIS(ERRCONTENTIONLP0_1);
|
|
|
|
PIS(ERRCONTENTIONLP1_1);
|
|
|
|
PIS(ERRCONTENTIONLP0_2);
|
|
|
|
PIS(ERRCONTENTIONLP1_2);
|
|
|
|
PIS(ERRCONTENTIONLP0_3);
|
|
|
|
PIS(ERRCONTENTIONLP1_3);
|
|
|
|
PIS(ULPSACTIVENOT_ALL0);
|
|
|
|
PIS(ULPSACTIVENOT_ALL1);
|
|
|
|
#undef PIS
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:29 +07:00
|
|
|
static void dsi1_dump_irqs(struct seq_file *s)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
|
|
|
|
|
2011-05-12 18:56:29 +07:00
|
|
|
dsi_dump_dsidev_irqs(dsidev, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi2_dump_irqs(struct seq_file *s)
|
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
|
|
|
|
|
|
|
|
dsi_dump_dsidev_irqs(dsidev, s);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void dsi_dump_dsidev_regs(struct platform_device *dsidev,
|
|
|
|
struct seq_file *s)
|
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r))
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
if (dsi_runtime_get(dsidev))
|
|
|
|
return;
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_enable_scp_clk(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DUMPREG(DSI_REVISION);
|
|
|
|
DUMPREG(DSI_SYSCONFIG);
|
|
|
|
DUMPREG(DSI_SYSSTATUS);
|
|
|
|
DUMPREG(DSI_IRQSTATUS);
|
|
|
|
DUMPREG(DSI_IRQENABLE);
|
|
|
|
DUMPREG(DSI_CTRL);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_CFG1);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
|
|
|
|
DUMPREG(DSI_CLK_CTRL);
|
|
|
|
DUMPREG(DSI_TIMING1);
|
|
|
|
DUMPREG(DSI_TIMING2);
|
|
|
|
DUMPREG(DSI_VM_TIMING1);
|
|
|
|
DUMPREG(DSI_VM_TIMING2);
|
|
|
|
DUMPREG(DSI_VM_TIMING3);
|
|
|
|
DUMPREG(DSI_CLK_TIMING);
|
|
|
|
DUMPREG(DSI_TX_FIFO_VC_SIZE);
|
|
|
|
DUMPREG(DSI_RX_FIFO_VC_SIZE);
|
|
|
|
DUMPREG(DSI_COMPLEXIO_CFG2);
|
|
|
|
DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
|
|
|
|
DUMPREG(DSI_VM_TIMING4);
|
|
|
|
DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
|
|
|
|
DUMPREG(DSI_VM_TIMING5);
|
|
|
|
DUMPREG(DSI_VM_TIMING6);
|
|
|
|
DUMPREG(DSI_VM_TIMING7);
|
|
|
|
DUMPREG(DSI_STOPCLK_TIMING);
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(0));
|
|
|
|
DUMPREG(DSI_VC_TE(0));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(0));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(0));
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(1));
|
|
|
|
DUMPREG(DSI_VC_TE(1));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(1));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(1));
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(2));
|
|
|
|
DUMPREG(DSI_VC_TE(2));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(2));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(2));
|
|
|
|
|
|
|
|
DUMPREG(DSI_VC_CTRL(3));
|
|
|
|
DUMPREG(DSI_VC_TE(3));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
|
|
|
|
DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
|
|
|
|
DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
|
|
|
|
DUMPREG(DSI_VC_IRQSTATUS(3));
|
|
|
|
DUMPREG(DSI_VC_IRQENABLE(3));
|
|
|
|
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG0);
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG1);
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG2);
|
|
|
|
DUMPREG(DSI_DSIPHY_CFG5);
|
|
|
|
|
|
|
|
DUMPREG(DSI_PLL_CONTROL);
|
|
|
|
DUMPREG(DSI_PLL_STATUS);
|
|
|
|
DUMPREG(DSI_PLL_GO);
|
|
|
|
DUMPREG(DSI_PLL_CONFIGURATION1);
|
|
|
|
DUMPREG(DSI_PLL_CONFIGURATION2);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_disable_scp_clk(dsidev);
|
2011-05-27 14:52:19 +07:00
|
|
|
dsi_runtime_put(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
#undef DUMPREG
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:29 +07:00
|
|
|
static void dsi1_dump_regs(struct seq_file *s)
|
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_id(0);
|
|
|
|
|
|
|
|
dsi_dump_dsidev_regs(dsidev, s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi2_dump_regs(struct seq_file *s)
|
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_id(1);
|
|
|
|
|
|
|
|
dsi_dump_dsidev_regs(dsidev, s);
|
|
|
|
}
|
|
|
|
|
2010-10-06 19:18:13 +07:00
|
|
|
enum dsi_cio_power_state {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSI_COMPLEXIO_POWER_OFF = 0x0,
|
|
|
|
DSI_COMPLEXIO_POWER_ON = 0x1,
|
|
|
|
DSI_COMPLEXIO_POWER_ULPS = 0x2,
|
|
|
|
};
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_cio_power(struct platform_device *dsidev,
|
|
|
|
enum dsi_cio_power_state state)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
int t = 0;
|
|
|
|
|
|
|
|
/* PWR_CMD */
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* PWR_STATUS */
|
2011-05-12 18:56:26 +07:00
|
|
|
while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1),
|
|
|
|
26, 25) != state) {
|
2010-01-07 19:19:48 +07:00
|
|
|
if (++t > 1000) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("failed to set complexio power state to "
|
|
|
|
"%d\n", state);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
2010-01-07 19:19:48 +07:00
|
|
|
udelay(1);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-16 16:47:09 +07:00
|
|
|
static unsigned dsi_get_line_buf_size(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
/* line buffer on OMAP3 is 1024 x 24bits */
|
|
|
|
/* XXX: for some reason using full buffer size causes
|
|
|
|
* considerable TX slowdown with update sizes that fill the
|
|
|
|
* whole buffer */
|
|
|
|
if (!dss_has_feature(FEAT_DSI_GNQ))
|
|
|
|
return 1023 * 3;
|
|
|
|
|
|
|
|
val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */
|
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
case 1:
|
|
|
|
return 512 * 3; /* 512x24 bits */
|
|
|
|
case 2:
|
|
|
|
return 682 * 3; /* 682x24 bits */
|
|
|
|
case 3:
|
|
|
|
return 853 * 3; /* 853x24 bits */
|
|
|
|
case 4:
|
|
|
|
return 1024 * 3; /* 1024x24 bits */
|
|
|
|
case 5:
|
|
|
|
return 1194 * 3; /* 1194x24 bits */
|
|
|
|
case 6:
|
|
|
|
return 1365 * 3; /* 1365x24 bits */
|
2012-08-22 20:00:47 +07:00
|
|
|
case 7:
|
|
|
|
return 1920 * 3; /* 1920x24 bits */
|
2011-05-16 16:47:09 +07:00
|
|
|
default:
|
|
|
|
BUG();
|
2012-05-18 15:47:02 +07:00
|
|
|
return 0;
|
2011-05-16 16:47:09 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static int dsi_set_lane_config(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-10-13 15:22:39 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
static const u8 offsets[] = { 0, 4, 8, 12, 16 };
|
|
|
|
static const enum dsi_lane_function functions[] = {
|
|
|
|
DSI_LANE_CLK,
|
|
|
|
DSI_LANE_DATA1,
|
|
|
|
DSI_LANE_DATA2,
|
|
|
|
DSI_LANE_DATA3,
|
|
|
|
DSI_LANE_DATA4,
|
|
|
|
};
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 r;
|
2011-10-13 15:22:39 +07:00
|
|
|
int i;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1);
|
2011-10-13 15:22:39 +07:00
|
|
|
|
|
|
|
for (i = 0; i < dsi->num_lanes_used; ++i) {
|
|
|
|
unsigned offset = offsets[i];
|
|
|
|
unsigned polarity, lane_number;
|
|
|
|
unsigned t;
|
|
|
|
|
|
|
|
for (t = 0; t < dsi->num_lanes_supported; ++t)
|
|
|
|
if (dsi->lanes[t].function == functions[i])
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (t == dsi->num_lanes_supported)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
lane_number = t;
|
|
|
|
polarity = dsi->lanes[t].polarity;
|
|
|
|
|
|
|
|
r = FLD_MOD(r, lane_number + 1, offset + 2, offset);
|
|
|
|
r = FLD_MOD(r, polarity, offset + 3, offset + 3);
|
2011-05-16 16:47:08 +07:00
|
|
|
}
|
|
|
|
|
2011-10-13 15:22:39 +07:00
|
|
|
/* clear the unused lanes */
|
|
|
|
for (; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
unsigned offset = offsets[i];
|
|
|
|
|
|
|
|
r = FLD_MOD(r, 0, offset + 2, offset);
|
|
|
|
r = FLD_MOD(r, 0, offset + 3, offset + 3);
|
2011-05-16 16:47:08 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-10-13 15:22:39 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-10-13 15:22:39 +07:00
|
|
|
return 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
/* convert time in ns to ddr ticks, rounding up */
|
2011-05-12 18:56:27 +07:00
|
|
|
unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4;
|
2009-10-28 16:59:56 +07:00
|
|
|
return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4;
|
2009-10-28 16:59:56 +07:00
|
|
|
return ddr * 1000 * 1000 / (ddr_clk / 1000);
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_cio_timings(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
u32 r;
|
|
|
|
u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
|
|
|
|
u32 tlpx_half, tclk_trail, tclk_zero;
|
|
|
|
u32 tclk_prepare;
|
|
|
|
|
|
|
|
/* calculate timings */
|
|
|
|
|
|
|
|
/* 1 * DDR_CLK = 2 * UI */
|
|
|
|
|
|
|
|
/* min 40ns + 4*UI max 85ns + 6*UI */
|
2011-05-12 18:56:26 +07:00
|
|
|
ths_prepare = ns2ddr(dsidev, 70) + 2;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* min 145ns + 10*UI */
|
2011-05-12 18:56:26 +07:00
|
|
|
ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* min max(8*UI, 60ns+4*UI) */
|
2011-05-12 18:56:26 +07:00
|
|
|
ths_trail = ns2ddr(dsidev, 60) + 5;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* min 100ns */
|
2011-05-12 18:56:26 +07:00
|
|
|
ths_exit = ns2ddr(dsidev, 145);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* tlpx min 50n */
|
2011-05-12 18:56:26 +07:00
|
|
|
tlpx_half = ns2ddr(dsidev, 25);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* min 60ns */
|
2011-05-12 18:56:26 +07:00
|
|
|
tclk_trail = ns2ddr(dsidev, 60) + 2;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* min 38ns, max 95ns */
|
2011-05-12 18:56:26 +07:00
|
|
|
tclk_prepare = ns2ddr(dsidev, 65);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* min tclk-prepare + tclk-zero = 300ns */
|
2011-05-12 18:56:26 +07:00
|
|
|
tclk_zero = ns2ddr(dsidev, 260);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
|
2011-05-12 18:56:26 +07:00
|
|
|
ths_prepare, ddr2ns(dsidev, ths_prepare),
|
|
|
|
ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero));
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
|
2011-05-12 18:56:26 +07:00
|
|
|
ths_trail, ddr2ns(dsidev, ths_trail),
|
|
|
|
ths_exit, ddr2ns(dsidev, ths_exit));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
|
|
|
|
"tclk_zero %u (%uns)\n",
|
2011-05-12 18:56:26 +07:00
|
|
|
tlpx_half, ddr2ns(dsidev, tlpx_half),
|
|
|
|
tclk_trail, ddr2ns(dsidev, tclk_trail),
|
|
|
|
tclk_zero, ddr2ns(dsidev, tclk_zero));
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("tclk_prepare %u (%uns)\n",
|
2011-05-12 18:56:26 +07:00
|
|
|
tclk_prepare, ddr2ns(dsidev, tclk_prepare));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* program timings */
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, ths_prepare, 31, 24);
|
|
|
|
r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
|
|
|
|
r = FLD_MOD(r, ths_trail, 15, 8);
|
|
|
|
r = FLD_MOD(r, ths_exit, 7, 0);
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
|
2012-09-24 13:34:52 +07:00
|
|
|
r = FLD_MOD(r, tlpx_half, 20, 16);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, tclk_trail, 15, 8);
|
|
|
|
r = FLD_MOD(r, tclk_zero, 7, 0);
|
2012-09-24 19:15:57 +07:00
|
|
|
|
|
|
|
if (dss_has_feature(FEAT_DSI_PHY_DCC)) {
|
|
|
|
r = FLD_MOD(r, 0, 21, 21); /* DCCEN = disable */
|
|
|
|
r = FLD_MOD(r, 1, 22, 22); /* CLKINP_DIVBY2EN = enable */
|
|
|
|
r = FLD_MOD(r, 1, 23, 23); /* CLKINP_SEL = enable */
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, tclk_prepare, 7, 0);
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-10-13 20:06:43 +07:00
|
|
|
/* lane masks have lane 0 at lsb. mask_p for positive lines, n for negative */
|
2012-08-14 13:59:22 +07:00
|
|
|
static void dsi_cio_enable_lane_override(struct platform_device *dsidev,
|
2011-10-13 20:06:43 +07:00
|
|
|
unsigned mask_p, unsigned mask_n)
|
2010-07-27 15:11:48 +07:00
|
|
|
{
|
2011-05-16 16:47:08 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-10-13 20:06:43 +07:00
|
|
|
int i;
|
|
|
|
u32 l;
|
2011-10-12 19:05:59 +07:00
|
|
|
u8 lptxscp_start = dsi->num_lanes_supported == 3 ? 22 : 26;
|
2010-07-27 15:11:48 +07:00
|
|
|
|
2011-10-13 20:06:43 +07:00
|
|
|
l = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
unsigned p = dsi->lanes[i].polarity;
|
|
|
|
|
|
|
|
if (mask_p & (1 << i))
|
|
|
|
l |= 1 << (i * 2 + (p ? 0 : 1));
|
|
|
|
|
|
|
|
if (mask_n & (1 << i))
|
|
|
|
l |= 1 << (i * 2 + (p ? 1 : 0));
|
|
|
|
}
|
|
|
|
|
2010-07-27 15:11:48 +07:00
|
|
|
/*
|
|
|
|
* Bits in REGLPTXSCPDAT4TO0DXDY:
|
|
|
|
* 17: DY0 18: DX0
|
|
|
|
* 19: DY1 20: DX1
|
|
|
|
* 21: DY2 22: DX2
|
2011-05-16 16:47:08 +07:00
|
|
|
* 23: DY3 24: DX3
|
|
|
|
* 25: DY4 26: DX4
|
2010-07-27 15:11:48 +07:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* Set the lane override configuration */
|
2011-05-12 18:56:26 +07:00
|
|
|
|
|
|
|
/* REGLPTXSCPDAT4TO0DXDY */
|
2011-05-16 16:47:08 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17);
|
2010-07-27 15:11:48 +07:00
|
|
|
|
|
|
|
/* Enable lane override */
|
2011-05-12 18:56:26 +07:00
|
|
|
|
|
|
|
/* ENLPTXSCPDAT */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27);
|
2010-07-27 15:11:48 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_cio_disable_lane_override(struct platform_device *dsidev)
|
2010-07-27 15:11:48 +07:00
|
|
|
{
|
|
|
|
/* Disable lane override */
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */
|
2010-07-27 15:11:48 +07:00
|
|
|
/* Reset the lane override configuration */
|
2011-05-12 18:56:26 +07:00
|
|
|
/* REGLPTXSCPDAT4TO0DXDY */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17);
|
2010-07-27 15:11:48 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static int dsi_cio_wait_tx_clk_esc_reset(struct platform_device *dsidev)
|
2010-10-07 17:59:22 +07:00
|
|
|
{
|
2011-10-13 19:26:50 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int t, i;
|
|
|
|
bool in_use[DSI_MAX_NR_LANES];
|
|
|
|
static const u8 offsets_old[] = { 28, 27, 26 };
|
|
|
|
static const u8 offsets_new[] = { 24, 25, 26, 27, 28 };
|
|
|
|
const u8 *offsets;
|
|
|
|
|
|
|
|
if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC))
|
|
|
|
offsets = offsets_old;
|
|
|
|
else
|
|
|
|
offsets = offsets_new;
|
2010-10-07 17:59:22 +07:00
|
|
|
|
2011-10-13 19:26:50 +07:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i)
|
|
|
|
in_use[i] = dsi->lanes[i].function != DSI_LANE_UNUSED;
|
2010-10-07 17:59:22 +07:00
|
|
|
|
|
|
|
t = 100000;
|
|
|
|
while (true) {
|
|
|
|
u32 l;
|
|
|
|
int ok;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
|
2010-10-07 17:59:22 +07:00
|
|
|
|
|
|
|
ok = 0;
|
2011-10-13 19:26:50 +07:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (!in_use[i] || (l & (1 << offsets[i])))
|
2010-10-07 17:59:22 +07:00
|
|
|
ok++;
|
|
|
|
}
|
|
|
|
|
2011-10-13 19:26:50 +07:00
|
|
|
if (ok == dsi->num_lanes_supported)
|
2010-10-07 17:59:22 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (--t == 0) {
|
2011-10-13 19:26:50 +07:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (!in_use[i] || (l & (1 << offsets[i])))
|
2010-10-07 17:59:22 +07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
DSSERR("CIO TXCLKESC%d domain not coming " \
|
|
|
|
"out of reset\n", i);
|
|
|
|
}
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-13 19:12:23 +07:00
|
|
|
/* return bitmask of enabled lanes, lane0 being the lsb */
|
2012-08-14 13:59:22 +07:00
|
|
|
static unsigned dsi_get_lane_mask(struct platform_device *dsidev)
|
2011-06-15 19:21:12 +07:00
|
|
|
{
|
2011-10-13 19:12:23 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
unsigned mask = 0;
|
|
|
|
int i;
|
2011-06-15 19:21:12 +07:00
|
|
|
|
2011-10-13 19:12:23 +07:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (dsi->lanes[i].function != DSI_LANE_UNUSED)
|
|
|
|
mask |= 1 << i;
|
|
|
|
}
|
2011-06-15 19:21:12 +07:00
|
|
|
|
2011-10-13 19:12:23 +07:00
|
|
|
return mask;
|
2011-06-15 19:21:12 +07:00
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static int dsi_cio_init(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-04-15 15:58:41 +07:00
|
|
|
int r;
|
2010-07-28 19:53:38 +07:00
|
|
|
u32 l;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-09-24 18:42:58 +07:00
|
|
|
DSSDBG("DSI CIO init starts");
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
r = dss_dsi_enable_pads(dsi->module_id, dsi_get_lane_mask(dsidev));
|
2011-06-15 19:21:12 +07:00
|
|
|
if (r)
|
|
|
|
return r;
|
2010-07-30 15:57:57 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_enable_scp_clk(dsidev);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
/* A dummy read using the SCP interface to any DSIPHY register is
|
|
|
|
* required after DSIPHY reset to complete the reset of the DSI complex
|
|
|
|
* I/O. */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_read_reg(dsidev, DSI_DSIPHY_CFG5);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) {
|
2011-04-15 15:58:41 +07:00
|
|
|
DSSERR("CIO SCP Clock domain not coming out of reset.\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err_scp_clk_dom;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
r = dsi_set_lane_config(dsidev);
|
2011-10-13 15:22:39 +07:00
|
|
|
if (r)
|
|
|
|
goto err_scp_clk_dom;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-07-28 19:53:38 +07:00
|
|
|
/* set TX STOP MODE timer to maximum for this operation */
|
2011-05-12 18:56:26 +07:00
|
|
|
l = dsi_read_reg(dsidev, DSI_TIMING1);
|
2010-07-28 19:53:38 +07:00
|
|
|
l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
|
|
|
|
l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */
|
|
|
|
l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */
|
|
|
|
l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_TIMING1, l);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->ulps_enabled) {
|
2011-10-13 20:06:43 +07:00
|
|
|
unsigned mask_p;
|
|
|
|
int i;
|
2011-05-16 16:47:08 +07:00
|
|
|
|
2011-04-15 15:58:41 +07:00
|
|
|
DSSDBG("manual ulps exit\n");
|
|
|
|
|
2010-07-28 19:53:38 +07:00
|
|
|
/* ULPS is exited by Mark-1 state for 1ms, followed by
|
|
|
|
* stop state. DSS HW cannot do this via the normal
|
|
|
|
* ULPS exit sequence, as after reset the DSS HW thinks
|
|
|
|
* that we are not in ULPS mode, and refuses to send the
|
|
|
|
* sequence. So we need to send the ULPS exit sequence
|
2011-10-13 20:06:43 +07:00
|
|
|
* manually by setting positive lines high and negative lines
|
|
|
|
* low for 1ms.
|
2010-07-28 19:53:38 +07:00
|
|
|
*/
|
|
|
|
|
2011-10-13 20:06:43 +07:00
|
|
|
mask_p = 0;
|
2011-05-16 16:47:08 +07:00
|
|
|
|
2011-10-13 20:06:43 +07:00
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (dsi->lanes[i].function == DSI_LANE_UNUSED)
|
|
|
|
continue;
|
|
|
|
mask_p |= 1 << i;
|
|
|
|
}
|
2011-05-16 16:47:08 +07:00
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
dsi_cio_enable_lane_override(dsidev, mask_p, 0);
|
2010-07-28 19:53:38 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
2011-04-15 15:58:41 +07:00
|
|
|
goto err_cio_pwr;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) {
|
2011-04-15 15:58:41 +07:00
|
|
|
DSSERR("CIO PWR clock domain not coming out of reset.\n");
|
|
|
|
r = -ENODEV;
|
|
|
|
goto err_cio_pwr_dom;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_if_enable(dsidev, true);
|
|
|
|
dsi_if_enable(dsidev, false);
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
r = dsi_cio_wait_tx_clk_esc_reset(dsidev);
|
2010-10-07 17:59:22 +07:00
|
|
|
if (r)
|
|
|
|
goto err_tx_clk_esc_rst;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->ulps_enabled) {
|
2010-07-28 19:53:38 +07:00
|
|
|
/* Keep Mark-1 state for 1ms (as per DSI spec) */
|
|
|
|
ktime_t wait = ns_to_ktime(1000 * 1000);
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
|
schedule_hrtimeout(&wait, HRTIMER_MODE_REL);
|
|
|
|
|
|
|
|
/* Disable the override. The lanes should be set to Mark-11
|
|
|
|
* state by the HW */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_cio_disable_lane_override(dsidev);
|
2010-07-28 19:53:38 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* FORCE_TX_STOP_MODE_IO */
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_cio_timings(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2011-09-05 18:18:27 +07:00
|
|
|
/* DDR_CLK_ALWAYS_ON */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL,
|
2012-08-13 23:43:39 +07:00
|
|
|
dsi->vm_timings.ddr_clk_always_on, 13, 13);
|
2011-09-05 18:18:27 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->ulps_enabled = false;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("CIO init done\n");
|
2011-04-15 15:58:41 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2010-10-07 17:59:22 +07:00
|
|
|
err_tx_clk_esc_rst:
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */
|
2011-04-15 15:58:41 +07:00
|
|
|
err_cio_pwr_dom:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
|
2011-04-15 15:58:41 +07:00
|
|
|
err_cio_pwr:
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->ulps_enabled)
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_cio_disable_lane_override(dsidev);
|
2011-04-15 15:58:41 +07:00
|
|
|
err_scp_clk_dom:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_disable_scp_clk(dsidev);
|
2012-08-14 13:59:22 +07:00
|
|
|
dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static void dsi_cio_uninit(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2012-03-09 21:07:39 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-05-12 18:56:27 +07:00
|
|
|
|
2011-09-05 18:18:27 +07:00
|
|
|
/* DDR_CLK_ALWAYS_ON */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF);
|
|
|
|
dsi_disable_scp_clk(dsidev);
|
2012-08-14 13:59:22 +07:00
|
|
|
dss_dsi_disable_pads(dsi->module_id, dsi_get_lane_mask(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_config_tx_fifo(struct platform_device *dsidev,
|
|
|
|
enum fifo_size size1, enum fifo_size size2,
|
2009-10-28 16:59:56 +07:00
|
|
|
enum fifo_size size3, enum fifo_size size4)
|
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 r = 0;
|
|
|
|
int add = 0;
|
|
|
|
int i;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->vc[0].fifo_size = size1;
|
|
|
|
dsi->vc[1].fifo_size = size2;
|
|
|
|
dsi->vc[2].fifo_size = size3;
|
|
|
|
dsi->vc[3].fifo_size = size4;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
u8 v;
|
2011-05-12 18:56:27 +07:00
|
|
|
int size = dsi->vc[i].fifo_size;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
if (add + size > 4) {
|
|
|
|
DSSERR("Illegal FIFO configuration\n");
|
|
|
|
BUG();
|
2012-05-18 15:47:02 +07:00
|
|
|
return;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
|
|
|
|
r |= v << (8 * i);
|
|
|
|
/*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
|
|
|
|
add += size;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_config_rx_fifo(struct platform_device *dsidev,
|
|
|
|
enum fifo_size size1, enum fifo_size size2,
|
2009-10-28 16:59:56 +07:00
|
|
|
enum fifo_size size3, enum fifo_size size4)
|
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 r = 0;
|
|
|
|
int add = 0;
|
|
|
|
int i;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->vc[0].fifo_size = size1;
|
|
|
|
dsi->vc[1].fifo_size = size2;
|
|
|
|
dsi->vc[2].fifo_size = size3;
|
|
|
|
dsi->vc[3].fifo_size = size4;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
u8 v;
|
2011-05-12 18:56:27 +07:00
|
|
|
int size = dsi->vc[i].fifo_size;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
if (add + size > 4) {
|
|
|
|
DSSERR("Illegal FIFO configuration\n");
|
|
|
|
BUG();
|
2012-05-18 15:47:02 +07:00
|
|
|
return;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
|
|
|
|
r |= v << (8 * i);
|
|
|
|
/*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
|
|
|
|
add += size;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
u32 r;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_TIMING1);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_TIMING1, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("TX_STOP bit not going down\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel)
|
2011-03-23 16:59:34 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0);
|
2011-03-23 16:59:34 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_packet_sent_handler_vp(void *data, u32 mask)
|
|
|
|
{
|
2011-05-12 18:56:28 +07:00
|
|
|
struct dsi_packet_sent_handler_data *vp_data =
|
|
|
|
(struct dsi_packet_sent_handler_data *) data;
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev);
|
2011-05-12 18:56:27 +07:00
|
|
|
const int channel = dsi->update_channel;
|
|
|
|
u8 bit = dsi->te_enabled ? 30 : 31;
|
2011-03-23 16:59:34 +07:00
|
|
|
|
2011-05-12 18:56:28 +07:00
|
|
|
if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0)
|
|
|
|
complete(vp_data->completion);
|
2011-03-23 16:59:34 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel)
|
2011-03-23 16:59:34 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-05-12 18:56:28 +07:00
|
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
|
|
|
struct dsi_packet_sent_handler_data vp_data = { dsidev, &completion };
|
2011-03-23 16:59:34 +07:00
|
|
|
int r = 0;
|
|
|
|
u8 bit;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
bit = dsi->te_enabled ? 30 : 31;
|
2011-03-23 16:59:34 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
|
2011-05-12 18:56:28 +07:00
|
|
|
&vp_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 16:59:34 +07:00
|
|
|
if (r)
|
|
|
|
goto err0;
|
|
|
|
|
|
|
|
/* Wait for completion only if TE_EN/TE_START is still set */
|
2011-05-12 18:56:26 +07:00
|
|
|
if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) {
|
2011-03-23 16:59:34 +07:00
|
|
|
if (wait_for_completion_timeout(&completion,
|
|
|
|
msecs_to_jiffies(10)) == 0) {
|
|
|
|
DSSERR("Failed to complete previous frame transfer\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
|
2011-05-12 18:56:28 +07:00
|
|
|
&vp_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 16:59:34 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
err1:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp,
|
2011-05-12 18:56:28 +07:00
|
|
|
&vp_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 16:59:34 +07:00
|
|
|
err0:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_packet_sent_handler_l4(void *data, u32 mask)
|
|
|
|
{
|
2011-05-12 18:56:28 +07:00
|
|
|
struct dsi_packet_sent_handler_data *l4_data =
|
|
|
|
(struct dsi_packet_sent_handler_data *) data;
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev);
|
2011-05-12 18:56:27 +07:00
|
|
|
const int channel = dsi->update_channel;
|
2011-03-23 16:59:34 +07:00
|
|
|
|
2011-05-12 18:56:28 +07:00
|
|
|
if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0)
|
|
|
|
complete(l4_data->completion);
|
2011-03-23 16:59:34 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel)
|
2011-03-23 16:59:34 +07:00
|
|
|
{
|
|
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
2011-05-12 18:56:28 +07:00
|
|
|
struct dsi_packet_sent_handler_data l4_data = { dsidev, &completion };
|
|
|
|
int r = 0;
|
2011-03-23 16:59:34 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
|
2011-05-12 18:56:28 +07:00
|
|
|
&l4_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 16:59:34 +07:00
|
|
|
if (r)
|
|
|
|
goto err0;
|
|
|
|
|
|
|
|
/* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */
|
2011-05-12 18:56:26 +07:00
|
|
|
if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) {
|
2011-03-23 16:59:34 +07:00
|
|
|
if (wait_for_completion_timeout(&completion,
|
|
|
|
msecs_to_jiffies(10)) == 0) {
|
|
|
|
DSSERR("Failed to complete previous l4 transfer\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
|
2011-05-12 18:56:28 +07:00
|
|
|
&l4_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 16:59:34 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
err1:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4,
|
2011-05-12 18:56:28 +07:00
|
|
|
&l4_data, DSI_VC_IRQ_PACKET_SENT);
|
2011-03-23 16:59:34 +07:00
|
|
|
err0:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_sync_vc(struct platform_device *dsidev, int channel)
|
2011-03-23 16:59:34 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2011-03-23 16:59:34 +07:00
|
|
|
|
|
|
|
WARN_ON(in_interrupt());
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (!dsi_vc_is_enabled(dsidev, channel))
|
2011-03-23 16:59:34 +07:00
|
|
|
return 0;
|
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
switch (dsi->vc[channel].source) {
|
|
|
|
case DSI_VC_SOURCE_VP:
|
2011-05-12 18:56:26 +07:00
|
|
|
return dsi_sync_vc_vp(dsidev, channel);
|
2011-08-22 13:28:08 +07:00
|
|
|
case DSI_VC_SOURCE_L4:
|
2011-05-12 18:56:26 +07:00
|
|
|
return dsi_sync_vc_l4(dsidev, channel);
|
2011-03-23 16:59:34 +07:00
|
|
|
default:
|
|
|
|
BUG();
|
2012-05-18 15:47:02 +07:00
|
|
|
return -EINVAL;
|
2011-03-23 16:59:34 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_vc_enable(struct platform_device *dsidev, int channel,
|
|
|
|
bool enable)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2010-01-11 21:12:31 +07:00
|
|
|
DSSDBG("dsi_vc_enable channel %d, enable %d\n",
|
|
|
|
channel, enable);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
enable = enable ? 1 : 0;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel),
|
|
|
|
0, enable) != enable) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_vc_initial_config(struct platform_device *dsidev, int channel)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2013-02-22 18:42:59 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 r;
|
|
|
|
|
2012-09-24 18:42:58 +07:00
|
|
|
DSSDBG("Initial config of virtual channel %d", channel);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
if (FLD_GET(r, 15, 15)) /* VC_BUSY */
|
|
|
|
DSSERR("VC(%d) busy when trying to configure it!\n",
|
|
|
|
channel);
|
|
|
|
|
|
|
|
r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
|
|
|
|
r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */
|
|
|
|
r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
|
|
|
|
r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
|
|
|
|
r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
|
|
|
|
r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
|
|
|
|
r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
|
2011-03-22 18:33:36 +07:00
|
|
|
if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH))
|
|
|
|
r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
|
|
|
|
r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r);
|
2013-02-22 18:42:59 +07:00
|
|
|
|
|
|
|
dsi->vc[channel].source = DSI_VC_SOURCE_L4;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
static int dsi_vc_config_source(struct platform_device *dsidev, int channel,
|
|
|
|
enum dsi_vc_source source)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
if (dsi->vc[channel].source == source)
|
2010-04-30 15:24:33 +07:00
|
|
|
return 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-09-24 18:42:58 +07:00
|
|
|
DSSDBG("Source config of virtual channel %d", channel);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_sync_vc(dsidev, channel);
|
2011-03-23 16:59:34 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_enable(dsidev, channel, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-30 15:24:33 +07:00
|
|
|
/* VC_BUSY */
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("vc(%d) busy when trying to config for VP\n", channel);
|
2010-04-30 15:24:33 +07:00
|
|
|
return -EIO;
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
/* SOURCE, 0 = L4, 1 = video port */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), source, 1, 1);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-22 18:33:36 +07:00
|
|
|
/* DCS_CMD_ENABLE */
|
2011-08-22 13:28:08 +07:00
|
|
|
if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
|
|
|
|
bool enable = source == DSI_VC_SOURCE_VP;
|
|
|
|
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 30, 30);
|
|
|
|
}
|
2011-03-22 18:33:36 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_enable(dsidev, channel, 1);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
dsi->vc[channel].source = source;
|
2010-04-30 15:24:33 +07:00
|
|
|
|
|
|
|
return 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel,
|
|
|
|
bool enable)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2012-08-13 23:43:39 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-05-12 18:56:26 +07:00
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2010-01-12 21:00:30 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_enable(dsidev, channel, 0);
|
|
|
|
dsi_if_enable(dsidev, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_enable(dsidev, channel, 1);
|
|
|
|
dsi_if_enable(dsidev, 1);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_force_tx_stop_mode_io(dsidev);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
|
|
|
/* start the DDR clock by sending a NULL packet */
|
2012-08-13 23:43:39 +07:00
|
|
|
if (dsi->vm_timings.ddr_clk_always_on && enable)
|
2011-09-05 18:18:27 +07:00
|
|
|
dsi_vc_send_null(dssdev, channel);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2010-01-12 21:00:30 +07:00
|
|
|
EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 val;
|
2011-05-12 18:56:26 +07:00
|
|
|
val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
|
|
|
|
(val >> 0) & 0xff,
|
|
|
|
(val >> 8) & 0xff,
|
|
|
|
(val >> 16) & 0xff,
|
|
|
|
(val >> 24) & 0xff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dsi_show_rx_ack_with_err(u16 err)
|
|
|
|
{
|
|
|
|
DSSERR("\tACK with ERROR (%#x):\n", err);
|
|
|
|
if (err & (1 << 0))
|
|
|
|
DSSERR("\t\tSoT Error\n");
|
|
|
|
if (err & (1 << 1))
|
|
|
|
DSSERR("\t\tSoT Sync Error\n");
|
|
|
|
if (err & (1 << 2))
|
|
|
|
DSSERR("\t\tEoT Sync Error\n");
|
|
|
|
if (err & (1 << 3))
|
|
|
|
DSSERR("\t\tEscape Mode Entry Command Error\n");
|
|
|
|
if (err & (1 << 4))
|
|
|
|
DSSERR("\t\tLP Transmit Sync Error\n");
|
|
|
|
if (err & (1 << 5))
|
|
|
|
DSSERR("\t\tHS Receive Timeout Error\n");
|
|
|
|
if (err & (1 << 6))
|
|
|
|
DSSERR("\t\tFalse Control Error\n");
|
|
|
|
if (err & (1 << 7))
|
|
|
|
DSSERR("\t\t(reserved7)\n");
|
|
|
|
if (err & (1 << 8))
|
|
|
|
DSSERR("\t\tECC Error, single-bit (corrected)\n");
|
|
|
|
if (err & (1 << 9))
|
|
|
|
DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
|
|
|
|
if (err & (1 << 10))
|
|
|
|
DSSERR("\t\tChecksum Error\n");
|
|
|
|
if (err & (1 << 11))
|
|
|
|
DSSERR("\t\tData type not recognized\n");
|
|
|
|
if (err & (1 << 12))
|
|
|
|
DSSERR("\t\tInvalid VC ID\n");
|
|
|
|
if (err & (1 << 13))
|
|
|
|
DSSERR("\t\tInvalid Transmission Length\n");
|
|
|
|
if (err & (1 << 14))
|
|
|
|
DSSERR("\t\t(reserved14)\n");
|
|
|
|
if (err & (1 << 15))
|
|
|
|
DSSERR("\t\tDSI Protocol Violation\n");
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev,
|
|
|
|
int channel)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
2011-05-12 18:56:26 +07:00
|
|
|
while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 val;
|
|
|
|
u8 dt;
|
2011-05-12 18:56:26 +07:00
|
|
|
val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
|
2010-03-16 21:19:06 +07:00
|
|
|
DSSERR("\trawval %#08x\n", val);
|
2009-10-28 16:59:56 +07:00
|
|
|
dt = FLD_GET(val, 5, 0);
|
2011-08-25 19:55:03 +07:00
|
|
|
if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
|
2009-10-28 16:59:56 +07:00
|
|
|
u16 err = FLD_GET(val, 23, 8);
|
|
|
|
dsi_show_rx_ack_with_err(err);
|
2011-08-25 19:55:03 +07:00
|
|
|
} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) {
|
2010-03-16 21:19:06 +07:00
|
|
|
DSSERR("\tDCS short response, 1 byte: %#x\n",
|
2009-10-28 16:59:56 +07:00
|
|
|
FLD_GET(val, 23, 8));
|
2011-08-25 19:55:03 +07:00
|
|
|
} else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) {
|
2010-03-16 21:19:06 +07:00
|
|
|
DSSERR("\tDCS short response, 2 byte: %#x\n",
|
2009-10-28 16:59:56 +07:00
|
|
|
FLD_GET(val, 23, 8));
|
2011-08-25 19:55:03 +07:00
|
|
|
} else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) {
|
2010-03-16 21:19:06 +07:00
|
|
|
DSSERR("\tDCS long response, len %d\n",
|
2009-10-28 16:59:56 +07:00
|
|
|
FLD_GET(val, 23, 8));
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_flush_long_data(dsidev, channel);
|
2009-10-28 16:59:56 +07:00
|
|
|
} else {
|
|
|
|
DSSERR("\tunknown datatype 0x%02x\n", dt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_vc_send_bta(struct platform_device *dsidev, int channel)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
if (dsi->debug_write || dsi->debug_read)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("dsi_vc_send_bta %d\n", channel);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
|
|
|
if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_flush_receive_data(dsidev, channel);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-10-12 14:13:14 +07:00
|
|
|
/* flush posted write */
|
|
|
|
dsi_read_reg(dsidev, DSI_VC_CTRL(channel));
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2011-03-02 19:48:41 +07:00
|
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
2009-10-28 16:59:56 +07:00
|
|
|
int r = 0;
|
|
|
|
u32 err;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler,
|
2011-03-02 19:48:41 +07:00
|
|
|
&completion, DSI_VC_IRQ_BTA);
|
|
|
|
if (r)
|
|
|
|
goto err0;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_register_isr(dsidev, dsi_completion_handler, &completion,
|
2010-10-08 20:15:25 +07:00
|
|
|
DSI_IRQ_ERROR_MASK);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
2011-03-02 19:48:41 +07:00
|
|
|
goto err1;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_vc_send_bta(dsidev, channel);
|
2010-10-08 20:15:25 +07:00
|
|
|
if (r)
|
|
|
|
goto err2;
|
|
|
|
|
2011-03-02 19:48:41 +07:00
|
|
|
if (wait_for_completion_timeout(&completion,
|
2009-10-28 16:59:56 +07:00
|
|
|
msecs_to_jiffies(500)) == 0) {
|
|
|
|
DSSERR("Failed to receive BTA\n");
|
|
|
|
r = -EIO;
|
2010-10-08 20:15:25 +07:00
|
|
|
goto err2;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
err = dsi_get_errors(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (err) {
|
|
|
|
DSSERR("Error while sending BTA: %x\n", err);
|
|
|
|
r = -EIO;
|
2010-10-08 20:15:25 +07:00
|
|
|
goto err2;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2010-10-08 20:15:25 +07:00
|
|
|
err2:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr(dsidev, dsi_completion_handler, &completion,
|
2010-10-08 20:15:25 +07:00
|
|
|
DSI_IRQ_ERROR_MASK);
|
2011-03-02 19:48:41 +07:00
|
|
|
err1:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler,
|
2011-03-02 19:48:41 +07:00
|
|
|
&completion, DSI_VC_IRQ_BTA);
|
|
|
|
err0:
|
2009-10-28 16:59:56 +07:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_send_bta_sync);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline void dsi_vc_write_long_header(struct platform_device *dsidev,
|
|
|
|
int channel, u8 data_type, u16 len, u8 ecc)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 val;
|
|
|
|
u8 data_id;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
data_id = data_type | dsi->vc[channel].vc_id << 6;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
|
|
|
|
FLD_VAL(ecc, 31, 24);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static inline void dsi_vc_write_long_payload(struct platform_device *dsidev,
|
|
|
|
int channel, u8 b1, u8 b2, u8 b3, u8 b4)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0;
|
|
|
|
|
|
|
|
/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
|
|
|
|
b1, b2, b3, b4, val); */
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_vc_send_long(struct platform_device *dsidev, int channel,
|
|
|
|
u8 data_type, u8 *data, u16 len, u8 ecc)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
/*u32 val; */
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int i;
|
|
|
|
u8 *p;
|
|
|
|
int r = 0;
|
|
|
|
u8 b1, b2, b3, b4;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_write)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("dsi_vc_send_long, %d bytes\n", len);
|
|
|
|
|
|
|
|
/* len + header */
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->vc[channel].fifo_size * 32 * 4 < len + 4) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("unable to send long packet: packet too long.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
p = data;
|
|
|
|
for (i = 0; i < len >> 2; i++) {
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_write)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("\tsending full packet %d\n", i);
|
|
|
|
|
|
|
|
b1 = *p++;
|
|
|
|
b2 = *p++;
|
|
|
|
b3 = *p++;
|
|
|
|
b4 = *p++;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
i = len % 4;
|
|
|
|
if (i) {
|
|
|
|
b1 = 0; b2 = 0; b3 = 0;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_write)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("\tsending remainder bytes %d\n", i);
|
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
case 3:
|
|
|
|
b1 = *p++;
|
|
|
|
b2 = *p++;
|
|
|
|
b3 = *p++;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
b1 = *p++;
|
|
|
|
b2 = *p++;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
b1 = *p++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_vc_send_short(struct platform_device *dsidev, int channel,
|
|
|
|
u8 data_type, u16 data, u8 ecc)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 r;
|
|
|
|
u8 data_id;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_write)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
|
|
|
|
channel,
|
|
|
|
data_type, data & 0xff, (data >> 8) & 0xff);
|
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_L4);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("ERROR FIFO FULL, aborting transfer\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
data_id = data_type | dsi->vc[channel].vc_id << 6;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
r = (data_id << 0) | (data << 8) | (ecc << 24);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
|
2011-09-05 18:31:08 +07:00
|
|
|
return dsi_vc_send_long(dsidev, channel, MIPI_DSI_NULL_PACKET, NULL,
|
|
|
|
0, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_send_null);
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static int dsi_vc_write_nosync_common(struct platform_device *dsidev,
|
2011-08-25 20:05:58 +07:00
|
|
|
int channel, u8 *data, int len, enum dss_dsi_content_type type)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2011-08-25 20:05:58 +07:00
|
|
|
if (len == 0) {
|
|
|
|
BUG_ON(type == DSS_DSI_CONTENT_DCS);
|
2011-08-25 19:55:03 +07:00
|
|
|
r = dsi_vc_send_short(dsidev, channel,
|
2011-08-25 20:05:58 +07:00
|
|
|
MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM, 0, 0);
|
|
|
|
} else if (len == 1) {
|
|
|
|
r = dsi_vc_send_short(dsidev, channel,
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM :
|
2011-08-25 19:55:03 +07:00
|
|
|
MIPI_DSI_DCS_SHORT_WRITE, data[0], 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
} else if (len == 2) {
|
2011-08-25 19:55:03 +07:00
|
|
|
r = dsi_vc_send_short(dsidev, channel,
|
2011-08-25 20:05:58 +07:00
|
|
|
type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM :
|
2011-08-25 19:55:03 +07:00
|
|
|
MIPI_DSI_DCS_SHORT_WRITE_PARAM,
|
2009-10-28 16:59:56 +07:00
|
|
|
data[0] | (data[1] << 8), 0);
|
|
|
|
} else {
|
2011-08-25 20:05:58 +07:00
|
|
|
r = dsi_vc_send_long(dsidev, channel,
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_GENERIC_LONG_WRITE :
|
|
|
|
MIPI_DSI_DCS_LONG_WRITE, data, len, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
2011-08-25 20:05:58 +07:00
|
|
|
|
|
|
|
int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u8 *data, int len)
|
|
|
|
{
|
2012-08-14 13:59:22 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
|
|
|
|
return dsi_vc_write_nosync_common(dsidev, channel, data, len,
|
2011-08-25 20:05:58 +07:00
|
|
|
DSS_DSI_CONTENT_DCS);
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
EXPORT_SYMBOL(dsi_vc_dcs_write_nosync);
|
|
|
|
|
2011-08-25 20:05:58 +07:00
|
|
|
int dsi_vc_generic_write_nosync(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u8 *data, int len)
|
|
|
|
{
|
2012-08-14 13:59:22 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
|
|
|
|
return dsi_vc_write_nosync_common(dsidev, channel, data, len,
|
2011-08-25 20:05:58 +07:00
|
|
|
DSS_DSI_CONTENT_GENERIC);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_write_nosync);
|
|
|
|
|
|
|
|
static int dsi_vc_write_common(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u8 *data, int len, enum dss_dsi_content_type type)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int r;
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
r = dsi_vc_write_nosync_common(dsidev, channel, data, len, type);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
2010-02-26 16:32:56 +07:00
|
|
|
goto err;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
r = dsi_vc_send_bta_sync(dssdev, channel);
|
2010-02-26 16:32:56 +07:00
|
|
|
if (r)
|
|
|
|
goto err;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
|
|
|
if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) {
|
2010-04-09 17:20:57 +07:00
|
|
|
DSSERR("rx fifo not empty after write, dumping data:\n");
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_flush_receive_data(dsidev, channel);
|
2010-04-09 17:20:57 +07:00
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2010-02-26 16:32:56 +07:00
|
|
|
return 0;
|
|
|
|
err:
|
2011-08-25 20:05:58 +07:00
|
|
|
DSSERR("dsi_vc_write_common(ch %d, cmd 0x%02x, len %d) failed\n",
|
2010-02-26 16:32:56 +07:00
|
|
|
channel, data[0], len);
|
2009-10-28 16:59:56 +07:00
|
|
|
return r;
|
|
|
|
}
|
2011-08-25 20:05:58 +07:00
|
|
|
|
|
|
|
int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data,
|
|
|
|
int len)
|
|
|
|
{
|
|
|
|
return dsi_vc_write_common(dssdev, channel, data, len,
|
|
|
|
DSS_DSI_CONTENT_DCS);
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
EXPORT_SYMBOL(dsi_vc_dcs_write);
|
|
|
|
|
2011-08-25 20:05:58 +07:00
|
|
|
int dsi_vc_generic_write(struct omap_dss_device *dssdev, int channel, u8 *data,
|
|
|
|
int len)
|
|
|
|
{
|
|
|
|
return dsi_vc_write_common(dssdev, channel, data, len,
|
|
|
|
DSS_DSI_CONTENT_GENERIC);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_write);
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
int dsi_vc_dcs_write_0(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd)
|
2009-12-16 19:53:15 +07:00
|
|
|
{
|
2011-05-12 18:56:24 +07:00
|
|
|
return dsi_vc_dcs_write(dssdev, channel, &dcs_cmd, 1);
|
2009-12-16 19:53:15 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_dcs_write_0);
|
|
|
|
|
2011-08-25 20:05:58 +07:00
|
|
|
int dsi_vc_generic_write_0(struct omap_dss_device *dssdev, int channel)
|
|
|
|
{
|
|
|
|
return dsi_vc_generic_write(dssdev, channel, NULL, 0);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_write_0);
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
|
|
|
|
u8 param)
|
2009-12-16 19:53:15 +07:00
|
|
|
{
|
|
|
|
u8 buf[2];
|
|
|
|
buf[0] = dcs_cmd;
|
|
|
|
buf[1] = param;
|
2011-05-12 18:56:24 +07:00
|
|
|
return dsi_vc_dcs_write(dssdev, channel, buf, 2);
|
2009-12-16 19:53:15 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_dcs_write_1);
|
|
|
|
|
2011-08-25 20:05:58 +07:00
|
|
|
int dsi_vc_generic_write_1(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u8 param)
|
|
|
|
{
|
|
|
|
return dsi_vc_generic_write(dssdev, channel, ¶m, 1);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_write_1);
|
|
|
|
|
|
|
|
int dsi_vc_generic_write_2(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u8 param1, u8 param2)
|
|
|
|
{
|
|
|
|
u8 buf[2];
|
|
|
|
buf[0] = param1;
|
|
|
|
buf[1] = param2;
|
|
|
|
return dsi_vc_generic_write(dssdev, channel, buf, 2);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_write_2);
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static int dsi_vc_dcs_send_read_request(struct platform_device *dsidev,
|
2011-08-30 17:18:23 +07:00
|
|
|
int channel, u8 dcs_cmd)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_read)
|
2011-08-30 17:18:23 +07:00
|
|
|
DSSDBG("dsi_vc_dcs_send_read_request(ch%d, dcs_cmd %x)\n",
|
|
|
|
channel, dcs_cmd);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-25 19:55:03 +07:00
|
|
|
r = dsi_vc_send_short(dsidev, channel, MIPI_DSI_DCS_READ, dcs_cmd, 0);
|
2011-08-30 17:18:23 +07:00
|
|
|
if (r) {
|
|
|
|
DSSERR("dsi_vc_dcs_send_read_request(ch %d, cmd 0x%02x)"
|
|
|
|
" failed\n", channel, dcs_cmd);
|
|
|
|
return r;
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-30 17:18:23 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static int dsi_vc_generic_send_read_request(struct platform_device *dsidev,
|
2011-08-30 17:37:39 +07:00
|
|
|
int channel, u8 *reqdata, int reqlen)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
u16 data;
|
|
|
|
u8 data_type;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (dsi->debug_read)
|
|
|
|
DSSDBG("dsi_vc_generic_send_read_request(ch %d, reqlen %d)\n",
|
|
|
|
channel, reqlen);
|
|
|
|
|
|
|
|
if (reqlen == 0) {
|
|
|
|
data_type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
|
|
|
|
data = 0;
|
|
|
|
} else if (reqlen == 1) {
|
|
|
|
data_type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
|
|
|
|
data = reqdata[0];
|
|
|
|
} else if (reqlen == 2) {
|
|
|
|
data_type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
|
|
|
|
data = reqdata[0] | (reqdata[1] << 8);
|
|
|
|
} else {
|
|
|
|
BUG();
|
2012-05-18 15:47:02 +07:00
|
|
|
return -EINVAL;
|
2011-08-30 17:37:39 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
r = dsi_vc_send_short(dsidev, channel, data_type, data, 0);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("dsi_vc_generic_send_read_request(ch %d, reqlen %d)"
|
|
|
|
" failed\n", channel, reqlen);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsi_vc_read_rx_fifo(struct platform_device *dsidev, int channel,
|
|
|
|
u8 *buf, int buflen, enum dss_dsi_content_type type)
|
2011-08-30 17:18:23 +07:00
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
u32 val;
|
|
|
|
u8 dt;
|
|
|
|
int r;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* RX_FIFO_NOT_EMPTY */
|
2011-05-12 18:56:26 +07:00
|
|
|
if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("RX fifo empty when trying to read.\n");
|
2010-02-26 16:32:56 +07:00
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel));
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_read)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("\theader: %08x\n", val);
|
|
|
|
dt = FLD_GET(val, 5, 0);
|
2011-08-25 19:55:03 +07:00
|
|
|
if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) {
|
2009-10-28 16:59:56 +07:00
|
|
|
u16 err = FLD_GET(val, 23, 8);
|
|
|
|
dsi_show_rx_ack_with_err(err);
|
2010-02-26 16:32:56 +07:00
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-30 17:37:39 +07:00
|
|
|
} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE :
|
|
|
|
MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
u8 data = FLD_GET(val, 15, 8);
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_read)
|
2011-08-30 17:37:39 +07:00
|
|
|
DSSDBG("\t%s short response, 1 byte: %02x\n",
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
|
|
|
|
"DCS", data);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-02-26 16:32:56 +07:00
|
|
|
if (buflen < 1) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
buf[0] = data;
|
|
|
|
|
|
|
|
return 1;
|
2011-08-30 17:37:39 +07:00
|
|
|
} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE :
|
|
|
|
MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
u16 data = FLD_GET(val, 23, 8);
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_read)
|
2011-08-30 17:37:39 +07:00
|
|
|
DSSDBG("\t%s short response, 2 byte: %04x\n",
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
|
|
|
|
"DCS", data);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-02-26 16:32:56 +07:00
|
|
|
if (buflen < 2) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
buf[0] = data & 0xff;
|
|
|
|
buf[1] = (data >> 8) & 0xff;
|
|
|
|
|
|
|
|
return 2;
|
2011-08-30 17:37:39 +07:00
|
|
|
} else if (dt == (type == DSS_DSI_CONTENT_GENERIC ?
|
|
|
|
MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE :
|
|
|
|
MIPI_DSI_RX_DCS_LONG_READ_RESPONSE)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
int w;
|
|
|
|
int len = FLD_GET(val, 23, 8);
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_read)
|
2011-08-30 17:37:39 +07:00
|
|
|
DSSDBG("\t%s long response, len %d\n",
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" :
|
|
|
|
"DCS", len);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-02-26 16:32:56 +07:00
|
|
|
if (len > buflen) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* two byte checksum ends the packet, not included in len */
|
|
|
|
for (w = 0; w < len + 2;) {
|
|
|
|
int b;
|
2011-05-12 18:56:26 +07:00
|
|
|
val = dsi_read_reg(dsidev,
|
|
|
|
DSI_VC_SHORT_PACKET_HEADER(channel));
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->debug_read)
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("\t\t%02x %02x %02x %02x\n",
|
|
|
|
(val >> 0) & 0xff,
|
|
|
|
(val >> 8) & 0xff,
|
|
|
|
(val >> 16) & 0xff,
|
|
|
|
(val >> 24) & 0xff);
|
|
|
|
|
|
|
|
for (b = 0; b < 4; ++b) {
|
|
|
|
if (w < len)
|
|
|
|
buf[w] = (val >> (b * 8)) & 0xff;
|
|
|
|
/* we discard the 2 byte checksum */
|
|
|
|
++w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
} else {
|
|
|
|
DSSERR("\tunknown datatype 0x%02x\n", dt);
|
2010-02-26 16:32:56 +07:00
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2010-02-26 16:32:56 +07:00
|
|
|
|
|
|
|
err:
|
2011-08-30 17:37:39 +07:00
|
|
|
DSSERR("dsi_vc_read_rx_fifo(ch %d type %s) failed\n", channel,
|
|
|
|
type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS");
|
2011-08-30 17:18:23 +07:00
|
|
|
|
2010-02-26 16:32:56 +07:00
|
|
|
return r;
|
2011-08-30 17:18:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
|
|
|
|
u8 *buf, int buflen)
|
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
int r;
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
r = dsi_vc_dcs_send_read_request(dsidev, channel, dcs_cmd);
|
2011-08-30 17:18:23 +07:00
|
|
|
if (r)
|
|
|
|
goto err;
|
2010-02-26 16:32:56 +07:00
|
|
|
|
2011-08-30 17:18:23 +07:00
|
|
|
r = dsi_vc_send_bta_sync(dssdev, channel);
|
|
|
|
if (r)
|
|
|
|
goto err;
|
|
|
|
|
2011-08-30 17:37:39 +07:00
|
|
|
r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
|
|
|
|
DSS_DSI_CONTENT_DCS);
|
2011-08-30 17:18:23 +07:00
|
|
|
if (r < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
if (r != buflen) {
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
DSSERR("dsi_vc_dcs_read(ch %d, cmd 0x%02x) failed\n", channel, dcs_cmd);
|
|
|
|
return r;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_dcs_read);
|
|
|
|
|
2011-08-30 17:37:39 +07:00
|
|
|
static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u8 *reqdata, int reqlen, u8 *buf, int buflen)
|
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
int r;
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
r = dsi_vc_generic_send_read_request(dsidev, channel, reqdata, reqlen);
|
2011-08-30 17:37:39 +07:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = dsi_vc_send_bta_sync(dssdev, channel);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = dsi_vc_read_rx_fifo(dsidev, channel, buf, buflen,
|
|
|
|
DSS_DSI_CONTENT_GENERIC);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (r != buflen) {
|
|
|
|
r = -EIO;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dsi_vc_generic_read_0(struct omap_dss_device *dssdev, int channel, u8 *buf,
|
|
|
|
int buflen)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = dsi_vc_generic_read(dssdev, channel, NULL, 0, buf, buflen);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("dsi_vc_generic_read_0(ch %d) failed\n", channel);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_read_0);
|
|
|
|
|
|
|
|
int dsi_vc_generic_read_1(struct omap_dss_device *dssdev, int channel, u8 param,
|
|
|
|
u8 *buf, int buflen)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = dsi_vc_generic_read(dssdev, channel, ¶m, 1, buf, buflen);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("dsi_vc_generic_read_1(ch %d) failed\n", channel);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_read_1);
|
|
|
|
|
|
|
|
int dsi_vc_generic_read_2(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u8 param1, u8 param2, u8 *buf, int buflen)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
u8 reqdata[2];
|
|
|
|
|
|
|
|
reqdata[0] = param1;
|
|
|
|
reqdata[1] = param2;
|
|
|
|
|
|
|
|
r = dsi_vc_generic_read(dssdev, channel, reqdata, 2, buf, buflen);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("dsi_vc_generic_read_2(ch %d) failed\n", channel);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_generic_read_2);
|
|
|
|
|
2011-05-12 18:56:24 +07:00
|
|
|
int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel,
|
|
|
|
u16 len)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
|
2011-08-25 19:55:03 +07:00
|
|
|
return dsi_vc_send_short(dsidev, channel,
|
|
|
|
MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static int dsi_enter_ulps(struct platform_device *dsidev)
|
2010-07-28 19:53:38 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2010-07-28 19:53:38 +07:00
|
|
|
DECLARE_COMPLETION_ONSTACK(completion);
|
2011-10-13 20:18:52 +07:00
|
|
|
int r, i;
|
|
|
|
unsigned mask;
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2012-09-24 18:42:58 +07:00
|
|
|
DSSDBG("Entering ULPS");
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
WARN_ON(dsi->ulps_enabled);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->ulps_enabled)
|
2010-07-28 19:53:38 +07:00
|
|
|
return 0;
|
|
|
|
|
2011-10-13 23:22:43 +07:00
|
|
|
/* DDR_CLK_ALWAYS_ON */
|
2011-05-12 18:56:26 +07:00
|
|
|
if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) {
|
2011-10-13 23:22:43 +07:00
|
|
|
dsi_if_enable(dsidev, 0);
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
|
|
|
|
dsi_if_enable(dsidev, 1);
|
2010-07-28 19:53:38 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_sync_vc(dsidev, 0);
|
|
|
|
dsi_sync_vc(dsidev, 1);
|
|
|
|
dsi_sync_vc(dsidev, 2);
|
|
|
|
dsi_sync_vc(dsidev, 3);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_force_tx_stop_mode_io(dsidev);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_enable(dsidev, 0, false);
|
|
|
|
dsi_vc_enable(dsidev, 1, false);
|
|
|
|
dsi_vc_enable(dsidev, 2, false);
|
|
|
|
dsi_vc_enable(dsidev, 3, false);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */
|
2010-07-28 19:53:38 +07:00
|
|
|
DSSERR("HS busy when enabling ULPS\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */
|
2010-07-28 19:53:38 +07:00
|
|
|
DSSERR("LP busy when enabling ULPS\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion,
|
2010-07-28 19:53:38 +07:00
|
|
|
DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2011-10-13 20:18:52 +07:00
|
|
|
mask = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < dsi->num_lanes_supported; ++i) {
|
|
|
|
if (dsi->lanes[i].function == DSI_LANE_UNUSED)
|
|
|
|
continue;
|
|
|
|
mask |= 1 << i;
|
|
|
|
}
|
2010-07-28 19:53:38 +07:00
|
|
|
/* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */
|
|
|
|
/* LANEx_ULPS_SIG2 */
|
2011-10-13 20:18:52 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, mask, 9, 5);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-10-12 14:10:21 +07:00
|
|
|
/* flush posted write and wait for SCP interface to finish the write */
|
|
|
|
dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
|
|
|
if (wait_for_completion_timeout(&completion,
|
|
|
|
msecs_to_jiffies(1000)) == 0) {
|
|
|
|
DSSERR("ULPS enable timeout\n");
|
|
|
|
r = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
|
2010-07-28 19:53:38 +07:00
|
|
|
DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
|
|
|
|
|
2011-05-31 20:55:47 +07:00
|
|
|
/* Reset LANEx_ULPS_SIG2 */
|
2011-10-13 20:18:52 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, 0, 9, 5);
|
2011-05-31 20:55:47 +07:00
|
|
|
|
2011-10-12 14:10:21 +07:00
|
|
|
/* flush posted write and wait for SCP interface to finish the write */
|
|
|
|
dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG2);
|
2011-05-31 20:55:47 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_if_enable(dsidev, false);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->ulps_enabled = true;
|
2010-07-28 19:53:38 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
|
2010-07-28 19:53:38 +07:00
|
|
|
DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_set_lp_rx_timeout(struct platform_device *dsidev,
|
|
|
|
unsigned ticks, bool x4, bool x16)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 14:40:12 +07:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
/* ticks in DSI_FCK */
|
2011-05-12 18:56:26 +07:00
|
|
|
fck = dsi_fclk_rate(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_TIMING2);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
|
2010-04-12 14:40:12 +07:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */
|
|
|
|
r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_TIMING2, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
|
|
|
|
|
|
|
|
DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks,
|
|
|
|
bool x8, bool x16)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 14:40:12 +07:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
|
|
|
|
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* ticks in DSI_FCK */
|
2011-05-12 18:56:26 +07:00
|
|
|
fck = dsi_fclk_rate(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_TIMING1);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
|
2010-04-12 14:40:12 +07:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */
|
|
|
|
r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_TIMING1, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1);
|
|
|
|
|
|
|
|
DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x8 ? " x8" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_set_stop_state_counter(struct platform_device *dsidev,
|
|
|
|
unsigned ticks, bool x4, bool x16)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 14:40:12 +07:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
/* ticks in DSI_FCK */
|
2011-05-12 18:56:26 +07:00
|
|
|
fck = dsi_fclk_rate(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_TIMING1);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
|
2010-04-12 14:40:12 +07:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */
|
|
|
|
r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_TIMING1, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
|
|
|
|
|
|
|
|
DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
|
|
|
|
unsigned ticks, bool x4, bool x16)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
unsigned long fck;
|
2010-04-12 14:40:12 +07:00
|
|
|
unsigned long total_ticks;
|
|
|
|
u32 r;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
BUG_ON(ticks > 0x1fff);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
/* ticks in TxByteClkHS */
|
2011-05-12 18:56:26 +07:00
|
|
|
fck = dsi_get_txbyteclkhs(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_TIMING2);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
|
2010-04-12 14:40:12 +07:00
|
|
|
r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */
|
|
|
|
r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_TIMING2, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-04-12 14:40:12 +07:00
|
|
|
total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1);
|
|
|
|
|
|
|
|
DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n",
|
|
|
|
total_ticks,
|
|
|
|
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
|
|
|
|
(total_ticks * 1000) / (fck / 1000 / 1000));
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static void dsi_config_vp_num_line_buffers(struct platform_device *dsidev)
|
2011-09-05 18:18:27 +07:00
|
|
|
{
|
2012-08-16 19:32:00 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-09-05 18:18:27 +07:00
|
|
|
int num_line_buffers;
|
|
|
|
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2012-08-10 16:31:33 +07:00
|
|
|
int bpp = dsi_get_pixel_size(dsi->pix_fmt);
|
2012-08-13 15:47:30 +07:00
|
|
|
struct omap_video_timings *timings = &dsi->timings;
|
2011-09-05 18:18:27 +07:00
|
|
|
/*
|
|
|
|
* Don't use line buffers if width is greater than the video
|
|
|
|
* port's line buffer size
|
|
|
|
*/
|
2013-03-05 15:37:02 +07:00
|
|
|
if (dsi->line_buffer_size <= timings->x_res * bpp / 8)
|
2011-09-05 18:18:27 +07:00
|
|
|
num_line_buffers = 0;
|
|
|
|
else
|
|
|
|
num_line_buffers = 2;
|
|
|
|
} else {
|
|
|
|
/* Use maximum number of line buffers in command mode */
|
|
|
|
num_line_buffers = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* LINE_BUFFER */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_CTRL, num_line_buffers, 13, 12);
|
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static void dsi_config_vp_sync_events(struct platform_device *dsidev)
|
2011-09-05 18:18:27 +07:00
|
|
|
{
|
2012-08-13 23:43:39 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2013-03-05 21:29:36 +07:00
|
|
|
bool sync_end;
|
2011-09-05 18:18:27 +07:00
|
|
|
u32 r;
|
|
|
|
|
2013-03-05 21:29:36 +07:00
|
|
|
if (dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE)
|
|
|
|
sync_end = true;
|
|
|
|
else
|
|
|
|
sync_end = false;
|
|
|
|
|
2011-09-05 18:18:27 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_CTRL);
|
2012-06-26 14:08:31 +07:00
|
|
|
r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */
|
|
|
|
r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */
|
|
|
|
r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */
|
2011-09-05 18:18:27 +07:00
|
|
|
r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */
|
2013-03-05 21:29:36 +07:00
|
|
|
r = FLD_MOD(r, sync_end, 16, 16); /* VP_VSYNC_END */
|
2011-09-05 18:18:27 +07:00
|
|
|
r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */
|
2013-03-05 21:29:36 +07:00
|
|
|
r = FLD_MOD(r, sync_end, 18, 18); /* VP_HSYNC_END */
|
2011-09-05 18:18:27 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_CTRL, r);
|
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static void dsi_config_blanking_modes(struct platform_device *dsidev)
|
2011-09-05 18:18:27 +07:00
|
|
|
{
|
2012-08-13 23:43:39 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int blanking_mode = dsi->vm_timings.blanking_mode;
|
|
|
|
int hfp_blanking_mode = dsi->vm_timings.hfp_blanking_mode;
|
|
|
|
int hbp_blanking_mode = dsi->vm_timings.hbp_blanking_mode;
|
|
|
|
int hsa_blanking_mode = dsi->vm_timings.hsa_blanking_mode;
|
2011-09-05 18:18:27 +07:00
|
|
|
u32 r;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 0 = TX FIFO packets sent or LPS in corresponding blanking periods
|
|
|
|
* 1 = Long blanking packets are sent in corresponding blanking periods
|
|
|
|
*/
|
|
|
|
r = dsi_read_reg(dsidev, DSI_CTRL);
|
|
|
|
r = FLD_MOD(r, blanking_mode, 20, 20); /* BLANKING_MODE */
|
|
|
|
r = FLD_MOD(r, hfp_blanking_mode, 21, 21); /* HFP_BLANKING */
|
|
|
|
r = FLD_MOD(r, hbp_blanking_mode, 22, 22); /* HBP_BLANKING */
|
|
|
|
r = FLD_MOD(r, hsa_blanking_mode, 23, 23); /* HSA_BLANKING */
|
|
|
|
dsi_write_reg(dsidev, DSI_CTRL, r);
|
|
|
|
}
|
|
|
|
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 13:02:18 +07:00
|
|
|
/*
|
|
|
|
* According to section 'HS Command Mode Interleaving' in OMAP TRM, Scenario 3
|
|
|
|
* results in maximum transition time for data and clock lanes to enter and
|
|
|
|
* exit HS mode. Hence, this is the scenario where the least amount of command
|
|
|
|
* mode data can be interleaved. We program the minimum amount of TXBYTECLKHS
|
|
|
|
* clock cycles that can be used to interleave command mode data in HS so that
|
|
|
|
* all scenarios are satisfied.
|
|
|
|
*/
|
|
|
|
static int dsi_compute_interleave_hs(int blank, bool ddr_alwon, int enter_hs,
|
|
|
|
int exit_hs, int exiths_clk, int ddr_pre, int ddr_post)
|
|
|
|
{
|
|
|
|
int transition;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If DDR_CLK_ALWAYS_ON is set, we need to consider HS mode transition
|
|
|
|
* time of data lanes only, if it isn't set, we need to consider HS
|
|
|
|
* transition time of both data and clock lanes. HS transition time
|
|
|
|
* of Scenario 3 is considered.
|
|
|
|
*/
|
|
|
|
if (ddr_alwon) {
|
|
|
|
transition = enter_hs + exit_hs + max(enter_hs, 2) + 1;
|
|
|
|
} else {
|
|
|
|
int trans1, trans2;
|
|
|
|
trans1 = ddr_pre + enter_hs + exit_hs + max(enter_hs, 2) + 1;
|
|
|
|
trans2 = ddr_pre + enter_hs + exiths_clk + ddr_post + ddr_pre +
|
|
|
|
enter_hs + 1;
|
|
|
|
transition = max(trans1, trans2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return blank > transition ? blank - transition : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* According to section 'LP Command Mode Interleaving' in OMAP TRM, Scenario 1
|
|
|
|
* results in maximum transition time for data lanes to enter and exit LP mode.
|
|
|
|
* Hence, this is the scenario where the least amount of command mode data can
|
|
|
|
* be interleaved. We program the minimum amount of bytes that can be
|
|
|
|
* interleaved in LP so that all scenarios are satisfied.
|
|
|
|
*/
|
|
|
|
static int dsi_compute_interleave_lp(int blank, int enter_hs, int exit_hs,
|
|
|
|
int lp_clk_div, int tdsi_fclk)
|
|
|
|
{
|
|
|
|
int trans_lp; /* time required for a LP transition, in TXBYTECLKHS */
|
|
|
|
int tlp_avail; /* time left for interleaving commands, in CLKIN4DDR */
|
|
|
|
int ttxclkesc; /* period of LP transmit escape clock, in CLKIN4DDR */
|
|
|
|
int thsbyte_clk = 16; /* Period of TXBYTECLKHS clock, in CLKIN4DDR */
|
|
|
|
int lp_inter; /* cmd mode data that can be interleaved, in bytes */
|
|
|
|
|
|
|
|
/* maximum LP transition time according to Scenario 1 */
|
|
|
|
trans_lp = exit_hs + max(enter_hs, 2) + 1;
|
|
|
|
|
|
|
|
/* CLKIN4DDR = 16 * TXBYTECLKHS */
|
|
|
|
tlp_avail = thsbyte_clk * (blank - trans_lp);
|
|
|
|
|
2012-06-04 15:06:34 +07:00
|
|
|
ttxclkesc = tdsi_fclk * lp_clk_div;
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 13:02:18 +07:00
|
|
|
|
|
|
|
lp_inter = ((tlp_avail - 8 * thsbyte_clk - 5 * tdsi_fclk) / ttxclkesc -
|
|
|
|
26) / 16;
|
|
|
|
|
|
|
|
return max(lp_inter, 0);
|
|
|
|
}
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static void dsi_config_cmd_mode_interleaving(struct platform_device *dsidev)
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 13:02:18 +07:00
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int blanking_mode;
|
|
|
|
int hfp_blanking_mode, hbp_blanking_mode, hsa_blanking_mode;
|
|
|
|
int hsa, hfp, hbp, width_bytes, bllp, lp_clk_div;
|
|
|
|
int ddr_clk_pre, ddr_clk_post, enter_hs_mode_lat, exit_hs_mode_lat;
|
|
|
|
int tclk_trail, ths_exit, exiths_clk;
|
|
|
|
bool ddr_alwon;
|
2012-08-13 15:47:30 +07:00
|
|
|
struct omap_video_timings *timings = &dsi->timings;
|
2012-08-10 16:31:33 +07:00
|
|
|
int bpp = dsi_get_pixel_size(dsi->pix_fmt);
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 13:02:18 +07:00
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
2012-11-27 22:05:54 +07:00
|
|
|
int dsi_fclk_hsdiv = dsi->user_dsi_cinfo.regm_dsi + 1;
|
OMAPDSS: DSI: Support command mode interleaving during video mode blanking periods
DSI supports interleaving of command mode packets during the HSA, HFP, HBP and
BLLP blanking intervals in a video mode stream. This is useful as a user may
want to read or change the configuration of a panel without stopping the video
stream.
On OMAP DSI, we can queue HS or LP command mode packets in the TX FIFO, and
the DSI HW takes care of interleaving this data during the one of the blanking
intervals. The DSI HW needs to be programmed with the maximum amount of data
that can be interleaved in a particular blanking period. A blanking period
cannot be used to send command mode data for it's complete duration, there is
some amount of time required for the DSI data and clock lanes to transition
to the desired LP or HS state.
Based on the state of the lanes at the beginning and end of the blanking period,
we have different scenarios, with each scenario having a different value of time
required to transition to HS or LP. Refer to the section 'Interleaving Mode' in
OMAP TRM for more info on the scenarios and the equations to calculate the time
required for HS or LP transitions.
We use the scenarios which takes the maximum time for HS or LP transition, this
gives us the minimum amount of time that can be used to interleave command mode
data. The amount of data that can be sent during this minimum time is calculated
for command mode packets both in LP and HS. These are written to the registers
DSI_VM_TIMING4 to DSI_VM_TIMING6.
The calculations don't take into account the time required of transmitting BTA
when doing a DSI read, or verifying if a DSI write went through correctly. Until
these latencies aren't considered, the behaviour of DSI is unpredictable when
a BTA is interleaved during a blanking period. Enhancement of these calculations
is a TODO item.
The calculations are derived from DSI parameter calculation tools written by
Sebastien Fagard <s-fagard@ti.com>
Signed-off-by: Archit Taneja <archit@ti.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-05-15 13:02:18 +07:00
|
|
|
int hsa_interleave_hs = 0, hsa_interleave_lp = 0;
|
|
|
|
int hfp_interleave_hs = 0, hfp_interleave_lp = 0;
|
|
|
|
int hbp_interleave_hs = 0, hbp_interleave_lp = 0;
|
|
|
|
int bl_interleave_hs = 0, bl_interleave_lp = 0;
|
|
|
|
u32 r;
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_CTRL);
|
|
|
|
blanking_mode = FLD_GET(r, 20, 20);
|
|
|
|
hfp_blanking_mode = FLD_GET(r, 21, 21);
|
|
|
|
hbp_blanking_mode = FLD_GET(r, 22, 22);
|
|
|
|
hsa_blanking_mode = FLD_GET(r, 23, 23);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
|
|
|
|
hbp = FLD_GET(r, 11, 0);
|
|
|
|
hfp = FLD_GET(r, 23, 12);
|
|
|
|
hsa = FLD_GET(r, 31, 24);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_CLK_TIMING);
|
|
|
|
ddr_clk_post = FLD_GET(r, 7, 0);
|
|
|
|
ddr_clk_pre = FLD_GET(r, 15, 8);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING7);
|
|
|
|
exit_hs_mode_lat = FLD_GET(r, 15, 0);
|
|
|
|
enter_hs_mode_lat = FLD_GET(r, 31, 16);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_CLK_CTRL);
|
|
|
|
lp_clk_div = FLD_GET(r, 12, 0);
|
|
|
|
ddr_alwon = FLD_GET(r, 13, 13);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
|
|
|
|
ths_exit = FLD_GET(r, 7, 0);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
|
|
|
|
tclk_trail = FLD_GET(r, 15, 8);
|
|
|
|
|
|
|
|
exiths_clk = ths_exit + tclk_trail;
|
|
|
|
|
|
|
|
width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8);
|
|
|
|
bllp = hbp + hfp + hsa + DIV_ROUND_UP(width_bytes + 6, ndl);
|
|
|
|
|
|
|
|
if (!hsa_blanking_mode) {
|
|
|
|
hsa_interleave_hs = dsi_compute_interleave_hs(hsa, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
hsa_interleave_lp = dsi_compute_interleave_lp(hsa,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hfp_blanking_mode) {
|
|
|
|
hfp_interleave_hs = dsi_compute_interleave_hs(hfp, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
hfp_interleave_lp = dsi_compute_interleave_lp(hfp,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hbp_blanking_mode) {
|
|
|
|
hbp_interleave_hs = dsi_compute_interleave_hs(hbp, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
|
|
|
|
hbp_interleave_lp = dsi_compute_interleave_lp(hbp,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!blanking_mode) {
|
|
|
|
bl_interleave_hs = dsi_compute_interleave_hs(bllp, ddr_alwon,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
exiths_clk, ddr_clk_pre, ddr_clk_post);
|
|
|
|
|
|
|
|
bl_interleave_lp = dsi_compute_interleave_lp(bllp,
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat,
|
|
|
|
lp_clk_div, dsi_fclk_hsdiv);
|
|
|
|
}
|
|
|
|
|
|
|
|
DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n",
|
|
|
|
hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs,
|
|
|
|
bl_interleave_hs);
|
|
|
|
|
|
|
|
DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n",
|
|
|
|
hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp,
|
|
|
|
bl_interleave_lp);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING4);
|
|
|
|
r = FLD_MOD(r, hsa_interleave_hs, 23, 16);
|
|
|
|
r = FLD_MOD(r, hfp_interleave_hs, 15, 8);
|
|
|
|
r = FLD_MOD(r, hbp_interleave_hs, 7, 0);
|
|
|
|
dsi_write_reg(dsidev, DSI_VM_TIMING4, r);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING5);
|
|
|
|
r = FLD_MOD(r, hsa_interleave_lp, 23, 16);
|
|
|
|
r = FLD_MOD(r, hfp_interleave_lp, 15, 8);
|
|
|
|
r = FLD_MOD(r, hbp_interleave_lp, 7, 0);
|
|
|
|
dsi_write_reg(dsidev, DSI_VM_TIMING5, r);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING6);
|
|
|
|
r = FLD_MOD(r, bl_interleave_hs, 31, 15);
|
|
|
|
r = FLD_MOD(r, bl_interleave_lp, 16, 0);
|
|
|
|
dsi_write_reg(dsidev, DSI_VM_TIMING6, r);
|
|
|
|
}
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static int dsi_proto_config(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2012-08-10 16:31:33 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 r;
|
|
|
|
int buswidth = 0;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32,
|
2009-12-16 21:49:03 +07:00
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32,
|
2009-12-16 21:49:03 +07:00
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32,
|
|
|
|
DSI_FIFO_SIZE_32);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* XXX what values for the timeouts? */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_set_stop_state_counter(dsidev, 0x1000, false, false);
|
|
|
|
dsi_set_ta_timeout(dsidev, 0x1fff, true, true);
|
|
|
|
dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true);
|
|
|
|
dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-10 16:31:33 +07:00
|
|
|
switch (dsi_get_pixel_size(dsi->pix_fmt)) {
|
2009-10-28 16:59:56 +07:00
|
|
|
case 16:
|
|
|
|
buswidth = 0;
|
|
|
|
break;
|
|
|
|
case 18:
|
|
|
|
buswidth = 1;
|
|
|
|
break;
|
|
|
|
case 24:
|
|
|
|
buswidth = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
2012-05-18 15:47:02 +07:00
|
|
|
return -EINVAL;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_CTRL);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */
|
|
|
|
r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */
|
|
|
|
r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */
|
|
|
|
r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/
|
|
|
|
r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
|
|
|
|
r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
|
|
|
|
r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
|
|
|
|
r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
|
2011-03-22 18:33:36 +07:00
|
|
|
if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) {
|
|
|
|
r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */
|
|
|
|
/* DCS_CMD_CODE, 1=start, 0=continue */
|
|
|
|
r = FLD_MOD(r, 0, 25, 25);
|
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_CTRL, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
dsi_config_vp_num_line_buffers(dsidev);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2012-08-14 13:59:22 +07:00
|
|
|
dsi_config_vp_sync_events(dsidev);
|
|
|
|
dsi_config_blanking_modes(dsidev);
|
2012-11-27 22:32:36 +07:00
|
|
|
dsi_config_cmd_mode_interleaving(dsidev);
|
2011-09-05 18:18:27 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_initial_config(dsidev, 0);
|
|
|
|
dsi_vc_initial_config(dsidev, 1);
|
|
|
|
dsi_vc_initial_config(dsidev, 2);
|
|
|
|
dsi_vc_initial_config(dsidev, 3);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
static void dsi_proto_timings(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-10-13 20:12:29 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail;
|
|
|
|
unsigned tclk_pre, tclk_post;
|
|
|
|
unsigned ths_prepare, ths_prepare_ths_zero, ths_zero;
|
|
|
|
unsigned ths_trail, ths_exit;
|
|
|
|
unsigned ddr_clk_pre, ddr_clk_post;
|
|
|
|
unsigned enter_hs_mode_lat, exit_hs_mode_lat;
|
|
|
|
unsigned ths_eot;
|
2011-10-13 20:12:29 +07:00
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
2009-10-28 16:59:56 +07:00
|
|
|
u32 r;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
|
2009-10-28 16:59:56 +07:00
|
|
|
ths_prepare = FLD_GET(r, 31, 24);
|
|
|
|
ths_prepare_ths_zero = FLD_GET(r, 23, 16);
|
|
|
|
ths_zero = ths_prepare_ths_zero - ths_prepare;
|
|
|
|
ths_trail = FLD_GET(r, 15, 8);
|
|
|
|
ths_exit = FLD_GET(r, 7, 0);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1);
|
2012-09-24 13:34:52 +07:00
|
|
|
tlpx = FLD_GET(r, 20, 16) * 2;
|
2009-10-28 16:59:56 +07:00
|
|
|
tclk_trail = FLD_GET(r, 15, 8);
|
|
|
|
tclk_zero = FLD_GET(r, 7, 0);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2);
|
2009-10-28 16:59:56 +07:00
|
|
|
tclk_prepare = FLD_GET(r, 7, 0);
|
|
|
|
|
|
|
|
/* min 8*UI */
|
|
|
|
tclk_pre = 20;
|
|
|
|
/* min 60ns + 52*UI */
|
2011-05-12 18:56:26 +07:00
|
|
|
tclk_post = ns2ddr(dsidev, 60) + 26;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-09-05 18:18:27 +07:00
|
|
|
ths_eot = DIV_ROUND_UP(4, ndl);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
|
|
|
|
4);
|
|
|
|
ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
|
|
|
|
|
|
|
|
BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
|
|
|
|
BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_read_reg(dsidev, DSI_CLK_TIMING);
|
2009-10-28 16:59:56 +07:00
|
|
|
r = FLD_MOD(r, ddr_clk_pre, 15, 8);
|
|
|
|
r = FLD_MOD(r, ddr_clk_post, 7, 0);
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_CLK_TIMING, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
|
|
|
|
ddr_clk_pre,
|
|
|
|
ddr_clk_post);
|
|
|
|
|
|
|
|
enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
|
|
|
|
DIV_ROUND_UP(ths_prepare, 4) +
|
|
|
|
DIV_ROUND_UP(ths_zero + 3, 4);
|
|
|
|
|
|
|
|
exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
|
|
|
|
|
|
|
|
r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
|
|
|
|
FLD_VAL(exit_hs_mode_lat, 15, 0);
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VM_TIMING7, r);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
|
|
|
|
enter_hs_mode_lat, exit_hs_mode_lat);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2011-09-05 18:18:27 +07:00
|
|
|
/* TODO: Implement a video mode check_timings function */
|
2012-08-13 23:43:39 +07:00
|
|
|
int hsa = dsi->vm_timings.hsa;
|
|
|
|
int hfp = dsi->vm_timings.hfp;
|
|
|
|
int hbp = dsi->vm_timings.hbp;
|
|
|
|
int vsa = dsi->vm_timings.vsa;
|
|
|
|
int vfp = dsi->vm_timings.vfp;
|
|
|
|
int vbp = dsi->vm_timings.vbp;
|
|
|
|
int window_sync = dsi->vm_timings.window_sync;
|
2013-03-05 21:29:36 +07:00
|
|
|
bool hsync_end;
|
2012-08-13 15:47:30 +07:00
|
|
|
struct omap_video_timings *timings = &dsi->timings;
|
2012-08-10 16:31:33 +07:00
|
|
|
int bpp = dsi_get_pixel_size(dsi->pix_fmt);
|
2011-09-05 18:18:27 +07:00
|
|
|
int tl, t_he, width_bytes;
|
|
|
|
|
2013-03-05 21:29:36 +07:00
|
|
|
hsync_end = dsi->vm_timings.trans_mode == OMAP_DSS_DSI_PULSE_MODE;
|
2011-09-05 18:18:27 +07:00
|
|
|
t_he = hsync_end ?
|
|
|
|
((hsa == 0 && ndl == 3) ? 1 : DIV_ROUND_UP(4, ndl)) : 0;
|
|
|
|
|
|
|
|
width_bytes = DIV_ROUND_UP(timings->x_res * bpp, 8);
|
|
|
|
|
|
|
|
/* TL = t_HS + HSA + t_HE + HFP + ceil((WC + 6) / NDL) + HBP */
|
|
|
|
tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp +
|
|
|
|
DIV_ROUND_UP(width_bytes + 6, ndl) + hbp;
|
|
|
|
|
|
|
|
DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp,
|
|
|
|
hfp, hsync_end ? hsa : 0, tl);
|
|
|
|
DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp,
|
|
|
|
vsa, timings->y_res);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
|
|
|
|
r = FLD_MOD(r, hbp, 11, 0); /* HBP */
|
|
|
|
r = FLD_MOD(r, hfp, 23, 12); /* HFP */
|
|
|
|
r = FLD_MOD(r, hsync_end ? hsa : 0, 31, 24); /* HSA */
|
|
|
|
dsi_write_reg(dsidev, DSI_VM_TIMING1, r);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING2);
|
|
|
|
r = FLD_MOD(r, vbp, 7, 0); /* VBP */
|
|
|
|
r = FLD_MOD(r, vfp, 15, 8); /* VFP */
|
|
|
|
r = FLD_MOD(r, vsa, 23, 16); /* VSA */
|
|
|
|
r = FLD_MOD(r, window_sync, 27, 24); /* WINDOW_SYNC */
|
|
|
|
dsi_write_reg(dsidev, DSI_VM_TIMING2, r);
|
|
|
|
|
|
|
|
r = dsi_read_reg(dsidev, DSI_VM_TIMING3);
|
|
|
|
r = FLD_MOD(r, timings->y_res, 14, 0); /* VACT */
|
|
|
|
r = FLD_MOD(r, tl, 31, 16); /* TL */
|
|
|
|
dsi_write_reg(dsidev, DSI_VM_TIMING3, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-28 19:58:56 +07:00
|
|
|
int omapdss_dsi_configure_pins(struct omap_dss_device *dssdev,
|
|
|
|
const struct omap_dsi_pin_config *pin_cfg)
|
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int num_pins;
|
|
|
|
const int *pins;
|
|
|
|
struct dsi_lane_config lanes[DSI_MAX_NR_LANES];
|
|
|
|
int num_lanes;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
static const enum dsi_lane_function functions[] = {
|
|
|
|
DSI_LANE_CLK,
|
|
|
|
DSI_LANE_DATA1,
|
|
|
|
DSI_LANE_DATA2,
|
|
|
|
DSI_LANE_DATA3,
|
|
|
|
DSI_LANE_DATA4,
|
|
|
|
};
|
|
|
|
|
|
|
|
num_pins = pin_cfg->num_pins;
|
|
|
|
pins = pin_cfg->pins;
|
|
|
|
|
|
|
|
if (num_pins < 4 || num_pins > dsi->num_lanes_supported * 2
|
|
|
|
|| num_pins % 2 != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (i = 0; i < DSI_MAX_NR_LANES; ++i)
|
|
|
|
lanes[i].function = DSI_LANE_UNUSED;
|
|
|
|
|
|
|
|
num_lanes = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < num_pins; i += 2) {
|
|
|
|
u8 lane, pol;
|
|
|
|
int dx, dy;
|
|
|
|
|
|
|
|
dx = pins[i];
|
|
|
|
dy = pins[i + 1];
|
|
|
|
|
|
|
|
if (dx < 0 || dx >= dsi->num_lanes_supported * 2)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (dy < 0 || dy >= dsi->num_lanes_supported * 2)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (dx & 1) {
|
|
|
|
if (dy != dx - 1)
|
|
|
|
return -EINVAL;
|
|
|
|
pol = 1;
|
|
|
|
} else {
|
|
|
|
if (dy != dx + 1)
|
|
|
|
return -EINVAL;
|
|
|
|
pol = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
lane = dx / 2;
|
|
|
|
|
|
|
|
lanes[lane].function = functions[i / 2];
|
|
|
|
lanes[lane].polarity = pol;
|
|
|
|
num_lanes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(dsi->lanes, lanes, sizeof(dsi->lanes));
|
|
|
|
dsi->num_lanes_used = num_lanes;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omapdss_dsi_configure_pins);
|
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
|
2011-09-05 18:18:27 +07:00
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2012-08-13 15:47:30 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2012-11-27 22:32:36 +07:00
|
|
|
struct omap_overlay_manager *mgr = dsi->output.manager;
|
2012-08-10 16:31:33 +07:00
|
|
|
int bpp = dsi_get_pixel_size(dsi->pix_fmt);
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
struct omap_dss_device *out = &dsi->output;
|
2011-09-05 18:18:27 +07:00
|
|
|
u8 data_type;
|
|
|
|
u16 word_count;
|
2011-11-21 18:42:58 +07:00
|
|
|
int r;
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2013-02-22 17:58:35 +07:00
|
|
|
if (out == NULL || out->manager == NULL) {
|
|
|
|
DSSERR("failed to enable display: no output/manager\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = dsi_display_init_dispc(dsidev, mgr);
|
|
|
|
if (r)
|
|
|
|
goto err_init_dispc;
|
|
|
|
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2012-08-10 16:31:33 +07:00
|
|
|
switch (dsi->pix_fmt) {
|
2011-11-09 20:30:11 +07:00
|
|
|
case OMAP_DSS_DSI_FMT_RGB888:
|
|
|
|
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
|
|
|
|
break;
|
|
|
|
case OMAP_DSS_DSI_FMT_RGB666:
|
|
|
|
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
|
|
|
|
break;
|
|
|
|
case OMAP_DSS_DSI_FMT_RGB666_PACKED:
|
|
|
|
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
|
|
|
|
break;
|
|
|
|
case OMAP_DSS_DSI_FMT_RGB565:
|
|
|
|
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
|
|
|
|
break;
|
|
|
|
default:
|
2013-02-22 17:58:35 +07:00
|
|
|
r = -EINVAL;
|
|
|
|
goto err_pix_fmt;
|
2011-11-09 20:30:11 +07:00
|
|
|
};
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
dsi_if_enable(dsidev, false);
|
|
|
|
dsi_vc_enable(dsidev, channel, false);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
/* MODE, 1 = video mode */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 4, 4);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2012-08-13 15:47:30 +07:00
|
|
|
word_count = DIV_ROUND_UP(dsi->timings.x_res * bpp, 8);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
dsi_vc_write_long_header(dsidev, channel, data_type,
|
|
|
|
word_count, 0);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
dsi_vc_enable(dsidev, channel, true);
|
|
|
|
dsi_if_enable(dsidev, true);
|
|
|
|
}
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2012-09-04 13:12:36 +07:00
|
|
|
r = dss_mgr_enable(mgr);
|
2013-02-22 17:58:35 +07:00
|
|
|
if (r)
|
|
|
|
goto err_mgr_enable;
|
2011-09-05 18:18:27 +07:00
|
|
|
|
|
|
|
return 0;
|
2013-02-22 17:58:35 +07:00
|
|
|
|
|
|
|
err_mgr_enable:
|
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
|
|
|
dsi_if_enable(dsidev, false);
|
|
|
|
dsi_vc_enable(dsidev, channel, false);
|
|
|
|
}
|
|
|
|
err_pix_fmt:
|
|
|
|
dsi_display_uninit_dispc(dsidev, mgr);
|
|
|
|
err_init_dispc:
|
|
|
|
return r;
|
2011-09-05 18:18:27 +07:00
|
|
|
}
|
2011-11-09 20:30:11 +07:00
|
|
|
EXPORT_SYMBOL(dsi_enable_video_output);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel)
|
2011-09-05 18:18:27 +07:00
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2012-08-16 19:32:00 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2012-11-27 22:32:36 +07:00
|
|
|
struct omap_overlay_manager *mgr = dsi->output.manager;
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
|
2011-11-09 20:30:11 +07:00
|
|
|
dsi_if_enable(dsidev, false);
|
|
|
|
dsi_vc_enable(dsidev, channel, false);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
/* MODE, 0 = command mode */
|
|
|
|
REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 4, 4);
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2011-11-09 20:30:11 +07:00
|
|
|
dsi_vc_enable(dsidev, channel, true);
|
|
|
|
dsi_if_enable(dsidev, true);
|
|
|
|
}
|
2011-09-05 18:18:27 +07:00
|
|
|
|
2012-09-04 13:12:36 +07:00
|
|
|
dss_mgr_disable(mgr);
|
2013-02-22 17:58:35 +07:00
|
|
|
|
|
|
|
dsi_display_uninit_dispc(dsidev, mgr);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2011-11-09 20:30:11 +07:00
|
|
|
EXPORT_SYMBOL(dsi_disable_video_output);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static void dsi_update_screen_dispc(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2012-11-27 22:32:36 +07:00
|
|
|
struct omap_overlay_manager *mgr = dsi->output.manager;
|
2009-10-28 16:59:56 +07:00
|
|
|
unsigned bytespp;
|
|
|
|
unsigned bytespl;
|
|
|
|
unsigned bytespf;
|
|
|
|
unsigned total_len;
|
|
|
|
unsigned packet_payload;
|
|
|
|
unsigned packet_len;
|
|
|
|
u32 l;
|
OMAP: DSS2: DSI: use a private workqueue
Using the shared workqueue led to to a deadlock in the case where the
display was unblanked via keyboard.
What happens is something like this:
- User presses a key
context 1:
- drivers/char/keyboard.c calls schedule_console_callback()
- fb_unblank takes the console semaphore
- dsi bus lock is taken, and frame transfer is started (dsi bus lock is
left on)
- Unblank code tries to set the panel backlight, which tries to take dsi
bus lock, but is blocked while the frame transfer is going on
context 2, shared workqueue, console_callback in drivers/char/vt.c:
- Tries to take console semaphore
- Blocks, as console semaphore is being held by context 1
- No other shared workqueue work can be run
context 3, HW irq, caused by FRAMEDONE interrupt:
- Interrupt handler schedules framedone-work in shared workqueue
- Framedone-work is never ran, as the shared workqueue is blocked. This
means that the unblank thread stays blocked, which means that context 2
stays blocked.
While I think the real problem is in keyboard/virtual terminal code, using
a private workqueue in the DSI driver is perhaps safer and more robust
than using the shared one. The DSI works should not be delayed more than a
millisecond or so, and even if the private workqueue gives us no hard
promise of doing so, it's still safer bet than the shared workqueue.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
2010-04-12 13:57:19 +07:00
|
|
|
int r;
|
2011-05-12 18:56:27 +07:00
|
|
|
const unsigned channel = dsi->update_channel;
|
2013-03-05 15:37:02 +07:00
|
|
|
const unsigned line_buf_size = dsi->line_buffer_size;
|
2012-08-09 17:11:13 +07:00
|
|
|
u16 w = dsi->timings.x_res;
|
|
|
|
u16 h = dsi->timings.y_res;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-11-03 21:34:20 +07:00
|
|
|
DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-22 13:28:08 +07:00
|
|
|
dsi_vc_config_source(dsidev, channel, DSI_VC_SOURCE_VP);
|
2010-01-12 19:16:41 +07:00
|
|
|
|
2012-08-10 16:31:33 +07:00
|
|
|
bytespp = dsi_get_pixel_size(dsi->pix_fmt) / 8;
|
2009-10-28 16:59:56 +07:00
|
|
|
bytespl = w * bytespp;
|
|
|
|
bytespf = bytespl * h;
|
|
|
|
|
|
|
|
/* NOTE: packet_payload has to be equal to N * bytespl, where N is
|
|
|
|
* number of lines in a packet. See errata about VP_CLK_RATIO */
|
|
|
|
|
|
|
|
if (bytespf < line_buf_size)
|
|
|
|
packet_payload = bytespf;
|
|
|
|
else
|
|
|
|
packet_payload = (line_buf_size) / bytespl * bytespl;
|
|
|
|
|
|
|
|
packet_len = packet_payload + 1; /* 1 byte for DCS cmd */
|
|
|
|
total_len = (bytespf / packet_payload) * packet_len;
|
|
|
|
|
|
|
|
if (bytespf % packet_payload)
|
|
|
|
total_len += (bytespf % packet_payload) + 1;
|
|
|
|
|
|
|
|
l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-25 19:55:03 +07:00
|
|
|
dsi_vc_write_long_header(dsidev, channel, MIPI_DSI_DCS_LONG_WRITE,
|
2011-05-12 18:56:26 +07:00
|
|
|
packet_len, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->te_enabled)
|
2009-10-28 16:59:56 +07:00
|
|
|
l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
|
|
|
|
else
|
|
|
|
l = FLD_MOD(l, 1, 31, 31); /* TE_START */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_write_reg(dsidev, DSI_VC_TE(channel), l);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* We put SIDLEMODE to no-idle for the duration of the transfer,
|
|
|
|
* because DSS interrupts are not capable of waking up the CPU and the
|
|
|
|
* framedone interrupt could be delayed for quite a long time. I think
|
|
|
|
* the same goes for any DSS interrupts, but for some reason I have not
|
|
|
|
* seen the problem anywhere else than here.
|
|
|
|
*/
|
|
|
|
dispc_disable_sidle();
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_perf_mark_start(dsidev);
|
2010-01-12 19:16:41 +07:00
|
|
|
|
2011-05-16 16:47:07 +07:00
|
|
|
r = schedule_delayed_work(&dsi->framedone_timeout_work,
|
|
|
|
msecs_to_jiffies(250));
|
OMAP: DSS2: DSI: use a private workqueue
Using the shared workqueue led to to a deadlock in the case where the
display was unblanked via keyboard.
What happens is something like this:
- User presses a key
context 1:
- drivers/char/keyboard.c calls schedule_console_callback()
- fb_unblank takes the console semaphore
- dsi bus lock is taken, and frame transfer is started (dsi bus lock is
left on)
- Unblank code tries to set the panel backlight, which tries to take dsi
bus lock, but is blocked while the frame transfer is going on
context 2, shared workqueue, console_callback in drivers/char/vt.c:
- Tries to take console semaphore
- Blocks, as console semaphore is being held by context 1
- No other shared workqueue work can be run
context 3, HW irq, caused by FRAMEDONE interrupt:
- Interrupt handler schedules framedone-work in shared workqueue
- Framedone-work is never ran, as the shared workqueue is blocked. This
means that the unblank thread stays blocked, which means that context 2
stays blocked.
While I think the real problem is in keyboard/virtual terminal code, using
a private workqueue in the DSI driver is perhaps safer and more robust
than using the shared one. The DSI works should not be delayed more than a
millisecond or so, and even if the private workqueue gives us no hard
promise of doing so, it's still safer bet than the shared workqueue.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com>
2010-04-12 13:57:19 +07:00
|
|
|
BUG_ON(r == 0);
|
2010-01-12 19:16:41 +07:00
|
|
|
|
2012-09-04 13:12:36 +07:00
|
|
|
dss_mgr_set_timings(mgr, &dsi->timings);
|
2012-08-09 17:11:13 +07:00
|
|
|
|
2012-09-04 13:12:36 +07:00
|
|
|
dss_mgr_start_update(mgr);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->te_enabled) {
|
2009-10-28 16:59:56 +07:00
|
|
|
/* disable LP_RX_TO, so that we can receive TE. Time to wait
|
|
|
|
* for TE is longer than the timer allows */
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_send_bta(dsidev, channel);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
2011-05-12 18:56:27 +07:00
|
|
|
mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250));
|
2009-10-28 16:59:56 +07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
|
|
|
static void dsi_te_timeout(unsigned long arg)
|
|
|
|
{
|
|
|
|
DSSERR("TE not received for 250ms!\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_handle_framedone(struct platform_device *dsidev, int error)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
/* SIDLEMODE back to smart-idle */
|
|
|
|
dispc_enable_sidle();
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->te_enabled) {
|
2010-01-12 19:16:41 +07:00
|
|
|
/* enable LP_RX_TO again after the TE */
|
2011-05-12 18:56:26 +07:00
|
|
|
REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->framedone_callback(error, dsi->framedone_data);
|
2010-06-09 19:31:01 +07:00
|
|
|
|
|
|
|
if (!error)
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_perf_show(dsidev, "DISPC");
|
2010-01-12 19:16:41 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-06-09 19:31:01 +07:00
|
|
|
static void dsi_framedone_timeout_work_callback(struct work_struct *work)
|
2010-01-12 19:16:41 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = container_of(work, struct dsi_data,
|
|
|
|
framedone_timeout_work.work);
|
2010-06-09 19:31:01 +07:00
|
|
|
/* XXX While extremely unlikely, we could get FRAMEDONE interrupt after
|
|
|
|
* 250ms which would conflict with this timeout work. What should be
|
|
|
|
* done is first cancel the transfer on the HW, and then cancel the
|
|
|
|
* possibly scheduled framedone work. However, cancelling the transfer
|
|
|
|
* on the HW is buggy, and would probably require resetting the whole
|
|
|
|
* DSI */
|
2010-01-12 19:16:41 +07:00
|
|
|
|
2010-06-09 19:31:01 +07:00
|
|
|
DSSERR("Framedone not received for 250ms!\n");
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi_handle_framedone(dsi->pdev, -ETIMEDOUT);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2012-10-10 17:59:07 +07:00
|
|
|
static void dsi_framedone_irq_callback(void *data)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2012-08-14 13:59:22 +07:00
|
|
|
struct platform_device *dsidev = (struct platform_device *) data;
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2010-06-09 19:31:01 +07:00
|
|
|
/* Note: We get FRAMEDONE when DISPC has finished sending pixels and
|
|
|
|
* turns itself off. However, DSI still has the pixels in its buffers,
|
|
|
|
* and is sending the data.
|
|
|
|
*/
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-22 03:18:24 +07:00
|
|
|
cancel_delayed_work(&dsi->framedone_timeout_work);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_handle_framedone(dsidev, 0);
|
2010-01-12 19:16:41 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-11-03 21:34:20 +07:00
|
|
|
int omap_dsi_update(struct omap_dss_device *dssdev, int channel,
|
|
|
|
void (*callback)(int, void *), void *data)
|
2010-01-12 19:16:41 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2011-11-03 21:34:20 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2010-01-12 19:16:41 +07:00
|
|
|
u16 dw, dh;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_perf_mark_setup(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->update_channel = channel;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-08-15 15:22:21 +07:00
|
|
|
dsi->framedone_callback = callback;
|
|
|
|
dsi->framedone_data = data;
|
2010-07-14 19:11:50 +07:00
|
|
|
|
2012-08-09 16:53:43 +07:00
|
|
|
dw = dsi->timings.x_res;
|
|
|
|
dh = dsi->timings.y_res;
|
2010-07-14 19:11:50 +07:00
|
|
|
|
2011-11-03 21:34:20 +07:00
|
|
|
#ifdef DEBUG
|
|
|
|
dsi->update_bytes = dw * dh *
|
2012-08-10 16:31:33 +07:00
|
|
|
dsi_get_pixel_size(dsi->pix_fmt) / 8;
|
2011-11-03 21:34:20 +07:00
|
|
|
#endif
|
2012-11-27 22:32:36 +07:00
|
|
|
dsi_update_screen_dispc(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2010-01-12 19:16:41 +07:00
|
|
|
EXPORT_SYMBOL(omap_dsi_update);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
/* Display funcs */
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static int dsi_configure_dispc_clocks(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2012-06-29 16:01:07 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
struct dispc_clock_info dispc_cinfo;
|
2009-10-28 16:59:56 +07:00
|
|
|
int r;
|
2013-03-07 16:21:45 +07:00
|
|
|
unsigned long fck;
|
2012-06-29 16:01:07 +07:00
|
|
|
|
|
|
|
fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
|
|
|
|
|
2012-11-27 22:05:54 +07:00
|
|
|
dispc_cinfo.lck_div = dsi->user_dispc_cinfo.lck_div;
|
|
|
|
dispc_cinfo.pck_div = dsi->user_dispc_cinfo.pck_div;
|
2012-06-29 16:01:07 +07:00
|
|
|
|
|
|
|
r = dispc_calc_clock_rates(fck, &dispc_cinfo);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("Failed to calc dispc clocks\n");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->mgr_config.clock_info = dispc_cinfo;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-22 17:58:35 +07:00
|
|
|
static int dsi_display_init_dispc(struct platform_device *dsidev,
|
|
|
|
struct omap_overlay_manager *mgr)
|
2012-06-29 16:01:07 +07:00
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
int r;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2013-03-05 22:11:16 +07:00
|
|
|
dss_select_lcd_clk_source(mgr->id, dsi->module_id == 0 ?
|
|
|
|
OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC :
|
|
|
|
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC);
|
2011-11-03 21:34:20 +07:00
|
|
|
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) {
|
2012-10-10 17:59:07 +07:00
|
|
|
r = dss_mgr_register_framedone_handler(mgr,
|
|
|
|
dsi_framedone_irq_callback, dsidev);
|
2011-09-05 18:18:27 +07:00
|
|
|
if (r) {
|
2012-10-10 17:59:07 +07:00
|
|
|
DSSERR("can't register FRAMEDONE handler\n");
|
2012-06-29 16:01:07 +07:00
|
|
|
goto err;
|
2011-09-05 18:18:27 +07:00
|
|
|
}
|
|
|
|
|
2012-06-29 16:01:07 +07:00
|
|
|
dsi->mgr_config.stallmode = true;
|
|
|
|
dsi->mgr_config.fifohandcheck = true;
|
2011-09-05 18:18:27 +07:00
|
|
|
} else {
|
2012-06-29 16:01:07 +07:00
|
|
|
dsi->mgr_config.stallmode = false;
|
|
|
|
dsi->mgr_config.fifohandcheck = false;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2012-06-26 14:08:31 +07:00
|
|
|
/*
|
|
|
|
* override interlace, logic level and edge related parameters in
|
|
|
|
* omap_video_timings with default values
|
|
|
|
*/
|
2012-08-13 15:47:30 +07:00
|
|
|
dsi->timings.interlace = false;
|
|
|
|
dsi->timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
|
|
|
|
dsi->timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
|
|
|
|
dsi->timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
|
|
|
|
dsi->timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH;
|
|
|
|
dsi->timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
|
2012-06-26 14:08:31 +07:00
|
|
|
|
2012-09-04 13:12:36 +07:00
|
|
|
dss_mgr_set_timings(mgr, &dsi->timings);
|
2012-06-26 14:08:31 +07:00
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
r = dsi_configure_dispc_clocks(dsidev);
|
2012-06-29 16:01:07 +07:00
|
|
|
if (r)
|
|
|
|
goto err1;
|
|
|
|
|
|
|
|
dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
|
|
|
|
dsi->mgr_config.video_port_width =
|
2012-08-10 16:31:33 +07:00
|
|
|
dsi_get_pixel_size(dsi->pix_fmt);
|
2012-06-29 16:01:07 +07:00
|
|
|
dsi->mgr_config.lcden_sig_polarity = 0;
|
|
|
|
|
2012-09-04 13:12:36 +07:00
|
|
|
dss_mgr_set_lcd_config(mgr, &dsi->mgr_config);
|
2012-06-21 11:15:11 +07:00
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
return 0;
|
2012-06-29 16:01:07 +07:00
|
|
|
err1:
|
2012-08-16 19:32:00 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
|
2012-10-10 17:59:07 +07:00
|
|
|
dss_mgr_unregister_framedone_handler(mgr,
|
|
|
|
dsi_framedone_irq_callback, dsidev);
|
2012-06-29 16:01:07 +07:00
|
|
|
err:
|
2013-02-22 17:58:35 +07:00
|
|
|
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
|
2012-06-29 16:01:07 +07:00
|
|
|
return r;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2013-02-22 17:58:35 +07:00
|
|
|
static void dsi_display_uninit_dispc(struct platform_device *dsidev,
|
|
|
|
struct omap_overlay_manager *mgr)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2012-08-16 19:32:00 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2012-10-10 17:59:07 +07:00
|
|
|
if (dsi->mode == OMAP_DSS_DSI_CMD_MODE)
|
|
|
|
dss_mgr_unregister_framedone_handler(mgr,
|
|
|
|
dsi_framedone_irq_callback, dsidev);
|
2013-02-22 17:58:35 +07:00
|
|
|
|
|
|
|
dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static int dsi_configure_dsi_clocks(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2012-11-27 22:05:54 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
struct dsi_clock_info cinfo;
|
|
|
|
int r;
|
|
|
|
|
2012-11-27 22:05:54 +07:00
|
|
|
cinfo = dsi->user_dsi_cinfo;
|
|
|
|
|
2012-03-15 20:22:58 +07:00
|
|
|
r = dsi_calc_clock_rates(dsidev, &cinfo);
|
2010-04-23 03:50:05 +07:00
|
|
|
if (r) {
|
|
|
|
DSSERR("Failed to calc dsi clocks\n");
|
2009-10-28 16:59:56 +07:00
|
|
|
return r;
|
2010-04-23 03:50:05 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_pll_set_clock_div(dsidev, &cinfo);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r) {
|
|
|
|
DSSERR("Failed to set dsi clocks\n");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static int dsi_display_init_dsi(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2012-03-09 21:07:39 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int r;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
r = dsi_pll_init(dsidev, true, true);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
|
|
|
goto err0;
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
r = dsi_configure_dsi_clocks(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
|
|
|
goto err1;
|
|
|
|
|
2013-03-05 22:11:16 +07:00
|
|
|
dss_select_dsi_clk_source(dsi->module_id, dsi->module_id == 0 ?
|
|
|
|
OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI :
|
|
|
|
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
DSSDBG("PLL OK\n");
|
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
r = dsi_cio_init(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
|
|
|
goto err2;
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
_dsi_print_reset_status(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-14 13:59:22 +07:00
|
|
|
dsi_proto_timings(dsidev);
|
2012-11-27 22:32:36 +07:00
|
|
|
dsi_set_lp_clk_divisor(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
if (1)
|
2011-05-12 18:56:26 +07:00
|
|
|
_dsi_print_reset_status(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
r = dsi_proto_config(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
|
|
|
goto err3;
|
|
|
|
|
|
|
|
/* enable interface */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_vc_enable(dsidev, 0, 1);
|
|
|
|
dsi_vc_enable(dsidev, 1, 1);
|
|
|
|
dsi_vc_enable(dsidev, 2, 1);
|
|
|
|
dsi_vc_enable(dsidev, 3, 1);
|
|
|
|
dsi_if_enable(dsidev, 1);
|
|
|
|
dsi_force_tx_stop_mode_io(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
err3:
|
2012-08-14 13:59:22 +07:00
|
|
|
dsi_cio_uninit(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
err2:
|
2012-03-09 21:07:39 +07:00
|
|
|
dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK);
|
2009-10-28 16:59:56 +07:00
|
|
|
err1:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_pll_uninit(dsidev, true);
|
2009-10-28 16:59:56 +07:00
|
|
|
err0:
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
static void dsi_display_uninit_dsi(struct platform_device *dsidev,
|
2010-10-11 15:33:30 +07:00
|
|
|
bool disconnect_lanes, bool enter_ulps)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-05-12 18:56:26 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (enter_ulps && !dsi->ulps_enabled)
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_enter_ulps(dsidev);
|
2010-07-28 19:53:38 +07:00
|
|
|
|
2010-04-23 03:50:09 +07:00
|
|
|
/* disable interface */
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_if_enable(dsidev, 0);
|
|
|
|
dsi_vc_enable(dsidev, 0, 0);
|
|
|
|
dsi_vc_enable(dsidev, 1, 0);
|
|
|
|
dsi_vc_enable(dsidev, 2, 0);
|
|
|
|
dsi_vc_enable(dsidev, 3, 0);
|
2010-04-23 03:50:09 +07:00
|
|
|
|
2012-03-09 21:07:39 +07:00
|
|
|
dss_select_dsi_clk_source(dsi->module_id, OMAP_DSS_CLK_SRC_FCK);
|
2012-08-14 13:59:22 +07:00
|
|
|
dsi_cio_uninit(dsidev);
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_pll_uninit(dsidev, disconnect_lanes);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
|
|
|
|
2010-01-12 20:12:07 +07:00
|
|
|
int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
DSSDBG("dsi_display_enable\n");
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2010-01-12 20:12:07 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
mutex_lock(&dsi->lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
r = dsi_runtime_get(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
2011-05-27 14:52:19 +07:00
|
|
|
goto err_get_dsi;
|
|
|
|
|
|
|
|
dsi_enable_pll_clock(dsidev, 1);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
_dsi_initialize_irq(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
r = dsi_display_init_dsi(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
if (r)
|
2011-05-27 14:52:19 +07:00
|
|
|
goto err_init_dsi;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
mutex_unlock(&dsi->lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
err_init_dsi:
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_enable_pll_clock(dsidev, 0);
|
2011-05-27 14:52:19 +07:00
|
|
|
dsi_runtime_put(dsidev);
|
|
|
|
err_get_dsi:
|
2011-05-12 18:56:27 +07:00
|
|
|
mutex_unlock(&dsi->lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("dsi_display_enable FAILED\n");
|
|
|
|
return r;
|
|
|
|
}
|
2010-01-12 20:12:07 +07:00
|
|
|
EXPORT_SYMBOL(omapdss_dsi_display_enable);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-07-30 16:39:34 +07:00
|
|
|
void omapdss_dsi_display_disable(struct omap_dss_device *dssdev,
|
2010-10-11 15:33:30 +07:00
|
|
|
bool disconnect_lanes, bool enter_ulps)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-05-12 18:56:26 +07:00
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSDBG("dsi_display_disable\n");
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
WARN_ON(!dsi_bus_is_locked(dsidev));
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
mutex_lock(&dsi->lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-06-16 18:34:06 +07:00
|
|
|
dsi_sync_vc(dsidev, 0);
|
|
|
|
dsi_sync_vc(dsidev, 1);
|
|
|
|
dsi_sync_vc(dsidev, 2);
|
|
|
|
dsi_sync_vc(dsidev, 3);
|
|
|
|
|
2012-11-27 22:32:36 +07:00
|
|
|
dsi_display_uninit_dsi(dsidev, disconnect_lanes, enter_ulps);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
dsi_runtime_put(dsidev);
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_enable_pll_clock(dsidev, 0);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
mutex_unlock(&dsi->lock);
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2010-01-12 20:12:07 +07:00
|
|
|
EXPORT_SYMBOL(omapdss_dsi_display_disable);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2010-01-11 20:11:01 +07:00
|
|
|
int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
dsi->te_enabled = enable;
|
2010-01-11 20:11:01 +07:00
|
|
|
return 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2010-01-11 20:11:01 +07:00
|
|
|
EXPORT_SYMBOL(omapdss_dsi_enable_te);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
#ifdef PRINT_VERBOSE_VM_TIMINGS
|
|
|
|
static void print_dsi_vm(const char *str,
|
|
|
|
const struct omap_dss_dsi_videomode_timings *t)
|
|
|
|
{
|
|
|
|
unsigned long byteclk = t->hsclk / 4;
|
|
|
|
int bl, wc, pps, tot;
|
|
|
|
|
|
|
|
wc = DIV_ROUND_UP(t->hact * t->bitspp, 8);
|
|
|
|
pps = DIV_ROUND_UP(wc + 6, t->ndl); /* pixel packet size */
|
|
|
|
bl = t->hss + t->hsa + t->hse + t->hbp + t->hfp;
|
|
|
|
tot = bl + pps;
|
|
|
|
|
|
|
|
#define TO_DSI_T(x) ((u32)div64_u64((u64)x * 1000000000llu, byteclk))
|
|
|
|
|
|
|
|
pr_debug("%s bck %lu, %u/%u/%u/%u/%u/%u = %u+%u = %u, "
|
|
|
|
"%u/%u/%u/%u/%u/%u = %u + %u = %u\n",
|
|
|
|
str,
|
|
|
|
byteclk,
|
|
|
|
t->hss, t->hsa, t->hse, t->hbp, pps, t->hfp,
|
|
|
|
bl, pps, tot,
|
|
|
|
TO_DSI_T(t->hss),
|
|
|
|
TO_DSI_T(t->hsa),
|
|
|
|
TO_DSI_T(t->hse),
|
|
|
|
TO_DSI_T(t->hbp),
|
|
|
|
TO_DSI_T(pps),
|
|
|
|
TO_DSI_T(t->hfp),
|
|
|
|
|
|
|
|
TO_DSI_T(bl),
|
|
|
|
TO_DSI_T(pps),
|
|
|
|
|
|
|
|
TO_DSI_T(tot));
|
|
|
|
#undef TO_DSI_T
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_dispc_vm(const char *str, const struct omap_video_timings *t)
|
|
|
|
{
|
|
|
|
unsigned long pck = t->pixel_clock * 1000;
|
|
|
|
int hact, bl, tot;
|
|
|
|
|
|
|
|
hact = t->x_res;
|
|
|
|
bl = t->hsw + t->hbp + t->hfp;
|
|
|
|
tot = hact + bl;
|
|
|
|
|
|
|
|
#define TO_DISPC_T(x) ((u32)div64_u64((u64)x * 1000000000llu, pck))
|
|
|
|
|
|
|
|
pr_debug("%s pck %lu, %u/%u/%u/%u = %u+%u = %u, "
|
|
|
|
"%u/%u/%u/%u = %u + %u = %u\n",
|
|
|
|
str,
|
|
|
|
pck,
|
|
|
|
t->hsw, t->hbp, hact, t->hfp,
|
|
|
|
bl, hact, tot,
|
|
|
|
TO_DISPC_T(t->hsw),
|
|
|
|
TO_DISPC_T(t->hbp),
|
|
|
|
TO_DISPC_T(hact),
|
|
|
|
TO_DISPC_T(t->hfp),
|
|
|
|
TO_DISPC_T(bl),
|
|
|
|
TO_DISPC_T(hact),
|
|
|
|
TO_DISPC_T(tot));
|
|
|
|
#undef TO_DISPC_T
|
|
|
|
}
|
|
|
|
|
|
|
|
/* note: this is not quite accurate */
|
|
|
|
static void print_dsi_dispc_vm(const char *str,
|
|
|
|
const struct omap_dss_dsi_videomode_timings *t)
|
|
|
|
{
|
|
|
|
struct omap_video_timings vm = { 0 };
|
|
|
|
unsigned long byteclk = t->hsclk / 4;
|
|
|
|
unsigned long pck;
|
|
|
|
u64 dsi_tput;
|
|
|
|
int dsi_hact, dsi_htot;
|
|
|
|
|
|
|
|
dsi_tput = (u64)byteclk * t->ndl * 8;
|
|
|
|
pck = (u32)div64_u64(dsi_tput, t->bitspp);
|
|
|
|
dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(t->hact * t->bitspp, 8) + 6, t->ndl);
|
|
|
|
dsi_htot = t->hss + t->hsa + t->hse + t->hbp + dsi_hact + t->hfp;
|
|
|
|
|
|
|
|
vm.pixel_clock = pck / 1000;
|
|
|
|
vm.hsw = div64_u64((u64)(t->hsa + t->hse) * pck, byteclk);
|
|
|
|
vm.hbp = div64_u64((u64)t->hbp * pck, byteclk);
|
|
|
|
vm.hfp = div64_u64((u64)t->hfp * pck, byteclk);
|
|
|
|
vm.x_res = t->hact;
|
|
|
|
|
|
|
|
print_dispc_vm(str, &vm);
|
|
|
|
}
|
|
|
|
#endif /* PRINT_VERBOSE_VM_TIMINGS */
|
|
|
|
|
|
|
|
static bool dsi_cm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
|
|
|
|
unsigned long pck, void *data)
|
2012-08-13 15:47:30 +07:00
|
|
|
{
|
2013-03-05 22:21:35 +07:00
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
|
|
|
struct omap_video_timings *t = &ctx->dispc_vm;
|
2012-08-13 15:47:30 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
ctx->dispc_cinfo.lck_div = lckd;
|
|
|
|
ctx->dispc_cinfo.pck_div = pckd;
|
|
|
|
ctx->dispc_cinfo.lck = lck;
|
|
|
|
ctx->dispc_cinfo.pck = pck;
|
2012-08-13 15:47:30 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
*t = *ctx->config->timings;
|
|
|
|
t->pixel_clock = pck / 1000;
|
|
|
|
t->x_res = ctx->config->timings->x_res;
|
|
|
|
t->y_res = ctx->config->timings->y_res;
|
|
|
|
t->hsw = t->hfp = t->hbp = t->vsw = 1;
|
|
|
|
t->vfp = t->vbp = 0;
|
2012-08-13 15:47:30 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
return true;
|
2012-08-13 15:47:30 +07:00
|
|
|
}
|
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
static bool dsi_cm_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
|
|
|
|
void *data)
|
2012-08-09 16:53:43 +07:00
|
|
|
{
|
2013-03-05 22:21:35 +07:00
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
2012-08-09 16:53:43 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
ctx->dsi_cinfo.regm_dispc = regm_dispc;
|
|
|
|
ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc;
|
2012-08-09 16:53:43 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
return dispc_div_calc(dispc, ctx->req_pck_min, ctx->req_pck_max,
|
|
|
|
dsi_cm_calc_dispc_cb, ctx);
|
|
|
|
}
|
2012-08-09 16:53:43 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
static bool dsi_cm_calc_pll_cb(int regn, int regm, unsigned long fint,
|
|
|
|
unsigned long pll, void *data)
|
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
|
|
|
|
|
|
|
ctx->dsi_cinfo.regn = regn;
|
|
|
|
ctx->dsi_cinfo.regm = regm;
|
|
|
|
ctx->dsi_cinfo.fint = fint;
|
|
|
|
ctx->dsi_cinfo.clkin4ddr = pll;
|
|
|
|
|
|
|
|
return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->req_pck_min,
|
|
|
|
dsi_cm_calc_hsdiv_cb, ctx);
|
2012-08-09 16:53:43 +07:00
|
|
|
}
|
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
static bool dsi_cm_calc(struct dsi_data *dsi,
|
|
|
|
const struct omap_dss_dsi_config *cfg,
|
|
|
|
struct dsi_clk_calc_ctx *ctx)
|
2012-08-10 16:31:33 +07:00
|
|
|
{
|
2013-03-05 22:21:35 +07:00
|
|
|
unsigned long clkin;
|
|
|
|
int bitspp, ndl;
|
|
|
|
unsigned long pll_min, pll_max;
|
|
|
|
unsigned long pck, txbyteclk;
|
2012-08-10 16:31:33 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
clkin = clk_get_rate(dsi->sys_clk);
|
|
|
|
bitspp = dsi_get_pixel_size(cfg->pixel_format);
|
|
|
|
ndl = dsi->num_lanes_used - 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we should calculate minimum txbyteclk to be able to send the
|
|
|
|
* frame in time, and also to handle TE. That's not very simple, though,
|
|
|
|
* especially as we go to LP between each pixel packet due to HW
|
|
|
|
* "feature". So let's just estimate very roughly and multiply by 1.5.
|
|
|
|
*/
|
|
|
|
pck = cfg->timings->pixel_clock * 1000;
|
|
|
|
pck = pck * 3 / 2;
|
|
|
|
txbyteclk = pck * bitspp / 8 / ndl;
|
2012-08-10 16:31:33 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
|
|
ctx->dsidev = dsi->pdev;
|
|
|
|
ctx->config = cfg;
|
|
|
|
ctx->req_pck_min = pck;
|
|
|
|
ctx->req_pck_nom = pck;
|
|
|
|
ctx->req_pck_max = pck * 3 / 2;
|
|
|
|
ctx->dsi_cinfo.clkin = clkin;
|
2012-08-10 16:31:33 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
pll_min = max(cfg->hs_clk_min * 4, txbyteclk * 4 * 4);
|
|
|
|
pll_max = cfg->hs_clk_max * 4;
|
|
|
|
|
|
|
|
return dsi_pll_calc(dsi->pdev, clkin,
|
|
|
|
pll_min, pll_max,
|
|
|
|
dsi_cm_calc_pll_cb, ctx);
|
2012-08-10 16:31:33 +07:00
|
|
|
}
|
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
static bool dsi_vm_calc_blanking(struct dsi_clk_calc_ctx *ctx)
|
2012-08-16 19:32:00 +07:00
|
|
|
{
|
2013-03-05 22:21:35 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(ctx->dsidev);
|
|
|
|
const struct omap_dss_dsi_config *cfg = ctx->config;
|
|
|
|
int bitspp = dsi_get_pixel_size(cfg->pixel_format);
|
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
|
|
|
unsigned long hsclk = ctx->dsi_cinfo.clkin4ddr / 4;
|
|
|
|
unsigned long byteclk = hsclk / 4;
|
2012-08-16 19:32:00 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
unsigned long dispc_pck, req_pck_min, req_pck_nom, req_pck_max;
|
|
|
|
int xres;
|
|
|
|
int panel_htot, panel_hbl; /* pixels */
|
|
|
|
int dispc_htot, dispc_hbl; /* pixels */
|
|
|
|
int dsi_htot, dsi_hact, dsi_hbl, hss, hse; /* byteclks */
|
|
|
|
int hfp, hsa, hbp;
|
|
|
|
const struct omap_video_timings *req_vm;
|
|
|
|
struct omap_video_timings *dispc_vm;
|
|
|
|
struct omap_dss_dsi_videomode_timings *dsi_vm;
|
|
|
|
u64 dsi_tput, dispc_tput;
|
2012-08-16 19:32:00 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
dsi_tput = (u64)byteclk * ndl * 8;
|
2012-08-16 19:32:00 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
req_vm = cfg->timings;
|
|
|
|
req_pck_min = ctx->req_pck_min;
|
|
|
|
req_pck_max = ctx->req_pck_max;
|
|
|
|
req_pck_nom = ctx->req_pck_nom;
|
|
|
|
|
|
|
|
dispc_pck = ctx->dispc_cinfo.pck;
|
|
|
|
dispc_tput = (u64)dispc_pck * bitspp;
|
|
|
|
|
|
|
|
xres = req_vm->x_res;
|
|
|
|
|
|
|
|
panel_hbl = req_vm->hfp + req_vm->hbp + req_vm->hsw;
|
|
|
|
panel_htot = xres + panel_hbl;
|
|
|
|
|
|
|
|
dsi_hact = DIV_ROUND_UP(DIV_ROUND_UP(xres * bitspp, 8) + 6, ndl);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When there are no line buffers, DISPC and DSI must have the
|
|
|
|
* same tput. Otherwise DISPC tput needs to be higher than DSI's.
|
|
|
|
*/
|
|
|
|
if (dsi->line_buffer_size < xres * bitspp / 8) {
|
|
|
|
if (dispc_tput != dsi_tput)
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (dispc_tput < dsi_tput)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DSI tput must be over the min requirement */
|
|
|
|
if (dsi_tput < (u64)bitspp * req_pck_min)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* When non-burst mode, DSI tput must be below max requirement. */
|
|
|
|
if (cfg->trans_mode != OMAP_DSS_DSI_BURST_MODE) {
|
|
|
|
if (dsi_tput > (u64)bitspp * req_pck_max)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hss = DIV_ROUND_UP(4, ndl);
|
|
|
|
|
|
|
|
if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
|
|
|
|
if (ndl == 3 && req_vm->hsw == 0)
|
|
|
|
hse = 1;
|
|
|
|
else
|
|
|
|
hse = DIV_ROUND_UP(4, ndl);
|
|
|
|
} else {
|
|
|
|
hse = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DSI htot to match the panel's nominal pck */
|
|
|
|
dsi_htot = div64_u64((u64)panel_htot * byteclk, req_pck_nom);
|
|
|
|
|
|
|
|
/* fail if there would be no time for blanking */
|
|
|
|
if (dsi_htot < hss + hse + dsi_hact)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* total DSI blanking needed to achieve panel's TL */
|
|
|
|
dsi_hbl = dsi_htot - dsi_hact;
|
|
|
|
|
|
|
|
/* DISPC htot to match the DSI TL */
|
|
|
|
dispc_htot = div64_u64((u64)dsi_htot * dispc_pck, byteclk);
|
|
|
|
|
|
|
|
/* verify that the DSI and DISPC TLs are the same */
|
|
|
|
if ((u64)dsi_htot * dispc_pck != (u64)dispc_htot * byteclk)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dispc_hbl = dispc_htot - xres;
|
|
|
|
|
|
|
|
/* setup DSI videomode */
|
|
|
|
|
|
|
|
dsi_vm = &ctx->dsi_vm;
|
|
|
|
memset(dsi_vm, 0, sizeof(*dsi_vm));
|
|
|
|
|
|
|
|
dsi_vm->hsclk = hsclk;
|
|
|
|
|
|
|
|
dsi_vm->ndl = ndl;
|
|
|
|
dsi_vm->bitspp = bitspp;
|
|
|
|
|
|
|
|
if (cfg->trans_mode != OMAP_DSS_DSI_PULSE_MODE) {
|
|
|
|
hsa = 0;
|
|
|
|
} else if (ndl == 3 && req_vm->hsw == 0) {
|
|
|
|
hsa = 0;
|
|
|
|
} else {
|
|
|
|
hsa = div64_u64((u64)req_vm->hsw * byteclk, req_pck_nom);
|
|
|
|
hsa = max(hsa - hse, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
hbp = div64_u64((u64)req_vm->hbp * byteclk, req_pck_nom);
|
|
|
|
hbp = max(hbp, 1);
|
|
|
|
|
|
|
|
hfp = dsi_hbl - (hss + hsa + hse + hbp);
|
|
|
|
if (hfp < 1) {
|
|
|
|
int t;
|
|
|
|
/* we need to take cycles from hbp */
|
|
|
|
|
|
|
|
t = 1 - hfp;
|
|
|
|
hbp = max(hbp - t, 1);
|
|
|
|
hfp = dsi_hbl - (hss + hsa + hse + hbp);
|
|
|
|
|
|
|
|
if (hfp < 1 && hsa > 0) {
|
|
|
|
/* we need to take cycles from hsa */
|
|
|
|
t = 1 - hfp;
|
|
|
|
hsa = max(hsa - t, 1);
|
|
|
|
hfp = dsi_hbl - (hss + hsa + hse + hbp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hfp < 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dsi_vm->hss = hss;
|
|
|
|
dsi_vm->hsa = hsa;
|
|
|
|
dsi_vm->hse = hse;
|
|
|
|
dsi_vm->hbp = hbp;
|
|
|
|
dsi_vm->hact = xres;
|
|
|
|
dsi_vm->hfp = hfp;
|
|
|
|
|
|
|
|
dsi_vm->vsa = req_vm->vsw;
|
|
|
|
dsi_vm->vbp = req_vm->vbp;
|
|
|
|
dsi_vm->vact = req_vm->y_res;
|
|
|
|
dsi_vm->vfp = req_vm->vfp;
|
|
|
|
|
|
|
|
dsi_vm->trans_mode = cfg->trans_mode;
|
|
|
|
|
|
|
|
dsi_vm->blanking_mode = 0;
|
|
|
|
dsi_vm->hsa_blanking_mode = 1;
|
|
|
|
dsi_vm->hfp_blanking_mode = 1;
|
|
|
|
dsi_vm->hbp_blanking_mode = 1;
|
|
|
|
|
|
|
|
dsi_vm->ddr_clk_always_on = cfg->ddr_clk_always_on;
|
|
|
|
dsi_vm->window_sync = 4;
|
|
|
|
|
|
|
|
/* setup DISPC videomode */
|
|
|
|
|
|
|
|
dispc_vm = &ctx->dispc_vm;
|
|
|
|
*dispc_vm = *req_vm;
|
|
|
|
dispc_vm->pixel_clock = dispc_pck / 1000;
|
|
|
|
|
|
|
|
if (cfg->trans_mode == OMAP_DSS_DSI_PULSE_MODE) {
|
|
|
|
hsa = div64_u64((u64)req_vm->hsw * dispc_pck,
|
|
|
|
req_pck_nom);
|
|
|
|
hsa = max(hsa, 1);
|
|
|
|
} else {
|
|
|
|
hsa = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
hbp = div64_u64((u64)req_vm->hbp * dispc_pck, req_pck_nom);
|
|
|
|
hbp = max(hbp, 1);
|
|
|
|
|
|
|
|
hfp = dispc_hbl - hsa - hbp;
|
|
|
|
if (hfp < 1) {
|
|
|
|
int t;
|
|
|
|
/* we need to take cycles from hbp */
|
|
|
|
|
|
|
|
t = 1 - hfp;
|
|
|
|
hbp = max(hbp - t, 1);
|
|
|
|
hfp = dispc_hbl - hsa - hbp;
|
|
|
|
|
|
|
|
if (hfp < 1) {
|
|
|
|
/* we need to take cycles from hsa */
|
|
|
|
t = 1 - hfp;
|
|
|
|
hsa = max(hsa - t, 1);
|
|
|
|
hfp = dispc_hbl - hsa - hbp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hfp < 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dispc_vm->hfp = hfp;
|
|
|
|
dispc_vm->hsw = hsa;
|
|
|
|
dispc_vm->hbp = hbp;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool dsi_vm_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
|
|
|
|
unsigned long pck, void *data)
|
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
|
|
|
|
|
|
|
ctx->dispc_cinfo.lck_div = lckd;
|
|
|
|
ctx->dispc_cinfo.pck_div = pckd;
|
|
|
|
ctx->dispc_cinfo.lck = lck;
|
|
|
|
ctx->dispc_cinfo.pck = pck;
|
|
|
|
|
|
|
|
if (dsi_vm_calc_blanking(ctx) == false)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#ifdef PRINT_VERBOSE_VM_TIMINGS
|
|
|
|
print_dispc_vm("dispc", &ctx->dispc_vm);
|
|
|
|
print_dsi_vm("dsi ", &ctx->dsi_vm);
|
|
|
|
print_dispc_vm("req ", ctx->config->timings);
|
|
|
|
print_dsi_dispc_vm("act ", &ctx->dsi_vm);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dsi_vm_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
|
|
|
unsigned long pck_max;
|
|
|
|
|
|
|
|
ctx->dsi_cinfo.regm_dispc = regm_dispc;
|
|
|
|
ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In burst mode we can let the dispc pck be arbitrarily high, but it
|
|
|
|
* limits our scaling abilities. So for now, don't aim too high.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ctx->config->trans_mode == OMAP_DSS_DSI_BURST_MODE)
|
|
|
|
pck_max = ctx->req_pck_max + 10000000;
|
|
|
|
else
|
|
|
|
pck_max = ctx->req_pck_max;
|
|
|
|
|
|
|
|
return dispc_div_calc(dispc, ctx->req_pck_min, pck_max,
|
|
|
|
dsi_vm_calc_dispc_cb, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dsi_vm_calc_pll_cb(int regn, int regm, unsigned long fint,
|
|
|
|
unsigned long pll, void *data)
|
|
|
|
{
|
|
|
|
struct dsi_clk_calc_ctx *ctx = data;
|
|
|
|
|
|
|
|
ctx->dsi_cinfo.regn = regn;
|
|
|
|
ctx->dsi_cinfo.regm = regm;
|
|
|
|
ctx->dsi_cinfo.fint = fint;
|
|
|
|
ctx->dsi_cinfo.clkin4ddr = pll;
|
|
|
|
|
|
|
|
return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->req_pck_min,
|
|
|
|
dsi_vm_calc_hsdiv_cb, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool dsi_vm_calc(struct dsi_data *dsi,
|
|
|
|
const struct omap_dss_dsi_config *cfg,
|
|
|
|
struct dsi_clk_calc_ctx *ctx)
|
|
|
|
{
|
|
|
|
const struct omap_video_timings *t = cfg->timings;
|
|
|
|
unsigned long clkin;
|
|
|
|
unsigned long pll_min;
|
|
|
|
unsigned long pll_max;
|
|
|
|
int ndl = dsi->num_lanes_used - 1;
|
|
|
|
int bitspp = dsi_get_pixel_size(cfg->pixel_format);
|
|
|
|
unsigned long byteclk_min;
|
|
|
|
|
|
|
|
clkin = clk_get_rate(dsi->sys_clk);
|
|
|
|
|
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
|
|
ctx->dsidev = dsi->pdev;
|
|
|
|
ctx->config = cfg;
|
|
|
|
|
|
|
|
ctx->dsi_cinfo.clkin = clkin;
|
|
|
|
|
|
|
|
/* these limits should come from the panel driver */
|
|
|
|
ctx->req_pck_min = t->pixel_clock * 1000 - 1000;
|
|
|
|
ctx->req_pck_nom = t->pixel_clock * 1000;
|
|
|
|
ctx->req_pck_max = t->pixel_clock * 1000 + 1000;
|
|
|
|
|
|
|
|
byteclk_min = div64_u64((u64)ctx->req_pck_min * bitspp, ndl * 8);
|
|
|
|
pll_min = max(cfg->hs_clk_min * 4, byteclk_min * 4 * 4);
|
|
|
|
|
|
|
|
if (cfg->trans_mode == OMAP_DSS_DSI_BURST_MODE) {
|
|
|
|
pll_max = cfg->hs_clk_max * 4;
|
|
|
|
} else {
|
|
|
|
unsigned long byteclk_max;
|
|
|
|
byteclk_max = div64_u64((u64)ctx->req_pck_max * bitspp,
|
|
|
|
ndl * 8);
|
|
|
|
|
|
|
|
pll_max = byteclk_max * 4 * 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dsi_pll_calc(dsi->pdev, clkin,
|
|
|
|
pll_min, pll_max,
|
|
|
|
dsi_vm_calc_pll_cb, ctx);
|
2012-08-16 19:32:00 +07:00
|
|
|
}
|
|
|
|
|
2013-03-06 16:10:29 +07:00
|
|
|
int omapdss_dsi_set_config(struct omap_dss_device *dssdev,
|
|
|
|
const struct omap_dss_dsi_config *config)
|
2012-08-13 23:43:39 +07:00
|
|
|
{
|
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2013-03-05 22:21:35 +07:00
|
|
|
struct dsi_clk_calc_ctx ctx;
|
|
|
|
bool ok;
|
|
|
|
int r;
|
2012-08-13 23:43:39 +07:00
|
|
|
|
|
|
|
mutex_lock(&dsi->lock);
|
|
|
|
|
2013-03-06 16:10:29 +07:00
|
|
|
dsi->pix_fmt = config->pixel_format;
|
|
|
|
dsi->mode = config->mode;
|
2012-08-09 16:53:43 +07:00
|
|
|
|
2013-03-05 22:21:35 +07:00
|
|
|
if (config->mode == OMAP_DSS_DSI_VIDEO_MODE)
|
|
|
|
ok = dsi_vm_calc(dsi, config, &ctx);
|
|
|
|
else
|
|
|
|
ok = dsi_cm_calc(dsi, config, &ctx);
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
DSSERR("failed to find suitable DSI clock settings\n");
|
|
|
|
r = -EINVAL;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi_pll_calc_dsi_fck(&ctx.dsi_cinfo);
|
|
|
|
|
|
|
|
r = dsi_lp_clock_calc(&ctx.dsi_cinfo, config->lp_clk_min,
|
|
|
|
config->lp_clk_max);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("failed to find suitable DSI LP clock settings\n");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->user_dsi_cinfo = ctx.dsi_cinfo;
|
|
|
|
dsi->user_dispc_cinfo = ctx.dispc_cinfo;
|
|
|
|
|
|
|
|
dsi->timings = ctx.dispc_vm;
|
|
|
|
dsi->vm_timings = ctx.dsi_vm;
|
2012-08-13 23:43:39 +07:00
|
|
|
|
|
|
|
mutex_unlock(&dsi->lock);
|
2012-08-09 16:53:43 +07:00
|
|
|
|
2013-03-06 16:10:29 +07:00
|
|
|
return 0;
|
2013-03-05 22:21:35 +07:00
|
|
|
err:
|
|
|
|
mutex_unlock(&dsi->lock);
|
|
|
|
|
|
|
|
return r;
|
2012-08-13 23:43:39 +07:00
|
|
|
}
|
2013-03-06 16:10:29 +07:00
|
|
|
EXPORT_SYMBOL(omapdss_dsi_set_config);
|
2012-08-13 23:43:39 +07:00
|
|
|
|
2013-02-13 16:23:54 +07:00
|
|
|
/*
|
|
|
|
* Return a hardcoded channel for the DSI output. This should work for
|
|
|
|
* current use cases, but this can be later expanded to either resolve
|
|
|
|
* the channel in some more dynamic manner, or get the channel as a user
|
|
|
|
* parameter.
|
|
|
|
*/
|
|
|
|
static enum omap_channel dsi_get_channel(int module_id)
|
|
|
|
{
|
|
|
|
switch (omapdss_get_version()) {
|
|
|
|
case OMAPDSS_VER_OMAP24xx:
|
|
|
|
DSSWARN("DSI not supported\n");
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
|
|
|
|
case OMAPDSS_VER_OMAP34xx_ES1:
|
|
|
|
case OMAPDSS_VER_OMAP34xx_ES3:
|
|
|
|
case OMAPDSS_VER_OMAP3630:
|
|
|
|
case OMAPDSS_VER_AM35xx:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
|
|
|
|
case OMAPDSS_VER_OMAP4430_ES1:
|
|
|
|
case OMAPDSS_VER_OMAP4430_ES2:
|
|
|
|
case OMAPDSS_VER_OMAP4:
|
|
|
|
switch (module_id) {
|
|
|
|
case 0:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
case 1:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD2;
|
|
|
|
default:
|
|
|
|
DSSWARN("unsupported module id\n");
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
}
|
|
|
|
|
|
|
|
case OMAPDSS_VER_OMAP5:
|
|
|
|
switch (module_id) {
|
|
|
|
case 0:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
case 1:
|
|
|
|
return OMAP_DSS_CHANNEL_LCD3;
|
|
|
|
default:
|
|
|
|
DSSWARN("unsupported module id\n");
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
DSSWARN("unsupported DSS version\n");
|
|
|
|
return OMAP_DSS_CHANNEL_LCD;
|
|
|
|
}
|
2012-08-13 23:43:39 +07:00
|
|
|
}
|
|
|
|
|
2011-03-02 14:05:53 +07:00
|
|
|
int omap_dsi_request_vc(struct omap_dss_device *dssdev, int *channel)
|
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2011-03-02 14:05:53 +07:00
|
|
|
int i;
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
|
|
|
|
if (!dsi->vc[i].dssdev) {
|
|
|
|
dsi->vc[i].dssdev = dssdev;
|
2011-03-02 14:05:53 +07:00
|
|
|
*channel = i;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DSSERR("cannot get VC for display %s", dssdev->name);
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dsi_request_vc);
|
|
|
|
|
|
|
|
int omap_dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id)
|
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2011-03-02 14:05:53 +07:00
|
|
|
if (vc_id < 0 || vc_id > 3) {
|
|
|
|
DSSERR("VC ID out of range\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (channel < 0 || channel > 3) {
|
|
|
|
DSSERR("Virtual Channel out of range\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
if (dsi->vc[channel].dssdev != dssdev) {
|
2011-03-02 14:05:53 +07:00
|
|
|
DSSERR("Virtual Channel not allocated to display %s\n",
|
|
|
|
dssdev->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->vc[channel].vc_id = vc_id;
|
2011-03-02 14:05:53 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dsi_set_vc_id);
|
|
|
|
|
|
|
|
void omap_dsi_release_vc(struct omap_dss_device *dssdev, int channel)
|
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2011-03-02 14:05:53 +07:00
|
|
|
if ((channel >= 0 && channel <= 3) &&
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->vc[channel].dssdev == dssdev) {
|
|
|
|
dsi->vc[channel].dssdev = NULL;
|
|
|
|
dsi->vc[channel].vc_id = 0;
|
2011-03-02 14:05:53 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dsi_release_vc);
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev)
|
2010-06-09 19:28:12 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 7, 1) != 1)
|
2011-03-02 13:27:25 +07:00
|
|
|
DSSERR("%s (%s) not active\n",
|
2011-04-12 15:22:23 +07:00
|
|
|
dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC),
|
|
|
|
dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC));
|
2010-06-09 19:28:12 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev)
|
2010-06-09 19:28:12 +07:00
|
|
|
{
|
2011-05-12 18:56:26 +07:00
|
|
|
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 8, 1) != 1)
|
2011-03-02 13:27:25 +07:00
|
|
|
DSSERR("%s (%s) not active\n",
|
2011-04-12 15:22:23 +07:00
|
|
|
dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI),
|
|
|
|
dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI));
|
2010-06-09 19:28:12 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
static void dsi_calc_clock_param_ranges(struct platform_device *dsidev)
|
2011-03-15 11:28:23 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
|
|
|
dsi->regn_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGN);
|
|
|
|
dsi->regm_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM);
|
|
|
|
dsi->regm_dispc_max =
|
|
|
|
dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DISPC);
|
|
|
|
dsi->regm_dsi_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DSI);
|
|
|
|
dsi->fint_min = dss_feat_get_param_min(FEAT_PARAM_DSIPLL_FINT);
|
|
|
|
dsi->fint_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_FINT);
|
|
|
|
dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV);
|
2011-03-15 11:28:23 +07:00
|
|
|
}
|
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
static int dsi_get_clocks(struct platform_device *dsidev)
|
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
struct clk *clk;
|
|
|
|
|
2013-04-02 18:33:00 +07:00
|
|
|
clk = devm_clk_get(&dsidev->dev, "fck");
|
2011-05-27 14:52:19 +07:00
|
|
|
if (IS_ERR(clk)) {
|
|
|
|
DSSERR("can't get fck\n");
|
|
|
|
return PTR_ERR(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->dss_clk = clk;
|
|
|
|
|
2013-04-02 18:33:00 +07:00
|
|
|
clk = devm_clk_get(&dsidev->dev, "sys_clk");
|
2011-05-27 14:52:19 +07:00
|
|
|
if (IS_ERR(clk)) {
|
|
|
|
DSSERR("can't get sys_clk\n");
|
|
|
|
return PTR_ERR(clk);
|
|
|
|
}
|
|
|
|
|
|
|
|
dsi->sys_clk = clk;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:06 +07:00
|
|
|
static struct omap_dss_device *dsi_find_dssdev(struct platform_device *pdev)
|
2012-05-02 18:55:12 +07:00
|
|
|
{
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(pdev);
|
2012-10-29 17:40:46 +07:00
|
|
|
const char *def_disp_name = omapdss_get_default_display_name();
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
struct omap_dss_device *def_dssdev;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
def_dssdev = NULL;
|
2012-05-02 18:55:12 +07:00
|
|
|
|
|
|
|
for (i = 0; i < pdata->num_devices; ++i) {
|
|
|
|
struct omap_dss_device *dssdev = pdata->devices[i];
|
|
|
|
|
|
|
|
if (dssdev->type != OMAP_DISPLAY_TYPE_DSI)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (dssdev->phy.dsi.module != dsi->module_id)
|
|
|
|
continue;
|
|
|
|
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
if (def_dssdev == NULL)
|
|
|
|
def_dssdev = dssdev;
|
|
|
|
|
|
|
|
if (def_disp_name != NULL &&
|
|
|
|
strcmp(dssdev->name, def_disp_name) == 0) {
|
|
|
|
def_dssdev = dssdev;
|
|
|
|
break;
|
2012-05-02 18:55:12 +07:00
|
|
|
}
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
}
|
2012-05-02 18:55:12 +07:00
|
|
|
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
return def_dssdev;
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:41 +07:00
|
|
|
static int dsi_probe_pdata(struct platform_device *dsidev)
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
{
|
2012-12-07 17:50:08 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
2012-09-10 17:58:29 +07:00
|
|
|
struct omap_dss_device *plat_dssdev;
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
struct omap_dss_device *dssdev;
|
|
|
|
int r;
|
|
|
|
|
2012-09-10 17:58:29 +07:00
|
|
|
plat_dssdev = dsi_find_dssdev(dsidev);
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
|
2012-09-10 17:58:29 +07:00
|
|
|
if (!plat_dssdev)
|
2013-04-26 17:47:41 +07:00
|
|
|
return 0;
|
2012-09-10 17:58:29 +07:00
|
|
|
|
2013-05-03 17:42:24 +07:00
|
|
|
r = dsi_regulator_init(dsidev);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2012-09-10 17:58:29 +07:00
|
|
|
dssdev = dss_alloc_and_init_device(&dsidev->dev);
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
if (!dssdev)
|
2013-04-26 17:47:41 +07:00
|
|
|
return -ENOMEM;
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
|
2012-09-10 17:58:29 +07:00
|
|
|
dss_copy_device_pdata(dssdev, plat_dssdev);
|
|
|
|
|
2012-12-07 17:50:08 +07:00
|
|
|
r = omapdss_output_set_device(&dsi->output, dssdev);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("failed to connect output to new device: %s\n",
|
|
|
|
dssdev->name);
|
|
|
|
dss_put_device(dssdev);
|
2013-04-26 17:47:41 +07:00
|
|
|
return r;
|
2012-12-07 17:50:08 +07:00
|
|
|
}
|
|
|
|
|
2012-09-10 17:58:29 +07:00
|
|
|
r = dss_add_device(dssdev);
|
OMAPDSS: register only one display device per output
We have boards with multiple panel devices connected to the same
physical output, of which only one panel can be enabled at one time.
Examples of these are Overo, where you can use different daughter boards
that have different LCDs, and 3430SDP which has an LCD and a DVI output
and a physical switch to select the active display.
These are supported by omapdss so that we add all the possible display
devices at probe, but the displays are inactive until somebody enables
one. At this point the panel driver starts using the DSS, thus reserving
the physcal resource and excluding the other panels.
This is problematic:
- Panel drivers can't allocate their resources properly at probe(),
because the resources can be shared with other panels. Thus they can
be only reserved at enable time.
- Managing this in omapdss is confusing. It's not natural to have
child devices, which may not even exist (for example, a daughterboard
that is not connected).
Only some boards have multiple displays per output, and of those, only
very few have possibility of switching the display during runtime.
Because of the above points:
- We don't want to make omapdss and all the panel drivers more complex
just because some boards have complex setups.
- Only few boards support runtime switching, and afaik even then it's
not required. So we don't need to support runtime switching.
Thus we'll change to a model where we will have only one display device
per output and this cannot be (currently) changed at runtime. We'll
still have the possibility to select the display from multiple options
during boot with the default display option.
This patch accomplishes the above by changing how the output drivers
register the display device. Instead of registering all the devices
given from the board file, we'll only register one. If the default
display option is set, the output driver selects that display from its
displays. If the default display is not set, or the default display is
not one of the output's displays, the output driver selects the first
display.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-09-06 18:29:31 +07:00
|
|
|
if (r) {
|
|
|
|
DSSERR("device %s register failed: %d\n", dssdev->name, r);
|
2012-12-07 17:50:08 +07:00
|
|
|
omapdss_output_unset_device(&dsi->output);
|
2012-09-10 17:58:29 +07:00
|
|
|
dss_put_device(dssdev);
|
2013-04-26 17:47:06 +07:00
|
|
|
return r;
|
2012-05-02 18:55:12 +07:00
|
|
|
}
|
2013-04-26 17:47:06 +07:00
|
|
|
|
|
|
|
return 0;
|
2012-05-02 18:55:12 +07:00
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:06 +07:00
|
|
|
static void dsi_init_output(struct platform_device *dsidev)
|
2012-09-26 18:00:49 +07:00
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
struct omap_dss_device *out = &dsi->output;
|
2012-09-26 18:00:49 +07:00
|
|
|
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
out->dev = &dsidev->dev;
|
2012-09-26 18:00:49 +07:00
|
|
|
out->id = dsi->module_id == 0 ?
|
|
|
|
OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
|
|
|
|
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
out->output_type = OMAP_DISPLAY_TYPE_DSI;
|
2013-02-18 18:06:01 +07:00
|
|
|
out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
|
2013-02-13 16:23:54 +07:00
|
|
|
out->dispc_channel = dsi_get_channel(dsi->module_id);
|
2013-05-03 15:42:18 +07:00
|
|
|
out->owner = THIS_MODULE;
|
2012-09-26 18:00:49 +07:00
|
|
|
|
|
|
|
dss_register_output(out);
|
|
|
|
}
|
|
|
|
|
2013-04-26 17:47:41 +07:00
|
|
|
static void dsi_uninit_output(struct platform_device *dsidev)
|
2012-09-26 18:00:49 +07:00
|
|
|
{
|
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
OMAPDSS: combine omap_dss_output into omap_dss_device
We currently have omap_dss_device, which represents an external display
device, sometimes an external encoder, sometimes a panel. Then we have
omap_dss_output, which represents DSS's output encoder.
In the future with new display device model, we construct a video
pipeline from the display blocks. To accomplish this, all the blocks
need to be presented by the same entity.
Thus, this patch combines omap_dss_output into omap_dss_device. Some of
the fields in omap_dss_output are already found in omap_dss_device, but
some are not. This means we'll have DSS output specific fields in
omap_dss_device, which is not very nice. However, it is easier to just
keep those output specific fields there for now, and after transition to
new display device model is made, they can be cleaned up easier than
could be done now.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2013-04-19 19:09:34 +07:00
|
|
|
struct omap_dss_device *out = &dsi->output;
|
2012-09-26 18:00:49 +07:00
|
|
|
|
|
|
|
dss_unregister_output(out);
|
|
|
|
}
|
|
|
|
|
2011-05-16 17:52:51 +07:00
|
|
|
/* DSI1 HW IP initialisation */
|
2013-04-26 17:47:06 +07:00
|
|
|
static int omap_dsihw_probe(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
|
|
|
u32 rev;
|
2012-03-09 21:07:39 +07:00
|
|
|
int r, i;
|
2011-01-24 13:22:04 +07:00
|
|
|
struct resource *dsi_mem;
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi;
|
|
|
|
|
2012-01-24 20:00:45 +07:00
|
|
|
dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL);
|
2012-01-25 18:31:04 +07:00
|
|
|
if (!dsi)
|
|
|
|
return -ENOMEM;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-03-09 21:07:39 +07:00
|
|
|
dsi->module_id = dsidev->id;
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->pdev = dsidev;
|
|
|
|
dev_set_drvdata(&dsidev->dev, dsi);
|
2011-05-12 18:56:26 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_init(&dsi->irq_lock);
|
|
|
|
spin_lock_init(&dsi->errors_lock);
|
|
|
|
dsi->errors = 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2009-12-17 19:35:21 +07:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
2011-05-12 18:56:27 +07:00
|
|
|
spin_lock_init(&dsi->irq_stats_lock);
|
|
|
|
dsi->irq_stats.last_reset = jiffies;
|
2009-12-17 19:35:21 +07:00
|
|
|
#endif
|
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
mutex_init(&dsi->lock);
|
|
|
|
sema_init(&dsi->bus_lock, 1);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-08-22 03:18:23 +07:00
|
|
|
INIT_DEFERRABLE_WORK(&dsi->framedone_timeout_work,
|
|
|
|
dsi_framedone_timeout_work_callback);
|
2010-01-12 19:16:41 +07:00
|
|
|
|
2009-10-28 16:59:56 +07:00
|
|
|
#ifdef DSI_CATCH_MISSING_TE
|
2011-05-12 18:56:27 +07:00
|
|
|
init_timer(&dsi->te_timer);
|
|
|
|
dsi->te_timer.function = dsi_te_timeout;
|
|
|
|
dsi->te_timer.data = 0;
|
2009-10-28 16:59:56 +07:00
|
|
|
#endif
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0);
|
2011-01-24 13:22:04 +07:00
|
|
|
if (!dsi_mem) {
|
|
|
|
DSSERR("can't get IORESOURCE_MEM DSI\n");
|
2012-01-25 18:31:04 +07:00
|
|
|
return -EINVAL;
|
2011-01-24 13:22:04 +07:00
|
|
|
}
|
2012-01-25 18:31:04 +07:00
|
|
|
|
2012-01-24 20:00:45 +07:00
|
|
|
dsi->base = devm_ioremap(&dsidev->dev, dsi_mem->start,
|
|
|
|
resource_size(dsi_mem));
|
2011-05-12 18:56:27 +07:00
|
|
|
if (!dsi->base) {
|
2009-10-28 16:59:56 +07:00
|
|
|
DSSERR("can't ioremap DSI\n");
|
2012-01-25 18:31:04 +07:00
|
|
|
return -ENOMEM;
|
2009-10-28 16:59:56 +07:00
|
|
|
}
|
2012-01-25 18:31:04 +07:00
|
|
|
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->irq = platform_get_irq(dsi->pdev, 0);
|
|
|
|
if (dsi->irq < 0) {
|
2011-02-23 15:41:03 +07:00
|
|
|
DSSERR("platform_get_irq failed\n");
|
2012-01-25 18:31:04 +07:00
|
|
|
return -ENODEV;
|
2011-02-23 15:41:03 +07:00
|
|
|
}
|
|
|
|
|
2012-01-24 20:00:45 +07:00
|
|
|
r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler,
|
|
|
|
IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev);
|
2011-02-23 15:41:03 +07:00
|
|
|
if (r < 0) {
|
|
|
|
DSSERR("request_irq failed\n");
|
2012-01-25 18:31:04 +07:00
|
|
|
return r;
|
2011-02-23 15:41:03 +07:00
|
|
|
}
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-03-02 14:05:53 +07:00
|
|
|
/* DSI VCs initialization */
|
2011-05-12 18:56:27 +07:00
|
|
|
for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
|
2011-08-22 13:28:08 +07:00
|
|
|
dsi->vc[i].source = DSI_VC_SOURCE_L4;
|
2011-05-12 18:56:27 +07:00
|
|
|
dsi->vc[i].dssdev = NULL;
|
|
|
|
dsi->vc[i].vc_id = 0;
|
2011-03-02 14:05:53 +07:00
|
|
|
}
|
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
dsi_calc_clock_param_ranges(dsidev);
|
2011-03-15 11:28:23 +07:00
|
|
|
|
2012-01-25 18:31:04 +07:00
|
|
|
r = dsi_get_clocks(dsidev);
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
pm_runtime_enable(&dsidev->dev);
|
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
r = dsi_runtime_get(dsidev);
|
|
|
|
if (r)
|
2012-01-25 18:31:04 +07:00
|
|
|
goto err_runtime_get;
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2011-05-12 18:56:26 +07:00
|
|
|
rev = dsi_read_reg(dsidev, DSI_REVISION);
|
|
|
|
dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
|
2009-10-28 16:59:56 +07:00
|
|
|
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
|
|
|
|
|
2011-10-12 19:05:59 +07:00
|
|
|
/* DSI on OMAP3 doesn't have register DSI_GNQ, set number
|
|
|
|
* of data to 3 by default */
|
|
|
|
if (dss_has_feature(FEAT_DSI_GNQ))
|
|
|
|
/* NB_DATA_LANES */
|
|
|
|
dsi->num_lanes_supported = 1 + REG_GET(dsidev, DSI_GNQ, 11, 9);
|
|
|
|
else
|
|
|
|
dsi->num_lanes_supported = 3;
|
2011-05-16 16:47:08 +07:00
|
|
|
|
2013-03-05 15:37:02 +07:00
|
|
|
dsi->line_buffer_size = dsi_get_line_buf_size(dsidev);
|
|
|
|
|
2012-09-26 18:00:49 +07:00
|
|
|
dsi_init_output(dsidev);
|
|
|
|
|
2013-03-14 20:47:29 +07:00
|
|
|
if (dsidev->dev.platform_data) {
|
|
|
|
r = dsi_probe_pdata(dsidev);
|
|
|
|
if (r)
|
|
|
|
goto err_probe;
|
2013-04-26 17:47:41 +07:00
|
|
|
}
|
OMAPDSS: interface drivers register their panel devices
Currently the higher level omapdss platform driver gets the list of
displays in its platform data, and uses that list to create the
omap_dss_device for each display.
With DT, the logical way to do the above is to list the displays under
each individual output, i.e. we'd have "dpi" node, under which we would
have the display that uses DPI. In other words, each output driver
handles the displays that use that particular output.
To make the current code ready for DT, this patch modifies the output
drivers so that each of them creates the display devices which use that
output. However, instead of changing the platform data to suit this
method, each output driver is passed the full list of displays, and the
drivers pick the displays that are meant for them. This allows us to
keep the old platform data, and thus we avoid the need to change the
board files.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-03-01 20:45:53 +07:00
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
dsi_runtime_put(dsidev);
|
2009-10-28 16:59:56 +07:00
|
|
|
|
2012-03-09 21:07:39 +07:00
|
|
|
if (dsi->module_id == 0)
|
2012-03-02 23:01:07 +07:00
|
|
|
dss_debugfs_create_file("dsi1_regs", dsi1_dump_regs);
|
2012-03-09 21:07:39 +07:00
|
|
|
else if (dsi->module_id == 1)
|
2012-03-02 23:01:07 +07:00
|
|
|
dss_debugfs_create_file("dsi2_regs", dsi2_dump_regs);
|
|
|
|
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
|
2012-03-09 21:07:39 +07:00
|
|
|
if (dsi->module_id == 0)
|
2012-03-02 23:01:07 +07:00
|
|
|
dss_debugfs_create_file("dsi1_irqs", dsi1_dump_irqs);
|
2012-03-09 21:07:39 +07:00
|
|
|
else if (dsi->module_id == 1)
|
2012-03-02 23:01:07 +07:00
|
|
|
dss_debugfs_create_file("dsi2_irqs", dsi2_dump_irqs);
|
|
|
|
#endif
|
2009-10-28 16:59:56 +07:00
|
|
|
return 0;
|
2011-05-27 14:52:19 +07:00
|
|
|
|
2013-03-14 20:47:29 +07:00
|
|
|
err_probe:
|
|
|
|
dsi_runtime_put(dsidev);
|
|
|
|
dsi_uninit_output(dsidev);
|
2012-01-25 18:31:04 +07:00
|
|
|
err_runtime_get:
|
2011-05-27 14:52:19 +07:00
|
|
|
pm_runtime_disable(&dsidev->dev);
|
2009-10-28 16:59:56 +07:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2012-02-17 22:41:13 +07:00
|
|
|
static int __exit omap_dsihw_remove(struct platform_device *dsidev)
|
2009-10-28 16:59:56 +07:00
|
|
|
{
|
2011-05-12 18:56:27 +07:00
|
|
|
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
|
|
|
|
|
2011-05-16 17:52:51 +07:00
|
|
|
WARN_ON(dsi->scp_clk_refcount > 0);
|
|
|
|
|
2012-09-10 17:58:29 +07:00
|
|
|
dss_unregister_child_devices(&dsidev->dev);
|
OMAPDSS: interface drivers register their panel devices
Currently the higher level omapdss platform driver gets the list of
displays in its platform data, and uses that list to create the
omap_dss_device for each display.
With DT, the logical way to do the above is to list the displays under
each individual output, i.e. we'd have "dpi" node, under which we would
have the display that uses DPI. In other words, each output driver
handles the displays that use that particular output.
To make the current code ready for DT, this patch modifies the output
drivers so that each of them creates the display devices which use that
output. However, instead of changing the platform data to suit this
method, each output driver is passed the full list of displays, and the
drivers pick the displays that are meant for them. This allows us to
keep the old platform data, and thus we avoid the need to change the
board files.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-03-01 20:45:53 +07:00
|
|
|
|
2012-09-26 18:00:49 +07:00
|
|
|
dsi_uninit_output(dsidev);
|
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
pm_runtime_disable(&dsidev->dev);
|
|
|
|
|
2013-05-03 17:42:24 +07:00
|
|
|
if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {
|
|
|
|
regulator_disable(dsi->vdds_dsi_reg);
|
|
|
|
dsi->vdds_dsi_enabled = false;
|
2011-01-24 13:22:02 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-27 14:52:19 +07:00
|
|
|
static int dsi_runtime_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
dispc_runtime_put();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsi_runtime_resume(struct device *dev)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = dispc_runtime_get();
|
|
|
|
if (r)
|
2012-02-17 22:58:04 +07:00
|
|
|
return r;
|
2011-05-27 14:52:19 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dev_pm_ops dsi_pm_ops = {
|
|
|
|
.runtime_suspend = dsi_runtime_suspend,
|
|
|
|
.runtime_resume = dsi_runtime_resume,
|
|
|
|
};
|
|
|
|
|
2011-08-03 18:00:57 +07:00
|
|
|
static struct platform_driver omap_dsihw_driver = {
|
2013-04-26 17:47:06 +07:00
|
|
|
.probe = omap_dsihw_probe,
|
2012-02-17 22:41:13 +07:00
|
|
|
.remove = __exit_p(omap_dsihw_remove),
|
2011-01-24 13:22:02 +07:00
|
|
|
.driver = {
|
2011-08-03 18:00:57 +07:00
|
|
|
.name = "omapdss_dsi",
|
2011-01-24 13:22:02 +07:00
|
|
|
.owner = THIS_MODULE,
|
2011-05-27 14:52:19 +07:00
|
|
|
.pm = &dsi_pm_ops,
|
2011-01-24 13:22:02 +07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2012-02-17 22:41:13 +07:00
|
|
|
int __init dsi_init_platform_driver(void)
|
2011-01-24 13:22:02 +07:00
|
|
|
{
|
2013-04-26 17:47:06 +07:00
|
|
|
return platform_driver_register(&omap_dsihw_driver);
|
2011-01-24 13:22:02 +07:00
|
|
|
}
|
|
|
|
|
2012-02-17 22:41:13 +07:00
|
|
|
void __exit dsi_uninit_platform_driver(void)
|
2011-01-24 13:22:02 +07:00
|
|
|
{
|
2012-02-23 20:32:37 +07:00
|
|
|
platform_driver_unregister(&omap_dsihw_driver);
|
2011-01-24 13:22:02 +07:00
|
|
|
}
|