概述
I.MX适配不同屏:
https://boundarydev2.wpengine.com/i-mx6-kernel-3-10-53/
https://boundarydevices.com/configuring-i-mx6-machines-different-screens-nitrogen6x-sabre-lite/
平台 Android 5.0,使用 MIPI LCD,偶尔 LCD 会出现闪烁情况。LCD使用 HX8379 480RGB x 864 dot, 16.7M color。MIPI LCD 有两个模式一个是 LP 模式( 低功耗模式), 另一个是HS模式( 高速模式),当前配置HS模式为 800M,会超过 MIPI 指标, 因此 LCD 会闪烁。
kernel_imx/drivers/video/mxc$vi mxcfb_hx8369_wvga.c
#define HX8369_MAX_DPHY_CLK (800)
将其修改为 500M:
#define HX8369_MAX_DPHY_CLK (500)
修改后LCD闪烁不会出现,会带来另外的问题,有些设备会出现LCD初始化失败的情况。设置 HS 时钟为 800M, LP 时钟为 7.1M。
MIPI LCD 厂家需要配置如下:LP:6-8M;HS:小于 500M。需要设置 MIPI DSI 在低功耗模式下,如何设置 MIPI DSI低功耗时钟模式?
/kernel_imx/drivers/video/mxc$ vi mipi_dsi.c
/* enable LP mode when TX DCS cmd and enable DSI command mode */
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG,
MIPI_DSI_CMD_MODE_CFG_EN_LOWPOWER);
LCD屏有时候有轻微的抖动。测量数据线,屏幕抖动时每一帧的Vporch都会多一个,多在VS里 。代码里设置的是2+5+8=15,正常的时候,示波器上测出来的就是15个,当抖动的时候,示波器上测出来的多一个为16个。可能是板极硬件问题。禁止DVFS ,增大CAP电压。正常的波形如下:
异常的波形如下:
IPU 时钟设置如下:
pll5_video 1 1 1101184008
pll5_post_div 1 1 275296002
pll5_video_div 1 1 68824000
ipu1_di1_pre_sel 1 1 68824000
ipu1_di1_pre 1 1 34412000
ipu1_di1_sel 1 1 34412000
ipu1_di1 1 1 34412000
ipu1_pclk1_sel 1 1 34412000
ipu1_pclk1_div 1 1 34412000
ipu1_pclk_1 1 1 34412000
dump 时钟树:$ cat /sys/kernel/debug/clock/clk_summary
I.MX6DL, MIPI 像素时钟来自于 270MHz IPU 时钟,因此,建议设置像素时钟为30MHz。IPU不能准确的分频到精确的频率,只能尽可能的接近,因此会导致一些小抖动。
更改mipi接口接收时的LP频率。
#define DSI_CLKMGR_CFG_CLK_DIV (0x107) to (0x104)
更新mipi接口的HS频率。
#define HX8369_MAX_DPHY_CLK (800) to (500)
更新pixclock像素时钟37880为29059(同时帧频率也从45Hz提升到 60Hz) 。
static struct fb_videomode truly_lcd_modedb[] = {
{
"TRULY-WVGA", 60, 480, 854, 29059,
60, 60,
2, 5,
60, 8,
FB_SYNC_OE_LOW_ACT,
FB_VMODE_NONINTERLACED,
0,
},
};
在做如上更新后闪屏现象和无法初始化,以及读取芯片IC ID的问题已经解决,但还是有LCD 偶尔出现画面抖动的现象。
电源供电:VCC=2.8V IOVCC=1.8V 硬件复位;
接口:MIPI DSI Video 2Lanes, 420Mbps/Lane, Column inversion, Frame Freq= 60Hz。
#define VBPD 2
#define VFPD 5
#define VSPW 8
#define HBPD 60
#define HFPD 60
#define HSPW 60
需要仔细查看 LCD手册,HSYNC 和VSYNC是低有效,DE是高有效:
{ "TRULY-WVGA", 60, 480, 854, 29059, 60, 60, 2, 5, 60, 8, 0, FB_VMODE_NONINTERLACED, 0, },
或者
{ "TRULY-WVGA", 60, 480, 854, 29059, 60, 60, 2, 5, 60, 8, FB_SYNC_CLK_LAT_FALL, FB_VMODE_NONINTERLACED, 0, },
TX_ESC时钟和MIPI的LP时钟是一样的,因此,DSI_CLKMGR_CFG_CLK_DIV 用于控制LP时钟频率。
TX_ESC 时钟= PHY byte 时钟/ DSI_CLKMGR_CFG_CLK_DIV,在文件mipi_dsi.c中, DSI_CLKMGR_CFG_CLK_DIV用于调整 TX_ESC时钟,可以调整这个数值来测量 LP的时钟。默认500Mbps PHY的时钟数值是0x107,通过修改 DSI_CLKMGR_CFG_CLK_DIV 数值(0x106或者0x105)解决 LCD抖动问题。
/*
* Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* 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, write to the Free Software Foundation, Inc.,* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mipi_dsi.h"
#define DISPDRV_MIPI "mipi_dsi"
#define ROUND_UP(x) ((x)+1)
#define NS2PS_RATIO (1000)
#define NUMBER_OF_CHUNKS (0x8)
#define NULL_PKT_SIZE (0x8)
#define PHY_BTA_MAXTIME (0xd00)
#define PHY_LP2HS_MAXTIME (0x40)
#define PHY_HS2LP_MAXTIME (0x40)
#define PHY_STOP_WAIT_TIME (0x20)
//#define DSI_CLKMGR_CFG_CLK_DIV (0x107)
#define DSI_CLKMGR_CFG_CLK_DIV (0x104)
#define DSI_GEN_PLD_DATA_BUF_ENTRY (0x10)
#define MIPI_MUX_CTRL(v) (((v) & 0x3) << 4)
#define MIPI_LCD_SLEEP_MODE_DELAY (120)
#define MIPI_DSI_REG_RW_TIMEOUT (20)
#define MIPI_DSI_PHY_TIMEOUT (10)
//unsigned char gReportPowerKey = 0;
static int bl_pwn_gpio;
static struct mipi_dsi_match_lcd mipi_dsi_lcd_db[] = {
#ifdef CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL
{
"TRULY-WVGA",
{mipid_hx8369_get_lcd_videomode, mipid_hx8369_lcd_setup}
},
#endif
{
"", {NULL, NULL}
}
};
struct _mipi_dsi_phy_pll_clk {
u32 max_phy_clk;
u32 config;
};
/* configure data for DPHY PLL 27M reference clk out */
static const struct _mipi_dsi_phy_pll_clk mipi_dsi_phy_pll_clk_table[] = {
{1000, 0x74}, /* 950-1000MHz */
{950, 0x54}, /* 900-950Mhz */
{900, 0x34}, /* 850-900Mhz */
{850, 0x14}, /* 800-850MHz */
{800, 0x32}, /* 750-800MHz */
{750, 0x12}, /* 700-750Mhz */
{700, 0x30}, /* 650-700Mhz */
{650, 0x10}, /* 600-650MHz */
{600, 0x2e}, /* 550-600MHz */
{550, 0x0e}, /* 500-550Mhz */
{500, 0x2c}, /* 450-500Mhz */
{450, 0x0c}, /* 400-450MHz */
{400, 0x4a}, /* 360-400MHz */
{360, 0x2a}, /* 330-360Mhz */
{330, 0x48}, /* 300-330Mhz */
{300, 0x28}, /* 270-300MHz */
{270, 0x08}, /* 250-270MHz */
{250, 0x46}, /* 240-250Mhz */
{240, 0x26}, /* 210-240Mhz */
{210, 0x06}, /* 200-210MHz */
{200, 0x44}, /* 180-200MHz */
{180, 0x24}, /* 160-180MHz */
{160, 0x04}, /* 150-160MHz */
};
static int valid_mode(int pixel_fmt)
{
return ((pixel_fmt == IPU_PIX_FMT_RGB24) ||
(pixel_fmt == IPU_PIX_FMT_BGR24) ||
(pixel_fmt == IPU_PIX_FMT_RGB666) ||
(pixel_fmt == IPU_PIX_FMT_RGB565) ||
(pixel_fmt == IPU_PIX_FMT_BGR666) ||
(pixel_fmt == IPU_PIX_FMT_RGB332));
}
static inline void mipi_dsi_read_register(struct mipi_dsi_info *mipi_dsi,
u32 reg, u32 *val)
{
*val = ioread32(mipi_dsi->mmio_base + reg);
dev_dbg(&mipi_dsi->pdev->dev, "read_reg:0x%02x, val:0x%08x.n",
reg, *val);
}
static inline void mipi_dsi_write_register(struct mipi_dsi_info *mipi_dsi,
u32 reg, u32 val)
{
iowrite32(val, mipi_dsi->mmio_base + reg);
dev_dbg(&mipi_dsi->pdev->dev, "ttwrite_reg:0x%02x, val:0x%08x.n",
reg, val);
}
int mipi_dsi_pkt_write(struct mipi_dsi_info *mipi_dsi,
u8 data_type, const u32 *buf, int len)
{
u32 val;
u32 status = 0;
int write_len = len;
uint32_t timeout = 0;
if (len) {
/* generic long write command */
while (len / DSI_GEN_PLD_DATA_BUF_SIZE) {
mipi_dsi_write_register(mipi_dsi,
MIPI_DSI_GEN_PLD_DATA, *buf);
buf++;
len -= DSI_GEN_PLD_DATA_BUF_SIZE;
mipi_dsi_read_register(mipi_dsi,
MIPI_DSI_CMD_PKT_STATUS, &status);
while ((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) ==
DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
return -EIO;
mipi_dsi_read_register(mipi_dsi,
MIPI_DSI_CMD_PKT_STATUS, &status);
}
}
/* write the remainder bytes */
if (len > 0) {
while ((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) ==
DSI_CMD_PKT_STATUS_GEN_PLD_W_FULL) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
return -EIO;
mipi_dsi_read_register(mipi_dsi,
MIPI_DSI_CMD_PKT_STATUS, &status);
}
mipi_dsi_write_register(mipi_dsi,
MIPI_DSI_GEN_PLD_DATA, *buf);
}
val = data_type | ((write_len & DSI_GEN_HDR_DATA_MASK)
<< DSI_GEN_HDR_DATA_SHIFT);
} else {
/* generic short write command */
val = data_type | ((*buf & DSI_GEN_HDR_DATA_MASK)
<< DSI_GEN_HDR_DATA_SHIFT);
}
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &status);
while ((status & DSI_CMD_PKT_STATUS_GEN_CMD_FULL) ==
DSI_CMD_PKT_STATUS_GEN_CMD_FULL) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
return -EIO;
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
&status);
}
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_GEN_HDR, val);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &status);
while (!((status & DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY) ==
DSI_CMD_PKT_STATUS_GEN_CMD_EMPTY) ||
!((status & DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY) ==
DSI_CMD_PKT_STATUS_GEN_PLD_W_EMPTY)) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
return -EIO;
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
&status);
}
return 0;
}
int mipi_dsi_pkt_read(struct mipi_dsi_info *mipi_dsi,
u8 data_type, u32 *buf, int len)
{
u32 val;
int read_len = 0;
uint32_t timeout = 0;
if (!len) {
mipi_dbg("%s, len = 0 invalid error!n", __func__);
return -EINVAL;
}
val = data_type | ((*buf & DSI_GEN_HDR_DATA_MASK)
<< DSI_GEN_HDR_DATA_SHIFT);
memset(buf, 0, len);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_GEN_HDR, val);
/* wait for cmd to sent out */
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &val);
while ((val & DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) !=
DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
return -EIO;
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
&val);
}
/* wait for entire response stroed in FIFO */
while ((val & DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) ==
DSI_CMD_PKT_STATUS_GEN_RD_CMD_BUSY) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_REG_RW_TIMEOUT)
return -EIO;
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
&val);
}
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS, &val);
while (!(val & DSI_CMD_PKT_STATUS_GEN_PLD_R_EMPTY)) {
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_GEN_PLD_DATA, buf);
read_len += DSI_GEN_PLD_DATA_BUF_SIZE;
buf++;
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_PKT_STATUS,
&val);
if (read_len == (DSI_GEN_PLD_DATA_BUF_ENTRY *
DSI_GEN_PLD_DATA_BUF_SIZE))
break;
}
if ((len <= read_len) &&
((len + DSI_GEN_PLD_DATA_BUF_SIZE) >= read_len))
return 0;
else {
dev_err(&mipi_dsi->pdev->dev,
"actually read_len:%d != len:%d.n", read_len, len);
return -ERANGE;
}
}
int mipi_dsi_dcs_cmd(struct mipi_dsi_info *mipi_dsi,
u8 cmd, const u32 *param, int num)
{
int err = 0;
u32 buf[DSI_CMD_BUF_MAXSIZE];
switch (cmd) {
case MIPI_DCS_EXIT_SLEEP_MODE:
case MIPI_DCS_ENTER_SLEEP_MODE:
case MIPI_DCS_SET_DISPLAY_ON:
case MIPI_DCS_SET_DISPLAY_OFF:
buf[0] = cmd;
err = mipi_dsi_pkt_write(mipi_dsi,
MIPI_DSI_DCS_SHORT_WRITE, buf, 0);
break;
default:
dev_err(&mipi_dsi->pdev->dev,
"MIPI DSI DCS Command:0x%x Not supported!n", cmd);
break;
}
return err;
}
static void mipi_dsi_dphy_init(struct mipi_dsi_info *mipi_dsi,
u32 cmd, u32 data)
{
u32 val;
u32 timeout = 0;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
DSI_PHY_IF_CTRL_RESET);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP, DSI_PWRUP_POWERUP);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL1,
(0x10000 | cmd));
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 2);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL1, (0 | data));
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 2);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TST_CTRL0, 0);
val = DSI_PHY_RSTZ_EN_CLK | DSI_PHY_RSTZ_DISABLE_RST |
DSI_PHY_RSTZ_DISABLE_SHUTDOWN;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ, val);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
while ((val & DSI_PHY_STATUS_LOCK) != DSI_PHY_STATUS_LOCK) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_PHY_TIMEOUT) {
dev_err(&mipi_dsi->pdev->dev,
"Error: phy lock timeout!n");
break;
}
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
}
timeout = 0;
while ((val & DSI_PHY_STATUS_STOPSTATE_CLK_LANE) !=
DSI_PHY_STATUS_STOPSTATE_CLK_LANE) {
msleep(1);
timeout++;
if (timeout == MIPI_DSI_PHY_TIMEOUT) {
dev_err(&mipi_dsi->pdev->dev,
"Error: phy lock lane timeout!n");
break;
}
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_PHY_STATUS, &val);
}
}
static void mipi_dsi_enable_controller(struct mipi_dsi_info *mipi_dsi,
bool init)
{
u32 val;
u32 lane_byte_clk_period;
struct fb_videomode *mode = mipi_dsi->mode;
struct mipi_lcd_config *lcd_config = mipi_dsi->lcd_config;
if (init) {
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
DSI_PWRUP_RESET);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ,
DSI_PHY_RSTZ_RST);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CLKMGR_CFG,
DSI_CLKMGR_CFG_CLK_DIV);
if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
val = DSI_DPI_CFG_VSYNC_ACT_LOW;
if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
val |= DSI_DPI_CFG_HSYNC_ACT_LOW;
if ((mode->sync & FB_SYNC_OE_LOW_ACT))
val |= DSI_DPI_CFG_DATAEN_ACT_LOW;
if (MIPI_RGB666_LOOSELY == lcd_config->dpi_fmt)
val |= DSI_DPI_CFG_EN18LOOSELY;
val |= (lcd_config->dpi_fmt & DSI_DPI_CFG_COLORCODE_MASK)
<< DSI_DPI_CFG_COLORCODE_SHIFT;
val |= (lcd_config->virtual_ch & DSI_DPI_CFG_VID_MASK)
<< DSI_DPI_CFG_VID_SHIFT;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_DPI_CFG, val);
val = DSI_PCKHDL_CFG_EN_BTA |
DSI_PCKHDL_CFG_EN_ECC_RX |
DSI_PCKHDL_CFG_EN_CRC_RX;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PCKHDL_CFG, val);
val = (mode->xres & DSI_VID_PKT_CFG_VID_PKT_SZ_MASK)
<< DSI_VID_PKT_CFG_VID_PKT_SZ_SHIFT;
val |= (NUMBER_OF_CHUNKS & DSI_VID_PKT_CFG_NUM_CHUNKS_MASK)
<< DSI_VID_PKT_CFG_NUM_CHUNKS_SHIFT;
val |= (NULL_PKT_SIZE & DSI_VID_PKT_CFG_NULL_PKT_SZ_MASK)
<< DSI_VID_PKT_CFG_NULL_PKT_SZ_SHIFT;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_PKT_CFG, val);
/* enable LP mode when TX DCS cmd and enable DSI command mode */
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG,
MIPI_DSI_CMD_MODE_CFG_EN_LOWPOWER);
/* mipi lane byte clk period in ns unit */
lane_byte_clk_period = NS2PS_RATIO /
(lcd_config->max_phy_clk / BITS_PER_BYTE);
val = ROUND_UP(mode->hsync_len * mode->pixclock /
NS2PS_RATIO / lane_byte_clk_period)
<< DSI_TME_LINE_CFG_HSA_TIME_SHIFT;
val |= ROUND_UP(mode->left_margin * mode->pixclock /
NS2PS_RATIO / lane_byte_clk_period)
<< DSI_TME_LINE_CFG_HBP_TIME_SHIFT;
val |= ROUND_UP((mode->left_margin + mode->right_margin +
mode->hsync_len + mode->xres) * mode->pixclock
/ NS2PS_RATIO / lane_byte_clk_period)
<< DSI_TME_LINE_CFG_HLINE_TIME_SHIFT;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_TMR_LINE_CFG, val);
val = ((mode->vsync_len & DSI_VTIMING_CFG_VSA_LINES_MASK)
<< DSI_VTIMING_CFG_VSA_LINES_SHIFT);
val |= ((mode->upper_margin & DSI_VTIMING_CFG_VBP_LINES_MASK)
<< DSI_VTIMING_CFG_VBP_LINES_SHIFT);
val |= ((mode->lower_margin & DSI_VTIMING_CFG_VFP_LINES_MASK)
<< DSI_VTIMING_CFG_VFP_LINES_SHIFT);
val |= ((mode->yres & DSI_VTIMING_CFG_V_ACT_LINES_MASK)
<< DSI_VTIMING_CFG_V_ACT_LINES_SHIFT);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VTIMING_CFG, val);
val = ((PHY_BTA_MAXTIME & DSI_PHY_TMR_CFG_BTA_TIME_MASK)
<< DSI_PHY_TMR_CFG_BTA_TIME_SHIFT);
val |= ((PHY_LP2HS_MAXTIME & DSI_PHY_TMR_CFG_LP2HS_TIME_MASK)
<< DSI_PHY_TMR_CFG_LP2HS_TIME_SHIFT);
val |= ((PHY_HS2LP_MAXTIME & DSI_PHY_TMR_CFG_HS2LP_TIME_MASK)
<< DSI_PHY_TMR_CFG_HS2LP_TIME_SHIFT);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_TMR_CFG, val);
val = (((lcd_config->data_lane_num - 1) &
DSI_PHY_IF_CFG_N_LANES_MASK)
<< DSI_PHY_IF_CFG_N_LANES_SHIFT);
val |= ((PHY_STOP_WAIT_TIME & DSI_PHY_IF_CFG_WAIT_TIME_MASK)
<< DSI_PHY_IF_CFG_WAIT_TIME_SHIFT);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CFG, val);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST0, &val);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST1, &val);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_ERROR_MSK0, 0);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_ERROR_MSK1, 0);
mipi_dsi_dphy_init(mipi_dsi, DSI_PHY_CLK_INIT_COMMAND,
mipi_dsi->dphy_pll_config);
} else {
mipi_dsi_dphy_init(mipi_dsi, DSI_PHY_CLK_INIT_COMMAND,
mipi_dsi->dphy_pll_config);
}
}
static void mipi_dsi_disable_controller(struct mipi_dsi_info *mipi_dsi)
{
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
DSI_PHY_IF_CTRL_RESET);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP, DSI_PWRUP_RESET);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_RSTZ, DSI_PHY_RSTZ_RST);
}
static irqreturn_t mipi_dsi_irq_handler(int irq, void *data)
{
u32 mask0;
u32 mask1;
u32 status0;
u32 status1;
struct mipi_dsi_info *mipi_dsi;
mipi_dsi = (struct mipi_dsi_info *)data;
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST0, &status0);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_ST1, &status1);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_MSK0, &mask0);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_ERROR_MSK1, &mask1);
if ((status0 & (~mask0)) || (status1 & (~mask1))) {
dev_err(&mipi_dsi->pdev->dev,
"mipi_dsi IRQ status0:0x%x, status1:0x%x!n",
status0, status1);
}
return IRQ_HANDLED;
}
static inline void mipi_dsi_set_mode(struct mipi_dsi_info *mipi_dsi,
bool cmd_mode)
{
u32 val;
if (cmd_mode) {
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
DSI_PWRUP_RESET);
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, &val);
val |= MIPI_DSI_CMD_MODE_CFG_EN_CMD_MODE;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, val);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, 0);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
DSI_PWRUP_POWERUP);
} else {
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
DSI_PWRUP_RESET);
/* Disable Command mode when tranfering video data */
mipi_dsi_read_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, &val);
val &= ~MIPI_DSI_CMD_MODE_CFG_EN_CMD_MODE;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_CMD_MODE_CFG, val);
val = DSI_VID_MODE_CFG_EN | DSI_VID_MODE_CFG_EN_BURSTMODE |
DSI_VID_MODE_CFG_EN_LP_MODE;
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_VID_MODE_CFG, val);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PWR_UP,
DSI_PWRUP_POWERUP);
mipi_dsi_write_register(mipi_dsi, MIPI_DSI_PHY_IF_CTRL,
DSI_PHY_IF_CTRL_TX_REQ_CLK_HS);
}
}
static int mipi_dsi_power_on(struct mxc_dispdrv_handle *disp)
{
int err;
struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
if (!mipi_dsi->dsi_power_on) {
clk_prepare_enable(mipi_dsi->dphy_clk);
clk_prepare_enable(mipi_dsi->cfg_clk);
mipi_dsi_enable_controller(mipi_dsi, false);
mipi_dsi_set_mode(mipi_dsi, false);
/* host send pclk/hsync/vsync for two frames before sleep-out */
msleep((1000/mipi_dsi->mode->refresh + 1) << 1);
mipi_dsi_set_mode(mipi_dsi, true);
err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_EXIT_SLEEP_MODE,
NULL, 0);
if (err) {
printk("n==============sleep out error=============n");
dev_err(&mipi_dsi->pdev->dev,
"MIPI DSI DCS Command sleep-in error!n");
}
msleep(MIPI_LCD_SLEEP_MODE_DELAY);
mipi_dsi_set_mode(mipi_dsi, false);
mipi_dsi->dsi_power_on = 1;
}
//power on backlight
gpio_set_value(bl_pwn_gpio,1);
return 0;
}
void mipi_dsi_power_off(struct mxc_dispdrv_handle *disp)
{
int err;
struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
if (mipi_dsi->dsi_power_on) {
mipi_dsi_set_mode(mipi_dsi, true);
err = mipi_dsi_dcs_cmd(mipi_dsi, MIPI_DCS_ENTER_SLEEP_MODE,
NULL, 0);
if (err) {
dev_err(&mipi_dsi->pdev->dev,
"MIPI DSI DCS Command display on error!n");
}
/* To allow time for the supply voltages
* and clock circuits to stabilize.
*/
msleep(5);
/* video stream timing on */
mipi_dsi_set_mode(mipi_dsi, false);
msleep(MIPI_LCD_SLEEP_MODE_DELAY);
mipi_dsi_set_mode(mipi_dsi, true);
mipi_dsi_disable_controller(mipi_dsi);
mipi_dsi->dsi_power_on = 0;
clk_disable_unprepare(mipi_dsi->dphy_clk);
clk_disable_unprepare(mipi_dsi->cfg_clk);
}
//power off backlight
gpio_set_value(bl_pwn_gpio,0);
//gReportPowerKey = 1;
}
static int mipi_dsi_lcd_init(struct mipi_dsi_info *mipi_dsi,
struct mxc_dispdrv_setting *setting)
{
int err;
int size;
int i;
struct fb_videomode *mipi_lcd_modedb;
struct fb_videomode mode;
struct device *dev = &mipi_dsi->pdev->dev;
for (i = 0; i < ARRAY_SIZE(mipi_dsi_lcd_db); i++) {
if (!strcmp(mipi_dsi->lcd_panel,
mipi_dsi_lcd_db[i].lcd_panel)) {
mipi_dsi->lcd_callback =
&mipi_dsi_lcd_db[i].lcd_callback;
break;
}
}
if (i == ARRAY_SIZE(mipi_dsi_lcd_db)) {
dev_err(dev, "failed to find supported lcd panel.n");
return -EINVAL;
}
/* get the videomode in the order: cmdline->platform data->driver */
mipi_dsi->lcd_callback->get_mipi_lcd_videomode(&mipi_lcd_modedb, &size,
&mipi_dsi->lcd_config);
err = fb_find_mode(&setting->fbi->var, setting->fbi,
setting->dft_mode_str,
mipi_lcd_modedb, size, NULL,
setting->default_bpp);
if (err != 1)
fb_videomode_to_var(&setting->fbi->var, mipi_lcd_modedb);
INIT_LIST_HEAD(&setting->fbi->modelist);
for (i = 0; i < size; i++) {
fb_var_to_videomode(&mode, &setting->fbi->var);
if (fb_mode_is_equal(&mode, mipi_lcd_modedb + i)) {
err = fb_add_videomode(mipi_lcd_modedb + i,
&setting->fbi->modelist);
/* Note: only support fb mode from driver */
mipi_dsi->mode = mipi_lcd_modedb + i;
break;
}
}
if ((err < 0) || (size == i)) {
dev_err(dev, "failed to add videomode.n");
return err;
}
for (i = 0; i < ARRAY_SIZE(mipi_dsi_phy_pll_clk_table); i++) {
if (mipi_dsi_phy_pll_clk_table[i].max_phy_clk <
mipi_dsi->lcd_config->max_phy_clk)
break;
}
if ((i == ARRAY_SIZE(mipi_dsi_phy_pll_clk_table)) ||
(mipi_dsi->lcd_config->max_phy_clk >
mipi_dsi_phy_pll_clk_table[0].max_phy_clk)) {
dev_err(dev, "failed to find data in"
"mipi_dsi_phy_pll_clk_table.n");
return -EINVAL;
}
mipi_dsi->dphy_pll_config = mipi_dsi_phy_pll_clk_table[--i].config;
dev_dbg(dev, "dphy_pll_config:0x%x.n", mipi_dsi->dphy_pll_config);
return 0;
}
static int mipi_dsi_enable(struct mxc_dispdrv_handle *disp,
struct fb_info *fbi)
{
int err;
struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
if (!mipi_dsi->lcd_inited) {
err = clk_prepare_enable(mipi_dsi->dphy_clk);
err |= clk_prepare_enable(mipi_dsi->cfg_clk);
if (err)
dev_err(&mipi_dsi->pdev->dev,
"clk enable error:%d!n", err);
mipi_dsi_enable_controller(mipi_dsi, true);
err = mipi_dsi->lcd_callback->mipi_lcd_setup(
mipi_dsi);
if (err < 0) {
dev_err(&mipi_dsi->pdev->dev,
"failed to init mipi lcd.");
clk_disable_unprepare(mipi_dsi->dphy_clk);
clk_disable_unprepare(mipi_dsi->cfg_clk);
return err;
}
mipi_dsi_set_mode(mipi_dsi, false);
mipi_dsi->dsi_power_on = 1;
mipi_dsi->lcd_inited = 1;
}
mipi_dsi_power_on(mipi_dsi->disp_mipi);
return 0;
}
static void mipi_dsi_disable(struct mxc_dispdrv_handle *disp,
struct fb_info *fbi)
{
struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
mipi_dsi_power_off(mipi_dsi->disp_mipi);
}
static int mipi_dsi_disp_init(struct mxc_dispdrv_handle *disp,
struct mxc_dispdrv_setting *setting)
{
struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
struct device *dev = &mipi_dsi->pdev->dev;
int ret = 0;
if (!valid_mode(setting->if_fmt)) {
dev_warn(dev, "Input pixel format not valid"
"use default RGB24n");
setting->if_fmt = IPU_PIX_FMT_RGB24;
}
ret = ipu_di_to_crtc(dev, mipi_dsi->dev_id,
mipi_dsi->disp_id, &setting->crtc);
if (ret < 0)
{
return ret;
}
ret = mipi_dsi_lcd_init(mipi_dsi, setting);
if (ret) {
dev_err(dev, "failed to init mipi dsi lcdn");
return ret;
}
dev_dbg(dev, "MIPI DSI dispdrv inited!n");
return ret;
}
static void mipi_dsi_disp_deinit(struct mxc_dispdrv_handle *disp)
{
struct mipi_dsi_info *mipi_dsi;
mipi_dsi = mxc_dispdrv_getdata(disp);
mipi_dsi_power_off(mipi_dsi->disp_mipi);
if (mipi_dsi->bl)
backlight_device_unregister(mipi_dsi->bl);
}
static int mipi_dsi_setup(struct mxc_dispdrv_handle *disp,
struct fb_info *fbi)
{
struct mipi_dsi_info *mipi_dsi = mxc_dispdrv_getdata(disp);
int xres_virtual = fbi->var.xres_virtual;
int yres_virtual = fbi->var.yres_virtual;
int xoffset = fbi->var.xoffset;
int yoffset = fbi->var.yoffset;
int pixclock = fbi->var.pixclock;
if (!mipi_dsi->mode)
return 0;
/* set the mode back to var in case userspace changes it */
fb_videomode_to_var(&fbi->var, mipi_dsi->mode);
/* restore some var entries cached */
fbi->var.xres_virtual = xres_virtual;
fbi->var.yres_virtual = yres_virtual;
fbi->var.xoffset = xoffset;
fbi->var.yoffset = yoffset;
fbi->var.pixclock = pixclock;
return 0;
}
static struct mxc_dispdrv_driver mipi_dsi_drv = {
.name = DISPDRV_MIPI,
.init = mipi_dsi_disp_init,
.deinit = mipi_dsi_disp_deinit,
.enable = mipi_dsi_enable,
.disable = mipi_dsi_disable,
.setup = mipi_dsi_setup,
};
static int imx6q_mipi_dsi_get_mux(int dev_id, int disp_id)
{
if (dev_id > 1 || disp_id > 1)
return -EINVAL;
return (dev_id << 5) | (disp_id << 4);
}
static struct mipi_dsi_bus_mux imx6q_mipi_dsi_mux[] = {
{
.reg = IOMUXC_GPR3,
.mask = IMX6Q_GPR3_MIPI_MUX_CTL_MASK,
.get_mux = imx6q_mipi_dsi_get_mux,
},
};
static int imx6dl_mipi_dsi_get_mux(int dev_id, int disp_id)
{
if (dev_id > 1 || disp_id > 1)
return -EINVAL;
/* MIPI DSI source is LCDIF */
if (dev_id)
disp_id = 0;
return (dev_id << 5) | (disp_id << 4);
}
static struct mipi_dsi_bus_mux imx6dl_mipi_dsi_mux[] = {
{
.reg = IOMUXC_GPR3,
.mask = IMX6Q_GPR3_MIPI_MUX_CTL_MASK,
.get_mux = imx6dl_mipi_dsi_get_mux,
},
};
static const struct of_device_id imx_mipi_dsi_dt_ids[] = {
{ .compatible = "fsl,imx6q-mipi-dsi", .data = imx6q_mipi_dsi_mux, },
{ .compatible = "fsl,imx6dl-mipi-dsi", .data = imx6dl_mipi_dsi_mux, },
{ }
};
MODULE_DEVICE_TABLE(of, imx_mipi_dsi_dt_ids);
/**
* This function is called by the driver framework to initialize the MIPI DSI
* device.
*
* @param pdev The device structure for the MIPI DSI passed in by the
* driver framework.
*
* @return Returns 0 on success or negative error code on error
*/
static int mipi_dsi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(of_match_ptr(imx_mipi_dsi_dt_ids),
&pdev->dev);
struct mipi_dsi_info *mipi_dsi;
struct resource *res;
u32 dev_id, disp_id;
const char *lcd_panel;
unsigned int mux;
int ret = 0;
mipi_dsi = devm_kzalloc(&pdev->dev, sizeof(*mipi_dsi), GFP_KERNEL);
if (!mipi_dsi)
return -ENOMEM;
ret = of_property_read_string(np, "lcd_panel", &lcd_panel);
if (ret) {
dev_err(&pdev->dev, "failed to read of property lcd_paneln");
return ret;
}
ret = of_property_read_u32(np, "dev_id", &dev_id);
if (ret) {
dev_err(&pdev->dev, "failed to read of property dev_idn");
return ret;
}
ret = of_property_read_u32(np, "disp_id", &disp_id);
if (ret) {
dev_err(&pdev->dev, "failed to read of property disp_idn");
return ret;
}
mipi_dsi->dev_id = dev_id;
mipi_dsi->disp_id = disp_id;
/*get and set the backlight power on gpio*/
/* bl_pwn_gpio = of_get_named_gpio(np, "bl-pwn-gpios", 0);
if (!gpio_is_valid(bl_pwn_gpio))
{
dev_err(&pdev->dev, "backlight power on gpio is invalidn");
return -ENODEV;
}
printk("bl_pwn_gpio = %dn",bl_pwn_gpio);
ret = gpio_request(bl_pwn_gpio, "bl_pwn");
if (ret) {
dev_err(&pdev->dev, "failed to request backlight pwn gpion");
return ret;
}
gpio_direction_output(bl_pwn_gpio, 1);
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get platform resource 0n");
return -ENODEV;
}
if (!devm_request_mem_region(&pdev->dev, res->start,
resource_size(res), pdev->name))
return -EBUSY;
mipi_dsi->mmio_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!mipi_dsi->mmio_base)
return -EBUSY;
mipi_dsi->irq = platform_get_irq(pdev, 0);
if (mipi_dsi->irq < 0) {
dev_err(&pdev->dev, "failed get device irqn");
return -ENODEV;
}
ret = devm_request_irq(&pdev->dev, mipi_dsi->irq,
mipi_dsi_irq_handler,
0, "mipi_dsi", mipi_dsi);
if (ret) {
dev_err(&pdev->dev, "failed to request irqn");
return ret;
}
mipi_dsi->dphy_clk = devm_clk_get(&pdev->dev, "mipi_pllref_clk");
if (IS_ERR(mipi_dsi->dphy_clk)) {
dev_err(&pdev->dev, "failed to get dphy pll_ref_clkn");
return PTR_ERR(mipi_dsi->dphy_clk);
}
mipi_dsi->cfg_clk = devm_clk_get(&pdev->dev, "mipi_cfg_clk");
if (IS_ERR(mipi_dsi->cfg_clk)) {
dev_err(&pdev->dev, "failed to get cfg_clkn");
return PTR_ERR(mipi_dsi->cfg_clk);
}
mipi_dsi->disp_power_on = devm_regulator_get(&pdev->dev,
"disp-power-on");
if (!IS_ERR(mipi_dsi->disp_power_on)) {
ret = regulator_enable(mipi_dsi->disp_power_on);
if (ret) {
dev_err(&pdev->dev, "failed to enable display "
"power regulator, err=%dn", ret);
return ret;
}
} else {
mipi_dsi->disp_power_on = NULL;
}
ret = device_reset(&pdev->dev);
printk("=============MIPI LCD Reset==============");
if (ret) {
dev_err(&pdev->dev, "failed to reset: %dn", ret);
goto dev_reset_fail;
}
if (of_id)
mipi_dsi->bus_mux = of_id->data;
mipi_dsi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
if (IS_ERR(mipi_dsi->regmap)) {
dev_err(&pdev->dev, "failed to get parent regmapn");
ret = PTR_ERR(mipi_dsi->regmap);
goto get_parent_regmap_fail;
}
mux = mipi_dsi->bus_mux->get_mux(dev_id, disp_id);
if (mux >= 0)
regmap_update_bits(mipi_dsi->regmap, mipi_dsi->bus_mux->reg,
mipi_dsi->bus_mux->mask, mux);
else
dev_warn(&pdev->dev, "invalid dev_id or disp_id muxingn");
mipi_dsi->lcd_panel = kstrdup(lcd_panel, GFP_KERNEL);
if (!mipi_dsi->lcd_panel) {
dev_err(&pdev->dev, "failed to allocate lcd panel namen");
ret = -ENOMEM;
goto kstrdup_fail;
}
mipi_dsi->pdev = pdev;
mipi_dsi->disp_mipi = mxc_dispdrv_register(&mipi_dsi_drv);
if (IS_ERR(mipi_dsi->disp_mipi)) {
dev_err(&pdev->dev, "mxc_dispdrv_register errorn");
ret = PTR_ERR(mipi_dsi->disp_mipi);
goto dispdrv_reg_fail;
}
mxc_dispdrv_setdata(mipi_dsi->disp_mipi, mipi_dsi);
dev_set_drvdata(&pdev->dev, mipi_dsi);
mxc_dispdrv_setdev(mipi_dsi->disp_mipi, &pdev->dev);
dev_info(&pdev->dev, "i.MX MIPI DSI driver probedn");
return ret;
dispdrv_reg_fail:
kfree(mipi_dsi->lcd_panel);
kstrdup_fail:
get_parent_regmap_fail:
dev_reset_fail:
if (mipi_dsi->disp_power_on)
regulator_disable(mipi_dsi->disp_power_on);
return ret;
}
static void mipi_dsi_shutdown(struct platform_device *pdev)
{
struct mipi_dsi_info *mipi_dsi = dev_get_drvdata(&pdev->dev);
mipi_dsi_power_off(mipi_dsi->disp_mipi);
}
static int mipi_dsi_remove(struct platform_device *pdev)
{
struct mipi_dsi_info *mipi_dsi = dev_get_drvdata(&pdev->dev);
mxc_dispdrv_puthandle(mipi_dsi->disp_mipi);
mxc_dispdrv_unregister(mipi_dsi->disp_mipi);
if (mipi_dsi->disp_power_on)
regulator_disable(mipi_dsi->disp_power_on);
kfree(mipi_dsi->lcd_panel);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
static struct platform_driver mipi_dsi_driver = {
.driver = {
.of_match_table = imx_mipi_dsi_dt_ids,
.name = "mxc_mipi_dsi",
},
.probe = mipi_dsi_probe,
.remove = mipi_dsi_remove,
.shutdown = mipi_dsi_shutdown,
};
static int __init mipi_dsi_init(void)
{
int err;
err = platform_driver_register(&mipi_dsi_driver);
if (err) {
pr_err("mipi_dsi_driver register failedn");
return -ENODEV;
}
pr_info("MIPI DSI driver module loadedn");
return 0;
}
static void __exit mipi_dsi_cleanup(void)
{
platform_driver_unregister(&mipi_dsi_driver);
}
module_init(mipi_dsi_init);
module_exit(mipi_dsi_cleanup);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("i.MX MIPI DSI driver");
MODULE_LICENSE("GPL");
最后
以上就是单身小蚂蚁为你收集整理的idea2020.2.2 license server_i.MX6 MIPI DSI: 低功耗LCD时钟配置出现显示闪烁和抖动的问题...的全部内容,希望文章能够帮你解决idea2020.2.2 license server_i.MX6 MIPI DSI: 低功耗LCD时钟配置出现显示闪烁和抖动的问题...所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复