blob: 58af5cf539e5ffffb6e88c8bea030cb9794f9326 [file] [log] [blame]
/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Header file for Samsung EXYNOS SoC MIPI-DSI Master driver.
*
* 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.
*/
#ifndef __SAMSUNG_DSIM_H__
#define __SAMSUNG_DSIM_H__
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <media/v4l2-subdev.h>
#include "./panels/decon_lcd.h"
#if defined(CONFIG_SOC_EXYNOS8895)
#include "regs-dsim.h"
#else
#include "regs-dsim_8890.h"
#endif
#include <video/mipi_display.h>
#if defined(CONFIG_EXYNOS_DECON_LCD_S6E3HA6)
#include "./panels/s6e3ha6_param.h"
#elif defined(CONFIG_EXYNOS_DECON_LCD_S6E3HF4)
#include "./panels/s6e3hf4_param.h"
#endif
extern int dsim_log_level;
#define DSIM_MODULE_NAME "exynos-dsim"
#define DSIM_DDI_ID_LEN 3
#if defined(CONFIG_SOC_EXYNOS8895)
#define DSIM_PIXEL_FORMAT_RGB24 0x0
#define DSIM_PIXEL_FORMAT_RGB18_PACKED 0x1
#define DSIM_PIXEL_FORMAT_RGB18 0x2
#else
#define DSIM_PIXEL_FORMAT_RGB24 0x7
#define DSIM_PIXEL_FORMAT_RGB18 0x6
#define DSIM_PIXEL_FORMAT_RGB18_PACKED 0x5
#endif
#define DSIM_RX_FIFO_MAX_DEPTH 64
#define MAX_DSIM_CNT 2
#define MAX_DSIM_DATALANE_CNT 4
#define MIPI_WR_TIMEOUT msecs_to_jiffies(33)
#define MIPI_RD_TIMEOUT msecs_to_jiffies(100)
#define dsim_err(fmt, ...) \
do { \
if (dsim_log_level >= 3) { \
pr_err(pr_fmt(fmt), ##__VA_ARGS__); \
exynos_ss_printk(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define dsim_warn(fmt, ...) \
do { \
if (dsim_log_level >= 4) { \
pr_warn(pr_fmt(fmt), ##__VA_ARGS__); \
exynos_ss_printk(fmt, ##__VA_ARGS__); \
} \
} while (0)
#define dsim_info(fmt, ...) \
do { \
if (dsim_log_level >= 6) \
pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define dsim_dbg(fmt, ...) \
do { \
if (dsim_log_level >= 7) \
pr_info(pr_fmt(fmt), ##__VA_ARGS__); \
} while (0)
#define call_panel_ops(q, op, args...) \
(((q)->panel_ops->op) ? ((q)->panel_ops->op(args)) : 0)
extern struct dsim_device *dsim_drvdata[MAX_DSIM_CNT];
extern struct dsim_lcd_driver s6e3ha2k_mipi_lcd_driver;
extern struct dsim_lcd_driver emul_disp_mipi_lcd_driver;
extern struct dsim_lcd_driver s6e3hf4_mipi_lcd_driver;
extern struct dsim_lcd_driver s6e3ha6_mipi_lcd_driver;
#if defined(CONFIG_SOC_EXYNOS8895)
/* define video timer interrupt */
enum {
DSIM_VBP = 0,
DSIM_VSYNC,
DSIM_V_ACTIVE,
DSIM_VFP,
};
/* define dsi bist pattern */
enum {
DSIM_COLOR_BAR = 0,
DSIM_GRAY_GRADATION,
DSIM_USER_DEFINED,
DSIM_PRB7_RANDOM,
};
#endif
/* define DSI lane types. */
enum {
DSIM_LANE_CLOCK = (1 << 0),
DSIM_LANE_DATA0 = (1 << 1),
DSIM_LANE_DATA1 = (1 << 2),
DSIM_LANE_DATA2 = (1 << 3),
DSIM_LANE_DATA3 = (1 << 4),
};
/* DSI Error report bit definitions */
enum {
MIPI_DSI_ERR_SOT = (1 << 0),
MIPI_DSI_ERR_SOT_SYNC = (1 << 1),
MIPI_DSI_ERR_EOT_SYNC = (1 << 2),
MIPI_DSI_ERR_ESCAPE_MODE_ENTRY_CMD = (1 << 3),
MIPI_DSI_ERR_LOW_POWER_TRANSMIT_SYNC = (1 << 4),
MIPI_DSI_ERR_HS_RECEIVE_TIMEOUT = (1 << 5),
MIPI_DSI_ERR_FALSE_CONTROL = (1 << 6),
/* Bit 7 is reserved */
MIPI_DSI_ERR_ECC_SINGLE_BIT = (1 << 8),
MIPI_DSI_ERR_ECC_MULTI_BIT = (1 << 9),
MIPI_DSI_ERR_CHECKSUM = (1 << 10),
MIPI_DSI_ERR_DATA_TYPE_NOT_RECOGNIZED = (1 << 11),
MIPI_DSI_ERR_VCHANNEL_ID_INVALID = (1 << 12),
MIPI_DSI_ERR_INVALID_TRANSMIT_LENGTH = (1 << 13),
/* Bit 14 is reserved */
MIPI_DSI_ERR_PROTOCAL_VIOLATION = (1 << 15),
/* DSI_PROTOCAL_VIOLATION[15] is for protocol violation that is caused EoTp
* missing So this bit is egnored because of not supportung @S.LSI AP */
/* FALSE_ERROR_CONTROL[6] is for detect invalid escape or turnaround sequence.
* This bit is not supporting @S.LSI AP because of non standard
* ULPS enter/exit sequence during power-gating */
/* Bit [14],[7] is reserved */
MIPI_DSI_ERR_BIT_MASK = (0x3f3f), /* Error_Range[13:0] */
};
/* operation state of dsim driver */
enum dsim_state {
DSIM_STATE_INIT,
DSIM_STATE_ON, /* HS clock was enabled. */
DSIM_STATE_ULPS, /* DSIM was entered ULPS state */
DSIM_STATE_OFF /* DSIM is suspend state */
};
#if defined(CONFIG_SOC_EXYNOS8895)
enum dphy_charic_value {
M_PLL_CTRL1,
M_PLL_CTRL2,
B_DPHY_CTRL2,
B_DPHY_CTRL3,
B_DPHY_CTRL4,
M_DPHY_CTRL1,
M_DPHY_CTRL2,
M_DPHY_CTRL3,
M_DPHY_CTRL4
};
#endif
struct dsim_pll_param {
u32 p;
u32 m;
u32 s;
u32 pll_freq; /* in/out parameter: Mhz */
};
struct dsim_clks {
u32 hs_clk;
u32 esc_clk;
u32 byte_clk;
#if defined(CONFIG_SOC_EXYNOS8895)
u32 word_clk;
#endif
};
struct dphy_timing_value {
u32 bps;
u32 clk_prepare;
u32 clk_zero;
u32 clk_post;
u32 clk_trail;
u32 hs_prepare;
u32 hs_zero;
u32 hs_trail;
u32 lpx;
u32 hs_exit;
u32 b_dphyctl;
};
struct dsim_resources {
struct clk *pclk;
struct clk *dphy_esc;
struct clk *dphy_byte;
struct clk *rgb_vclk0;
struct clk *pclk_disp;
int lcd_power[2];
int lcd_reset;
int irq;
void __iomem *regs;
void __iomem *ss_regs;
void __iomem *ver_regs;
};
struct dsim_device {
int id;
enum dsim_state state;
struct device *dev;
struct dsim_resources res;
unsigned int data_lane;
u32 data_lane_cnt;
struct phy *phy;
spinlock_t slock;
struct dsim_lcd_driver *panel_ops;
struct decon_lcd lcd_info;
struct v4l2_subdev sd;
struct dsim_clks clks;
struct timer_list cmd_timer;
struct mutex cmd_lock;
struct completion ph_wr_comp;
struct completion rd_comp;
int total_underrun_cnt;
int version;
};
struct dsim_lcd_driver {
int (*probe)(struct dsim_device *dsim);
int (*suspend)(struct dsim_device *dsim);
int (*displayon)(struct dsim_device *dsim);
int (*resume)(struct dsim_device *dsim);
int (*dump)(struct dsim_device *dsim);
};
int dsim_write_data(struct dsim_device *dsim, u32 id, unsigned long d0, u32 d1);
int dsim_read_data(struct dsim_device *dsim, u32 id, u32 addr, u32 cnt, u8 *buf);
int dsim_wait_for_cmd_done(struct dsim_device *dsim);
static inline struct dsim_device *get_dsim_drvdata(u32 id)
{
return dsim_drvdata[id];
}
static inline int dsim_rd_data(u32 id, u32 cmd_id, u32 addr, u32 size, u8 *buf)
{
int ret;
struct dsim_device *dsim = get_dsim_drvdata(id);
ret = dsim_read_data(dsim, cmd_id, addr, size, buf);
if (ret)
return ret;
return 0;
}
static inline int dsim_wr_data(u32 id, u32 cmd_id, unsigned long d0, u32 d1)
{
int ret;
struct dsim_device *dsim = get_dsim_drvdata(id);
u32 max_pl_size = 60;
u8 *param;
int cmd_cnt, i, j;
u8 offset_addr;
u8 pl_data[60] = {0, };
if (dsim->version) {
ret = dsim_write_data(dsim, cmd_id, d0, d1);
if (ret)
return ret;
} else {
switch (cmd_id) {
case MIPI_DSI_GENERIC_LONG_WRITE:
case MIPI_DSI_DCS_LONG_WRITE:
case MIPI_DSI_DSC_PPS:
if (d1 > max_pl_size) {
param = (u8 *)d0;
cmd_cnt = d1 / max_pl_size;
ret = dsim_write_data(dsim,
MIPI_DSI_DCS_LONG_WRITE,
(unsigned long)SEQ_TEST_KEY_ON_F0,
sizeof(SEQ_TEST_KEY_ON_F0));
if (ret)
return ret;
if (cmd_id == MIPI_DSI_DSC_PPS)
offset_addr = 0x9E;
else
offset_addr = *param;
for (i = 0; i < cmd_cnt; i++) {
for (j = 0; j < max_pl_size; j++) {
if (!i) {
pl_data[j] = *param++;
} else {
cmd_id = MIPI_DSI_DCS_LONG_WRITE;
if (j == 0)
pl_data[j] = offset_addr;
else
pl_data[j] = *param++;
/* send global command with next number of param*/
SEQ_TSET_GLOBAL[1] = (i * max_pl_size) - (i - 1);
ret = dsim_write_data(dsim,
MIPI_DSI_DCS_LONG_WRITE,
(unsigned long) SEQ_TSET_GLOBAL,
sizeof(SEQ_TSET_GLOBAL));
if (ret)
return ret;
}
}
ret = dsim_write_data(dsim, cmd_id,
(unsigned long)pl_data, max_pl_size);
if (ret)
return ret;
}
if (d1 % max_pl_size) {
cmd_id = MIPI_DSI_DCS_LONG_WRITE;
for (j = 0; j < (d1 % max_pl_size + i); j++) {
if (j == 0)
pl_data[j] = offset_addr;
else
pl_data[j] = *param++;
}
/* send global command with next number of param*/
SEQ_TSET_GLOBAL[1] = i * max_pl_size - (i - 1);
ret = dsim_write_data(dsim,
MIPI_DSI_DCS_LONG_WRITE,
(unsigned long) SEQ_TSET_GLOBAL, sizeof(SEQ_TSET_GLOBAL));
if (ret)
return ret;
ret = dsim_write_data(dsim, cmd_id,
(unsigned long)pl_data, (d1 % max_pl_size) + i);
if (ret)
return ret;
}
} else {
ret = dsim_write_data(dsim, cmd_id, d0, d1);
}
break;
default:
ret = dsim_write_data(dsim, cmd_id, d0, d1);
if (ret)
return ret;
break;
}
}
return 0;
}
static inline int dsim_wait_for_cmd_completion(u32 id)
{
int ret;
struct dsim_device *dsim = get_dsim_drvdata(id);
ret = dsim_wait_for_cmd_done(dsim);
return ret;
}
/* register access subroutines */
static inline u32 dsim_read(u32 id, u32 reg_id)
{
struct dsim_device *dsim = get_dsim_drvdata(id);
return readl(dsim->res.regs + reg_id);
}
static inline u32 dsim_read_mask(u32 id, u32 reg_id, u32 mask)
{
u32 val = dsim_read(id, reg_id);
val &= (mask);
return val;
}
static inline void dsim_write(u32 id, u32 reg_id, u32 val)
{
struct dsim_device *dsim = get_dsim_drvdata(id);
writel(val, dsim->res.regs + reg_id);
}
static inline void dsim_write_mask(u32 id, u32 reg_id, u32 val, u32 mask)
{
struct dsim_device *dsim = get_dsim_drvdata(id);
u32 old = dsim_read(id, reg_id);
val = (val & mask) | (old & ~mask);
writel(val, dsim->res.regs + reg_id);
}
/* CAL APIs list */
int dsim_reg_init(u32 id, struct decon_lcd *lcd_info,
u32 data_lane_cnt, struct dsim_clks *clks);
int dsim_reg_set_clocks(u32 id, struct dsim_clks *clks,
struct stdphy_pms *dphy_pms, u32 en);
int dsim_reg_set_lanes(u32 id, u32 lanes, u32 en);
void dsim_reg_set_int(u32 id, u32 en);
u32 dsim_reg_rx_fifo_is_empty(u32 id);
u32 dsim_reg_get_rx_fifo(u32 id);
int dsim_reg_rx_err_handler(u32 id, u32 rx_fifo);
void dsim_reg_sw_reset(u32 id);
void dsim_reg_dphy_reset(u32 id);
int dsim_reg_exit_ulps_and_start(u32 id, u32 ddi_type, u32 lanes);
int dsim_reg_stop_and_enter_ulps(u32 id, u32 ddi_type, u32 lanes);
void dsim_reg_start(u32 id);
void dsim_reg_stop(u32 id, u32 lanes);
void dsim_reg_wr_tx_payload(u32 id, u32 payload);
u32 dsim_reg_header_fifo_is_empty(u32 id);
void dsim_reg_clear_int(u32 id, u32 int_src);
void dsim_reg_set_fifo_ctrl(u32 id, u32 cfg);
void dsim_reg_enable_shadow_read(u32 id, u32 en);
u32 dsim_reg_is_writable_fifo_state(u32 id);
void dsim_reg_wr_tx_header(u32 id, u32 data_id, unsigned long data0, u32 data1, u32 bta_type);
void dsim_set_bist(u32 id, u32 en);
void dsim_reg_set_partial_update(u32 id, struct decon_lcd *lcd_info);
void dsim_reg_set_bta_type(u32 id, u32 bta_type);
void dsim_reg_set_num_of_transfer(u32 id, u32 num_of_transfer);
void dsim_reg_function_reset(u32 id);
void dsim_reg_set_esc_clk_on_lane(u32 id, u32 en, u32 lane);
void dsim_reg_enable_word_clock(u32 id, u32 en);
void dsim_reg_set_esc_clk_prescaler(u32 id, u32 en, u32 p);
#define DSIM_IOC_ENTER_ULPS _IOW('D', 0, u32)
#define DSIM_IOC_GET_LCD_INFO _IOW('D', 5, struct decon_lcd *)
#define DSIM_IOC_DUMP _IOW('D', 8, u32)
#define DSIM_IOC_GET_WCLK _IOW('D', 9, u32)
#endif /* __SAMSUNG_DSIM_H__ */