2077 lines
57 KiB
C
Raw Normal View History

2025-05-10 21:49:39 +08:00
/*
* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
*
* 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.
*/
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/iopoll.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/mfd/syscon.h>
#include <linux/phy/phy.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drmP.h>
#include <video/mipi_display.h>
#include <asm/unaligned.h>
#include <uapi/linux/videodev2.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_vop.h"
#define UPDATE(v, h, l) (((v) << (l)) & GENMASK((h), (l)))
/* DWC_mipi_dsi_host registers */
#define DSI_VERSION 0x000
#define DSI_PWR_UP 0x004
#define RESET 0
#define POWER_UP BIT(0)
#define DSI_CLKMGR_CFG 0x008
#define TO_CLK_DIVISION(x) UPDATE(x, 15, 8)
#define TX_ESC_CLK_DIVISION(x) UPDATE(x, 7, 0)
#define DSI_DPI_VCID 0x00c
#define DPI_VID(x) UPDATE(x, 1, 0)
#define DSI_DPI_COLOR_CODING 0x010
#define LOOSELY18_EN BIT(8)
#define DPI_COLOR_CODING(x) UPDATE(x, 3, 0)
#define DSI_DPI_CFG_POL 0x014
#define COLORM_ACTIVE_LOW BIT(4)
#define SHUTD_ACTIVE_LOW BIT(3)
#define HSYNC_ACTIVE_LOW BIT(2)
#define VSYNC_ACTIVE_LOW BIT(1)
#define DATAEN_ACTIVE_LOW BIT(0)
#define DSI_DPI_LP_CMD_TIM 0x018
#define OUTVACT_LPCMD_TIME(x) UPDATE(x, 23, 16)
#define INVACT_LPCMD_TIME(x) UPDATE(x, 7, 0)
#define DSI_DBI_VCID 0x01c
#define DBI_VCID(x) UPDATE(x, 1, 0)
#define DSI_PCKHDL_CFG 0x02c
#define CRC_RX_EN BIT(4)
#define ECC_RX_EN BIT(3)
#define BTA_EN BIT(2)
#define EOTP_RX_EN BIT(1)
#define EOTP_TX_EN BIT(0)
#define DSI_MODE_CFG 0x034
#define CMD_VIDEO_MODE(x) UPDATE(x, 0, 0)
#define DSI_VID_MODE_CFG 0x038
#define VPG_EN BIT(16)
#define LP_CMD_EN BIT(15)
#define FRAME_BTA_ACK_EN BIT(14)
#define LP_HFP_EN BIT(13)
#define LP_HBP_EN BIT(12)
#define LP_VACT_EN BIT(11)
#define LP_VFP_EN BIT(10)
#define LP_VBP_EN BIT(9)
#define LP_VSA_EN BIT(8)
#define VID_MODE_TYPE(x) UPDATE(x, 1, 0)
#define DSI_VID_PKT_SIZE 0x03c
#define VID_PKT_SIZE(x) UPDATE(x, 13, 0)
#define DSI_VID_HSA_TIME 0x048
#define VID_HSA_TIME(x) UPDATE(x, 11, 0)
#define DSI_VID_HBP_TIME 0x04c
#define VID_HBP_TIME(x) UPDATE(x, 11, 0)
#define DSI_VID_HLINE_TIME 0x050
#define VID_HLINE_TIME(x) UPDATE(x, 14, 0)
#define DSI_VID_VSA_LINES 0x054
#define VSA_LINES(x) UPDATE(x, 9, 0)
#define DSI_VID_VBP_LINES 0x058
#define VBP_LINES(x) UPDATE(x, 9, 0)
#define DSI_VID_VFP_LINES 0x05c
#define VFP_LINES(x) UPDATE(x, 9, 0)
#define DSI_VID_VACTIVE_LINES 0x060
#define V_ACTIVE_LINES(x) UPDATE(x, 13, 0)
#define DSI_EDPI_CMD_SIZE 0x064
#define EDPI_ALLOWED_CMD_SIZE(x) UPDATE(x, 15, 0)
#define DSI_CMD_MODE_CFG 0x068
#define MAX_RD_PKT_SIZE BIT(24)
#define DCS_LW_TX BIT(19)
#define DCS_SR_0P_TX BIT(18)
#define DCS_SW_1P_TX BIT(17)
#define DCS_SW_0P_TX BIT(16)
#define GEN_LW_TX BIT(14)
#define GEN_SR_2P_TX BIT(13)
#define GEN_SR_1P_TX BIT(12)
#define GEN_SR_0P_TX BIT(11)
#define GEN_SW_2P_TX BIT(10)
#define GEN_SW_1P_TX BIT(9)
#define GEN_SW_0P_TX BIT(8)
#define ACK_RQST_EN BIT(1)
#define TEAR_FX_EN BIT(0)
#define DSI_GEN_HDR 0x06c
#define GEN_WC_MSBYTE(x) UPDATE(x, 23, 16)
#define GEN_WC_LSBYTE(x) UPDATE(x, 15, 8)
#define GEN_VC(x) UPDATE(x, 7, 6)
#define GEN_DT(x) UPDATE(x, 5, 0)
#define DSI_GEN_PLD_DATA 0x070
#define DSI_CMD_PKT_STATUS 0x074
#define GEN_RD_CMD_BUSY BIT(6)
#define GEN_PLD_R_FULL BIT(5)
#define GEN_PLD_R_EMPTY BIT(4)
#define GEN_PLD_W_FULL BIT(3)
#define GEN_PLD_W_EMPTY BIT(2)
#define GEN_CMD_FULL BIT(1)
#define GEN_CMD_EMPTY BIT(0)
#define DSI_TO_CNT_CFG 0x078
#define HSTX_TO_CNT(x) UPDATE(x, 31, 16)
#define LPRX_TO_CNT(x) UPDATE(x, 15, 0)
#define DSI_HS_RD_TO_CNT 0x07c
#define HS_RD_TO_CNT(x) UPDATE(x, 15, 0)
#define DSI_LP_RD_TO_CNT 0x080
#define LP_RD_TO_CNT(x) UPDATE(x, 15, 0)
#define DSI_HS_WR_TO_CNT 0x084
#define HS_WR_TO_CNT(x) UPDATE(x, 15, 0)
#define DSI_LP_WR_TO_CNT 0x088
#define LP_WR_TO_CNT(x) UPDATE(x, 15, 0)
#define DSI_BTA_TO_CNT 0x08c
#define BTA_TO_CNT(x) UPDATE(x, 15, 0)
#define DSI_LPCLK_CTRL 0x094
#define AUTO_CLKLANE_CTRL BIT(1)
#define PHY_TXREQUESTCLKHS BIT(0)
#define DSI_PHY_TMR_LPCLK_CFG 0x098
#define PHY_CLKHS2LP_TIME(x) UPDATE(x, 25, 16)
#define PHY_CLKLP2HS_TIME(x) UPDATE(x, 9, 0)
#define DSI_PHY_TMR_CFG 0x09c
#define PHY_HS2LP_TIME(x) UPDATE(x, 31, 24)
#define PHY_LP2HS_TIME(x) UPDATE(x, 23, 16)
#define MAX_RD_TIME(x) UPDATE(x, 14, 0)
#define DSI_PHY_RSTZ 0x0a0
#define PHY_FORCEPLL BIT(3)
#define PHY_ENABLECLK BIT(2)
#define PHY_RSTZ BIT(1)
#define PHY_SHUTDOWNZ BIT(0)
#define DSI_PHY_IF_CFG 0x0a4
#define PHY_STOP_WAIT_TIME(x) UPDATE(x, 15, 8)
#define N_LANES(x) UPDATE(x, 1, 0)
#define DSI_PHY_STATUS 0x0b0
#define PHY_STOPSTATE3LANE BIT(11)
#define PHY_STOPSTATE2LANE BIT(9)
#define PHY_STOPSTATE1LANE BIT(7)
#define PHY_STOPSTATE0LANE BIT(4)
#define PHY_STOPSTATECLKLANE BIT(2)
#define PHY_LOCK BIT(0)
#define PHY_STOPSTATELANE (PHY_STOPSTATE0LANE | \
PHY_STOPSTATECLKLANE)
#define DSI_PHY_TST_CTRL0 0x0b4
#define PHY_TESTCLK BIT(1)
#define PHY_TESTCLR BIT(0)
#define DSI_PHY_TST_CTRL1 0x0b8
#define PHY_TESTEN BIT(16)
#define PHY_TESTDOUT_SHIFT 8
#define PHY_TESTDIN_MASK GENMASK(7, 0)
#define PHY_TESTDIN(x) UPDATE(x, 7, 0)
#define DSI_INT_ST0 0x0bc
#define DSI_INT_ST1 0x0c0
#define DSI_INT_MSK0 0x0c4
#define DSI_INT_MSK1 0x0c8
#define DSI_MAX_REGISGER DSI_INT_MSK1
/* control/test codes for DWC MIPI D-PHY Bidir 4L */
/* Test Code: 0x44 (HS RX Control of Lane 0) */
#define HSFREQRANGE(x) UPDATE(x, 6, 1)
/* Test Code: 0x17 (PLL Input Divider Ratio) */
#define INPUT_DIV(x) UPDATE(x, 6, 0)
/* Test Code: 0x18 (PLL Loop Divider Ratio) */
#define FEEDBACK_DIV_LO(x) UPDATE(x, 4, 0)
#define FEEDBACK_DIV_HI(x) (BIT(7) | UPDATE(x, 3, 0))
/* Test Code: 0x19 (PLL Input and Loop Divider Ratios Control) */
#define FEEDBACK_DIV_DEF_VAL_BYPASS BIT(5)
#define INPUT_DIV_DEF_VAL_BYPASS BIT(4)
#define PHY_STATUS_TIMEOUT_US 10000
#define CMD_PKT_STATUS_TIMEOUT_US 20000
enum soc_type {
PX30,
RK1808,
RK3128,
RK3288,
RK3368,
RK3399,
RK3568,
RV1126,
};
enum dpi_color_coding {
DPI_COLOR_CODING_16BIT_1,
DPI_COLOR_CODING_16BIT_2,
DPI_COLOR_CODING_16BIT_3,
DPI_COLOR_CODING_18BIT_1,
DPI_COLOR_CODING_18BIT_2,
DPI_COLOR_CODING_24BIT,
};
enum vid_mode_type {
VID_MODE_TYPE_NON_BURST_SYNC_PULSES,
VID_MODE_TYPE_NON_BURST_SYNC_EVENTS,
VID_MODE_TYPE_BURST,
};
enum operation_mode {
VIDEO_MODE,
COMMAND_MODE,
};
#define GRF_REG_FIELD(reg, lsb, msb) (((reg) << 10) | ((lsb) << 5) | (msb))
enum grf_reg_fields {
DPIUPDATECFG,
DPISHUTDN,
DPICOLORM,
VOPSEL,
TURNREQUEST,
TURNDISABLE,
SKEWCALHS,
FORCETXSTOPMODE,
FORCERXMODE,
ENABLE_N,
MASTERSLAVEZ,
ENABLECLK,
BASEDIR,
MAX_FIELDS,
};
struct dw_mipi_dsi_plat_data {
const u32 *dsi0_grf_reg_fields;
const u32 *dsi1_grf_reg_fields;
unsigned long max_bit_rate_per_lane;
enum soc_type soc_type;
};
struct mipi_dphy {
/* SNPS PHY */
struct regmap *regmap;
struct clk *ref_clk;
struct clk *cfg_clk;
u16 input_div;
u16 feedback_div;
/* Non-SNPS PHY */
struct phy *phy;
struct clk *hs_clk;
};
struct dw_mipi_dsi {
struct drm_encoder encoder;
struct drm_connector connector;
struct drm_bridge *bridge;
struct mipi_dsi_host host;
struct drm_panel *panel;
struct drm_display_mode mode;
struct device *dev;
struct device_node *client;
struct regmap *grf;
struct clk *pclk;
struct clk *hclk;
struct mipi_dphy dphy;
struct regmap *regmap;
struct reset_control *rst;
int irq;
int id;
/* dual-channel */
struct dw_mipi_dsi *master;
struct dw_mipi_dsi *slave;
bool data_swap;
unsigned int lane_mbps; /* per lane */
u32 channel;
u32 lanes;
u32 format;
unsigned long mode_flags;
const struct dw_mipi_dsi_plat_data *pdata;
struct rockchip_drm_sub_dev sub_dev;
};
static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
{
return container_of(host, struct dw_mipi_dsi, host);
}
static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con)
{
return container_of(con, struct dw_mipi_dsi, connector);
}
static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder)
{
return container_of(encoder, struct dw_mipi_dsi, encoder);
}
static void grf_field_write(struct dw_mipi_dsi *dsi, enum grf_reg_fields index,
unsigned int val)
{
const u32 field = dsi->id ?
dsi->pdata->dsi1_grf_reg_fields[index] :
dsi->pdata->dsi0_grf_reg_fields[index];
u32 reg;
u8 msb, lsb;
if (!field)
return;
reg = (field >> 10) & 0x3ffff;
lsb = (field >> 5) & 0x1f;
msb = (field >> 0) & 0x1f;
regmap_write(dsi->grf, reg, (val << lsb) | (GENMASK(msb, lsb) << 16));
}
static inline void dpishutdn_assert(struct dw_mipi_dsi *dsi)
{
grf_field_write(dsi, DPISHUTDN, 1);
}
static inline void dpishutdn_deassert(struct dw_mipi_dsi *dsi)
{
grf_field_write(dsi, DPISHUTDN, 0);
}
static int genif_wait_w_pld_fifo_not_full(struct dw_mipi_dsi *dsi)
{
u32 sts;
int ret;
ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
sts, !(sts & GEN_PLD_W_FULL),
0, 1000);
if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "generic write payload fifo is full\n");
return ret;
}
return 0;
}
static int genif_wait_cmd_fifo_not_full(struct dw_mipi_dsi *dsi)
{
u32 sts;
int ret;
ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
sts, !(sts & GEN_CMD_FULL),
0, 1000);
if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "generic write cmd fifo is full\n");
return ret;
}
return 0;
}
static int genif_wait_write_fifo_empty(struct dw_mipi_dsi *dsi)
{
u32 sts;
u32 mask;
int ret;
mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
sts, (sts & mask) == mask,
0, 1000);
if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "generic write fifo is full\n");
return ret;
}
return 0;
}
static inline void testif_testclk_assert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL0,
PHY_TESTCLK, PHY_TESTCLK);
udelay(1);
}
static inline void testif_testclk_deassert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL0, PHY_TESTCLK, 0);
udelay(1);
}
static inline void testif_testclr_assert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL0,
PHY_TESTCLR, PHY_TESTCLR);
udelay(1);
}
static inline void testif_testclr_deassert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL0, PHY_TESTCLR, 0);
udelay(1);
}
static inline void testif_testen_assert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL1,
PHY_TESTEN, PHY_TESTEN);
udelay(1);
}
static inline void testif_testen_deassert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL1, PHY_TESTEN, 0);
udelay(1);
}
static inline void testif_set_data(struct dw_mipi_dsi *dsi, u8 data)
{
regmap_update_bits(dsi->regmap, DSI_PHY_TST_CTRL1,
PHY_TESTDIN_MASK, PHY_TESTDIN(data));
udelay(1);
}
static inline u8 testif_get_data(struct dw_mipi_dsi *dsi)
{
u32 data = 0;
regmap_read(dsi->regmap, DSI_PHY_TST_CTRL1, &data);
return data >> PHY_TESTDOUT_SHIFT;
}
static void testif_test_code_write(struct dw_mipi_dsi *dsi, u8 test_code)
{
testif_testclk_assert(dsi);
testif_set_data(dsi, test_code);
testif_testen_assert(dsi);
testif_testclk_deassert(dsi);
testif_testen_deassert(dsi);
}
static void testif_test_data_write(struct dw_mipi_dsi *dsi, u8 test_data)
{
testif_testclk_deassert(dsi);
testif_set_data(dsi, test_data);
testif_testclk_assert(dsi);
}
static int testif_write(void *context, unsigned int reg, unsigned int value)
{
struct dw_mipi_dsi *dsi = context;
testif_testclr_deassert(dsi);
testif_test_code_write(dsi, reg);
testif_test_data_write(dsi, value);
DRM_DEV_DEBUG(dsi->dev, "test_code=0x%02x, ", reg);
DRM_DEV_DEBUG(dsi->dev, "test_data=0x%02x, ", value);
DRM_DEV_DEBUG(dsi->dev, "monitor_data=0x%02x\n", testif_get_data(dsi));
return 0;
}
static int testif_read(void *context, unsigned int reg, unsigned int *value)
{
struct dw_mipi_dsi *dsi = context;
testif_testclr_deassert(dsi);
testif_test_code_write(dsi, reg);
*value = testif_get_data(dsi);
testif_test_data_write(dsi, *value);
return 0;
}
static inline void mipi_dphy_enableclk_assert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ,
PHY_ENABLECLK, PHY_ENABLECLK);
udelay(1);
}
static inline void mipi_dphy_enableclk_deassert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_ENABLECLK, 0);
udelay(1);
}
static inline void mipi_dphy_shutdownz_assert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_SHUTDOWNZ, 0);
udelay(1);
}
static inline void mipi_dphy_shutdownz_deassert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ,
PHY_SHUTDOWNZ, PHY_SHUTDOWNZ);
udelay(1);
}
static inline void mipi_dphy_rstz_assert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_RSTZ, 0);
udelay(1);
}
static inline void mipi_dphy_rstz_deassert(struct dw_mipi_dsi *dsi)
{
regmap_update_bits(dsi->regmap, DSI_PHY_RSTZ, PHY_RSTZ, PHY_RSTZ);
udelay(1);
}
static void dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
{
struct mipi_dphy *dphy = &dsi->dphy;
const struct {
unsigned long max_lane_mbps;
u8 hsfreqrange;
} hsfreqrange_table[] = {
{ 90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
};
u8 hsfreqrange, counter;
unsigned int index, txbyteclkhs;
u16 n, m;
for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
if (dsi->lane_mbps <= hsfreqrange_table[index].max_lane_mbps)
break;
if (index == ARRAY_SIZE(hsfreqrange_table))
--index;
hsfreqrange = hsfreqrange_table[index].hsfreqrange;
regmap_write(dphy->regmap, 0x44, HSFREQRANGE(hsfreqrange));
txbyteclkhs = dsi->lane_mbps >> 3;
counter = txbyteclkhs * 60 / NSEC_PER_USEC;
regmap_write(dphy->regmap, 0x60, 0x80 | counter);
regmap_write(dphy->regmap, 0x70, 0x80 | counter);
n = dphy->input_div - 1;
m = dphy->feedback_div - 1;
regmap_write(dphy->regmap, 0x19,
FEEDBACK_DIV_DEF_VAL_BYPASS | INPUT_DIV_DEF_VAL_BYPASS);
regmap_write(dphy->regmap, 0x17, INPUT_DIV(n));
regmap_write(dphy->regmap, 0x18, FEEDBACK_DIV_LO(m));
regmap_write(dphy->regmap, 0x18, FEEDBACK_DIV_HI(m >> 5));
}
static int mipi_dphy_power_on(struct dw_mipi_dsi *dsi)
{
struct mipi_dphy *dphy = &dsi->dphy;
unsigned int val, mask;
int ret;
mipi_dphy_enableclk_deassert(dsi);
mipi_dphy_shutdownz_assert(dsi);
mipi_dphy_rstz_assert(dsi);
testif_testclr_assert(dsi);
/* Configures DPHY to work as a Master */
grf_field_write(dsi, MASTERSLAVEZ, 1);
/* Configures lane as TX */
grf_field_write(dsi, BASEDIR, 0);
/* Set all REQUEST inputs to zero */
grf_field_write(dsi, TURNREQUEST, 0);
grf_field_write(dsi, TURNDISABLE, 0);
grf_field_write(dsi, FORCETXSTOPMODE, 0);
grf_field_write(dsi, FORCERXMODE, 0);
udelay(1);
testif_testclr_deassert(dsi);
if (!dphy->phy)
dw_mipi_dsi_phy_init(dsi);
/* Enable Data Lane Module */
grf_field_write(dsi, ENABLE_N, GENMASK(dsi->lanes - 1, 0));
/* Enable Clock Lane Module */
grf_field_write(dsi, ENABLECLK, 1);
mipi_dphy_enableclk_assert(dsi);
mipi_dphy_shutdownz_deassert(dsi);
mipi_dphy_rstz_deassert(dsi);
usleep_range(1500, 2000);
if (dphy->phy) {
ret = phy_set_mode(dphy->phy, PHY_MODE_VIDEO_MIPI);
if (ret) {
DRM_DEV_ERROR(dsi->dev, "failed to set phy mode: %d\n",
ret);
return ret;
}
phy_power_on(dphy->phy);
}
ret = regmap_read_poll_timeout(dsi->regmap, DSI_PHY_STATUS,
val, val & PHY_LOCK, 0, 1000);
if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "PHY is not locked\n");
return ret;
}
usleep_range(100, 200);
mask = PHY_STOPSTATELANE;
ret = regmap_read_poll_timeout(dsi->regmap, DSI_PHY_STATUS,
val, (val & mask) == mask,
0, 1000);
if (ret < 0) {
DRM_DEV_ERROR(dsi->dev, "lane module is not in stop state\n");
return ret;
}
udelay(10);
return 0;
}
static void mipi_dphy_power_off(struct dw_mipi_dsi *dsi)
{
struct mipi_dphy *dphy = &dsi->dphy;
regmap_write(dsi->regmap, DSI_PHY_RSTZ, 0);
if (dphy->phy)
phy_power_off(dphy->phy);
}
static const struct regmap_config testif_regmap_config = {
.name = "phy",
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x97,
.fast_io = true,
.reg_write = testif_write,
.reg_read = testif_read,
};
static int mipi_dphy_attach(struct dw_mipi_dsi *dsi)
{
struct mipi_dphy *dphy = &dsi->dphy;
struct device *dev = dsi->dev;
int ret;
dphy->phy = devm_phy_optional_get(dev, "mipi_dphy");
if (IS_ERR(dphy->phy)) {
ret = PTR_ERR(dphy->phy);
DRM_DEV_ERROR(dev, "failed to get mipi dphy: %d\n", ret);
return ret;
}
if (dphy->phy) {
dphy->hs_clk = devm_clk_get(dev, "hs_clk");
if (IS_ERR(dphy->hs_clk)) {
ret = PTR_ERR(dphy->hs_clk);
DRM_DEV_ERROR(dev, "failed to get hs clock: %d\n", ret);
return ret;
}
} else {
dphy->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(dphy->ref_clk)) {
ret = PTR_ERR(dphy->ref_clk);
DRM_DEV_ERROR(dev,
"Unable to get pll reference clock: %d\n",
ret);
return ret;
}
dphy->cfg_clk = devm_clk_get(dev, "phy_cfg");
if (IS_ERR(dphy->cfg_clk)) {
if (PTR_ERR(dphy->cfg_clk) != -ENOENT) {
ret = PTR_ERR(dphy->cfg_clk);
DRM_DEV_ERROR(dev,
"Unable to get phy cfg clk: %d\n",
ret);
return ret;
}
/* Clock is optional (for RK3288) */
dphy->cfg_clk = NULL;
}
dphy->regmap = devm_regmap_init(dev, NULL, dsi,
&testif_regmap_config);
if (IS_ERR(dphy->regmap)) {
ret = PTR_ERR(dphy->regmap);
DRM_DEV_ERROR(dev, "failed to int phy regmap: %d\n",
ret);
return ret;
}
}
return 0;
}
static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
if (dsi->master)
return 0;
if (device->lanes < 1 || device->lanes > 8)
return -EINVAL;
dsi->client = device->dev.of_node;
dsi->lanes = device->lanes;
dsi->channel = device->channel;
dsi->format = device->format;
dsi->mode_flags = device->mode_flags;
return 0;
}
static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
return 0;
}
static int dw_mipi_dsi_turn_on_peripheral(struct dw_mipi_dsi *dsi)
{
dpishutdn_assert(dsi);
udelay(20);
dpishutdn_deassert(dsi);
return 0;
}
static int dw_mipi_dsi_shutdown_peripheral(struct dw_mipi_dsi *dsi)
{
dpishutdn_deassert(dsi);
udelay(20);
dpishutdn_assert(dsi);
return 0;
}
static int dw_mipi_dsi_read_from_fifo(struct dw_mipi_dsi *dsi,
const struct mipi_dsi_msg *msg)
{
u8 *payload = msg->rx_buf;
unsigned int vrefresh = drm_mode_vrefresh(&dsi->mode);
u16 length;
u32 val;
int ret;
ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
val, !(val & GEN_RD_CMD_BUSY),
0, DIV_ROUND_UP(1000000, vrefresh));
if (ret) {
DRM_DEV_ERROR(dsi->dev,
"entire response isn't stored in the FIFO\n");
return ret;
}
/* Receive payload */
for (length = msg->rx_len; length; length -= 4) {
ret = regmap_read_poll_timeout(dsi->regmap, DSI_CMD_PKT_STATUS,
val, !(val & GEN_PLD_R_EMPTY),
0, 1000);
if (ret) {
DRM_DEV_ERROR(dsi->dev, "Read payload FIFO is empty\n");
return ret;
}
regmap_read(dsi->regmap, DSI_GEN_PLD_DATA, &val);
switch (length) {
case 3:
payload[2] = (val >> 16) & 0xff;
/* Fall through */
case 2:
payload[1] = (val >> 8) & 0xff;
/* Fall through */
case 1:
payload[0] = val & 0xff;
return 0;
}
payload[0] = (val >> 0) & 0xff;
payload[1] = (val >> 8) & 0xff;
payload[2] = (val >> 16) & 0xff;
payload[3] = (val >> 24) & 0xff;
payload += 4;
}
return 0;
}
static ssize_t dw_mipi_dsi_transfer(struct dw_mipi_dsi *dsi,
const struct mipi_dsi_msg *msg)
{
struct mipi_dsi_packet packet;
int ret;
u32 val;
if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG,
ACK_RQST_EN, ACK_RQST_EN);
if (msg->flags & MIPI_DSI_MSG_USE_LPM) {
regmap_update_bits(dsi->regmap, DSI_VID_MODE_CFG,
LP_CMD_EN, LP_CMD_EN);
} else {
regmap_update_bits(dsi->regmap, DSI_VID_MODE_CFG, LP_CMD_EN, 0);
regmap_update_bits(dsi->regmap, DSI_LPCLK_CTRL,
PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
}
switch (msg->type) {
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
return dw_mipi_dsi_shutdown_peripheral(dsi);
case MIPI_DSI_TURN_ON_PERIPHERAL:
return dw_mipi_dsi_turn_on_peripheral(dsi);
case MIPI_DSI_DCS_SHORT_WRITE:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, DCS_SW_0P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
DCS_SW_0P_TX : 0);
break;
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, DCS_SW_1P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
DCS_SW_1P_TX : 0);
break;
case MIPI_DSI_DCS_LONG_WRITE:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, DCS_LW_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
DCS_LW_TX : 0);
break;
case MIPI_DSI_DCS_READ:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, DCS_SR_0P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
DCS_SR_0P_TX : 0);
break;
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG,
MAX_RD_PKT_SIZE,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
MAX_RD_PKT_SIZE : 0);
break;
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, GEN_SW_0P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
GEN_SW_0P_TX : 0);
break;
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, GEN_SW_1P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
GEN_SW_1P_TX : 0);
break;
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, GEN_SW_2P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
GEN_SW_2P_TX : 0);
break;
case MIPI_DSI_GENERIC_LONG_WRITE:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, GEN_LW_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
GEN_LW_TX : 0);
break;
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, GEN_SR_0P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
GEN_SR_0P_TX : 0);
break;
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, GEN_SR_1P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
GEN_SR_1P_TX : 0);
break;
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, GEN_SR_2P_TX,
msg->flags & MIPI_DSI_MSG_USE_LPM ?
GEN_SR_2P_TX : 0);
break;
default:
return -EINVAL;
}
/* create a packet to the DSI protocol */
ret = mipi_dsi_create_packet(&packet, msg);
if (ret) {
DRM_DEV_ERROR(dsi->dev, "failed to create packet: %d\n", ret);
return ret;
}
/* Send payload */
while (packet.payload_length >= 4) {
/*
* Alternatively, you can always keep the FIFO
* nearly full by monitoring the FIFO state until
* it is not full, and then writea single word of data.
* This solution is more resource consuming
* but it simultaneously avoids FIFO starvation,
* making it possible to use FIFO sizes smaller than
* the amount of data of the longest packet to be written.
*/
ret = genif_wait_w_pld_fifo_not_full(dsi);
if (ret)
return ret;
val = get_unaligned_le32(packet.payload);
regmap_write(dsi->regmap, DSI_GEN_PLD_DATA, val);
packet.payload += 4;
packet.payload_length -= 4;
}
val = 0;
switch (packet.payload_length) {
case 3:
val |= packet.payload[2] << 16;
/* Fall through */
case 2:
val |= packet.payload[1] << 8;
/* Fall through */
case 1:
val |= packet.payload[0];
regmap_write(dsi->regmap, DSI_GEN_PLD_DATA, val);
break;
}
ret = genif_wait_cmd_fifo_not_full(dsi);
if (ret)
return ret;
/* Send packet header */
val = get_unaligned_le32(packet.header);
regmap_write(dsi->regmap, DSI_GEN_HDR, val);
ret = genif_wait_write_fifo_empty(dsi);
if (ret)
return ret;
if (msg->rx_len) {
ret = dw_mipi_dsi_read_from_fifo(dsi, msg);
if (ret < 0)
return ret;
}
if (dsi->slave)
dw_mipi_dsi_transfer(dsi->slave, msg);
return msg->tx_len;
}
static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct dw_mipi_dsi *dsi = host_to_dsi(host);
return dw_mipi_dsi_transfer(dsi, msg);
}
static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
.attach = dw_mipi_dsi_host_attach,
.detach = dw_mipi_dsi_host_detach,
.transfer = dw_mipi_dsi_host_transfer,
};
static void dw_mipi_dsi_set_vid_mode(struct dw_mipi_dsi *dsi)
{
struct drm_display_mode *mode = &dsi->mode;
unsigned int lanebyteclk = (dsi->lane_mbps * USEC_PER_MSEC) >> 3;
unsigned int dpipclk = mode->crtc_clock;
u32 hline, hsa, hbp, hline_time, hsa_time, hbp_time;
u32 vactive, vsa, vfp, vbp;
u32 val;
val = LP_HFP_EN | LP_HBP_EN | LP_VACT_EN | LP_VFP_EN | LP_VBP_EN |
LP_VSA_EN;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP)
val &= ~LP_HFP_EN;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP)
val &= ~LP_HBP_EN;
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
val |= VID_MODE_TYPE_BURST;
else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
else
val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
regmap_write(dsi->regmap, DSI_VID_MODE_CFG, val);
if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
regmap_update_bits(dsi->regmap, DSI_LPCLK_CTRL,
AUTO_CLKLANE_CTRL, AUTO_CLKLANE_CTRL);
if (dsi->slave || dsi->master)
val = mode->hdisplay / 2;
else
val = mode->hdisplay;
regmap_write(dsi->regmap, DSI_VID_PKT_SIZE, VID_PKT_SIZE(val));
vactive = mode->vdisplay;
vsa = mode->vsync_end - mode->vsync_start;
vfp = mode->vsync_start - mode->vdisplay;
vbp = mode->vtotal - mode->vsync_end;
hsa = mode->hsync_end - mode->hsync_start;
hbp = mode->htotal - mode->hsync_end;
hline = mode->htotal;
hline_time = DIV_ROUND_CLOSEST_ULL(hline * lanebyteclk, dpipclk);
regmap_write(dsi->regmap, DSI_VID_HLINE_TIME,
VID_HLINE_TIME(hline_time));
hsa_time = DIV_ROUND_CLOSEST_ULL(hsa * lanebyteclk, dpipclk);
regmap_write(dsi->regmap, DSI_VID_HSA_TIME, VID_HSA_TIME(hsa_time));
hbp_time = DIV_ROUND_CLOSEST_ULL(hbp * lanebyteclk, dpipclk);
regmap_write(dsi->regmap, DSI_VID_HBP_TIME, VID_HBP_TIME(hbp_time));
regmap_write(dsi->regmap, DSI_VID_VACTIVE_LINES, vactive);
regmap_write(dsi->regmap, DSI_VID_VSA_LINES, vsa);
regmap_write(dsi->regmap, DSI_VID_VFP_LINES, vfp);
regmap_write(dsi->regmap, DSI_VID_VBP_LINES, vbp);
regmap_write(dsi->regmap, DSI_MODE_CFG, CMD_VIDEO_MODE(VIDEO_MODE));
}
static void dw_mipi_dsi_set_cmd_mode(struct dw_mipi_dsi *dsi)
{
struct drm_display_mode *mode = &dsi->mode;
regmap_write(dsi->regmap, DSI_DBI_VCID, DBI_VCID(dsi->channel));
regmap_update_bits(dsi->regmap, DSI_CMD_MODE_CFG, DCS_LW_TX, 0);
regmap_write(dsi->regmap, DSI_EDPI_CMD_SIZE,
EDPI_ALLOWED_CMD_SIZE(mode->hdisplay));
regmap_write(dsi->regmap, DSI_MODE_CFG,
CMD_VIDEO_MODE(COMMAND_MODE));
}
static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
{
regmap_write(dsi->regmap, DSI_PWR_UP, RESET);
regmap_write(dsi->regmap, DSI_LPCLK_CTRL, 0);
regmap_write(dsi->regmap, DSI_EDPI_CMD_SIZE, 0);
regmap_write(dsi->regmap, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
regmap_write(dsi->regmap, DSI_PWR_UP, POWER_UP);
if (dsi->slave)
dw_mipi_dsi_disable(dsi->slave);
}
static void dw_mipi_dsi_post_disable(struct dw_mipi_dsi *dsi)
{
regmap_write(dsi->regmap, DSI_INT_MSK0, 0);
regmap_write(dsi->regmap, DSI_INT_MSK1, 0);
regmap_write(dsi->regmap, DSI_PWR_UP, RESET);
mipi_dphy_power_off(dsi);
pm_runtime_put(dsi->dev);
if (dsi->slave)
dw_mipi_dsi_post_disable(dsi->slave);
}
static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
struct drm_crtc *crtc = encoder->crtc;
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
if (dsi->panel)
drm_panel_disable(dsi->panel);
if (IS_ENABLED(CONFIG_CPU_RK3568) && dsi->pdata->soc_type == RK3568)
vop2_standby(encoder->crtc, 1);
dw_mipi_dsi_disable(dsi);
if (dsi->panel)
drm_panel_unprepare(dsi->panel);
dw_mipi_dsi_post_disable(dsi);
if (IS_ENABLED(CONFIG_CPU_RK3568) && dsi->pdata->soc_type == RK3568)
vop2_standby(encoder->crtc, 0);
if (dsi->slave)
s->output_if &= ~(VOP_OUTPUT_IF_MIPI1 | VOP_OUTPUT_IF_MIPI0);
else
s->output_if &= ~(dsi->id ? VOP_OUTPUT_IF_MIPI1 : VOP_OUTPUT_IF_MIPI0);
}
static void dw_mipi_dsi_vop_routing(struct dw_mipi_dsi *dsi)
{
int pipe;
pipe = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
&dsi->encoder);
grf_field_write(dsi, VOPSEL, pipe);
if (dsi->slave)
grf_field_write(dsi->slave, VOPSEL, pipe);
}
static unsigned long dw_mipi_dsi_get_lane_rate(struct dw_mipi_dsi *dsi)
{
struct device *dev = dsi->dev;
const struct drm_display_mode *mode = &dsi->mode;
unsigned long max_lane_rate = dsi->pdata->max_bit_rate_per_lane;
unsigned long lane_rate;
unsigned int value;
int bpp, lanes;
u64 tmp;
/* optional override of the desired bandwidth */
if (!of_property_read_u32(dev->of_node, "rockchip,lane-rate", &value))
return value * USEC_PER_SEC;
bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
if (bpp < 0)
bpp = 24;
lanes = dsi->slave ? dsi->lanes * 2 : dsi->lanes;
tmp = (u64)mode->crtc_clock * 1000 * bpp;
do_div(tmp, lanes);
/* take 1 / 0.9, since mbps must big than bandwidth of RGB */
tmp *= 10;
do_div(tmp, 9);
if (tmp > max_lane_rate)
lane_rate = max_lane_rate;
else
lane_rate = tmp;
return lane_rate;
}
static void dw_mipi_dsi_calc_pll_cfg(struct dw_mipi_dsi *dsi,
unsigned long rate)
{
struct mipi_dphy *dphy = &dsi->dphy;
unsigned long fin, fout;
unsigned long fvco_min, fvco_max, best_freq = 984000000;
u8 min_prediv, max_prediv;
u8 _prediv, best_prediv = 2;
u16 _fbdiv, best_fbdiv = 82;
u32 min_delta = UINT_MAX;
fin = clk_get_rate(dphy->ref_clk);
fout = rate;
/* 5Mhz < Fref / N < 40MHz, 80MHz < Fvco < 1500Mhz */
min_prediv = DIV_ROUND_UP(fin, 40000000);
max_prediv = fin / 5000000;
fvco_min = 80000000;
fvco_max = 1500000000;
for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
u64 tmp, _fout;
u32 delta;
/* Fvco = Fref * M / N */
tmp = (u64)fout * _prediv;
do_div(tmp, fin);
_fbdiv = tmp;
/*
* Due to the use of a "by 2 pre-scaler," the range of the
* feedback multiplication value M is limited to even division
* numbers, and m must be greater than 12, less than 1000.
*/
if (_fbdiv <= 12 || _fbdiv >= 1000)
continue;
if (_fbdiv % 2)
++_fbdiv;
_fout = (u64)_fbdiv * fin;
do_div(_fout, _prediv);
if (_fout < fvco_min || _fout > fvco_max)
continue;
delta = abs(fout - _fout);
if (!delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = _fout;
break;
} else if (delta < min_delta) {
best_prediv = _prediv;
best_fbdiv = _fbdiv;
best_freq = _fout;
min_delta = delta;
}
}
dsi->lane_mbps = best_freq / USEC_PER_SEC;
dphy->input_div = best_prediv;
dphy->feedback_div = best_fbdiv;
if (dsi->slave) {
dsi->slave->lane_mbps = dsi->lane_mbps;
dsi->slave->dphy.input_div = dphy->input_div;
dsi->slave->dphy.feedback_div = dphy->feedback_div;
}
}
static void dw_mipi_dsi_pre_enable(struct dw_mipi_dsi *dsi)
{
u32 val;
pm_runtime_get_sync(dsi->dev);
regmap_write(dsi->regmap, DSI_PWR_UP, RESET);
regmap_write(dsi->regmap, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
val = DIV_ROUND_UP(dsi->lane_mbps >> 3, 20);
regmap_write(dsi->regmap, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) |
TX_ESC_CLK_DIVISION(val));
val = CRC_RX_EN | ECC_RX_EN | BTA_EN | EOTP_TX_EN;
if (dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)
val &= ~EOTP_TX_EN;
regmap_write(dsi->regmap, DSI_PCKHDL_CFG, val);
regmap_write(dsi->regmap, DSI_TO_CNT_CFG,
HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
regmap_write(dsi->regmap, DSI_BTA_TO_CNT, 0xd00);
regmap_write(dsi->regmap, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x14) |
PHY_LP2HS_TIME(0x10) | MAX_RD_TIME(10000));
regmap_write(dsi->regmap, DSI_PHY_TMR_LPCLK_CFG,
PHY_CLKHS2LP_TIME(0x40) | PHY_CLKLP2HS_TIME(0x40));
regmap_write(dsi->regmap, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
N_LANES(dsi->lanes - 1));
mipi_dphy_power_on(dsi);
regmap_write(dsi->regmap, DSI_PWR_UP, POWER_UP);
regmap_write(dsi->regmap, DSI_INT_MSK0, 0x1fffff);
regmap_write(dsi->regmap, DSI_INT_MSK1, 0x1f7f);
if (dsi->slave)
dw_mipi_dsi_pre_enable(dsi->slave);
}
static void dw_mipi_dsi_enable(struct dw_mipi_dsi *dsi)
{
struct drm_display_mode *mode = &dsi->mode;
u32 val;
regmap_write(dsi->regmap, DSI_PWR_UP, RESET);
switch (dsi->format) {
case MIPI_DSI_FMT_RGB666:
val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_2) | LOOSELY18_EN;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_1);
break;
case MIPI_DSI_FMT_RGB565:
val = DPI_COLOR_CODING(DPI_COLOR_CODING_16BIT_1);
break;
case MIPI_DSI_FMT_RGB888:
default:
val = DPI_COLOR_CODING(DPI_COLOR_CODING_24BIT);
break;
}
regmap_write(dsi->regmap, DSI_DPI_COLOR_CODING, val);
val = 0;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
val |= VSYNC_ACTIVE_LOW;
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
val |= HSYNC_ACTIVE_LOW;
regmap_write(dsi->regmap, DSI_DPI_CFG_POL, val);
regmap_write(dsi->regmap, DSI_DPI_VCID, DPI_VID(dsi->channel));
regmap_write(dsi->regmap, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4) |
INVACT_LPCMD_TIME(4));
regmap_update_bits(dsi->regmap, DSI_LPCLK_CTRL,
PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO)
dw_mipi_dsi_set_vid_mode(dsi);
else
dw_mipi_dsi_set_cmd_mode(dsi);
regmap_write(dsi->regmap, DSI_PWR_UP, POWER_UP);
if (dsi->slave)
dw_mipi_dsi_enable(dsi->slave);
}
static void dw_mipi_dsi_set_hs_clk(struct dw_mipi_dsi *dsi, unsigned long rate)
{
rate = clk_round_rate(dsi->dphy.hs_clk, rate);
clk_set_rate(dsi->dphy.hs_clk, rate);
dsi->lane_mbps = rate / USEC_PER_SEC;
}
static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
unsigned long lane_rate = dw_mipi_dsi_get_lane_rate(dsi);
if (dsi->dphy.phy)
dw_mipi_dsi_set_hs_clk(dsi, lane_rate);
else
dw_mipi_dsi_calc_pll_cfg(dsi, lane_rate);
if (dsi->slave && dsi->slave->dphy.phy)
dw_mipi_dsi_set_hs_clk(dsi->slave, lane_rate);
DRM_DEV_INFO(dsi->dev, "final DSI-Link bandwidth: %u x %d Mbps\n",
dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes);
dw_mipi_dsi_vop_routing(dsi);
if (IS_ENABLED(CONFIG_CPU_RK3568) && dsi->pdata->soc_type == RK3568)
vop2_standby(encoder->crtc, 1);
dw_mipi_dsi_pre_enable(dsi);
if (dsi->panel)
drm_panel_prepare(dsi->panel);
dw_mipi_dsi_enable(dsi);
if (IS_ENABLED(CONFIG_CPU_RK3568) && dsi->pdata->soc_type == RK3568)
vop2_standby(encoder->crtc, 0);
if (dsi->panel)
drm_panel_enable(dsi->panel);
}
static int
dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
struct drm_connector *connector = conn_state->connector;
struct drm_display_info *info = &connector->display_info;
switch (dsi->format) {
case MIPI_DSI_FMT_RGB888:
s->output_mode = ROCKCHIP_OUT_MODE_P888;
break;
case MIPI_DSI_FMT_RGB666:
s->output_mode = ROCKCHIP_OUT_MODE_P666;
break;
case MIPI_DSI_FMT_RGB565:
s->output_mode = ROCKCHIP_OUT_MODE_P565;
break;
default:
WARN_ON(1);
return -EINVAL;
}
if (info->num_bus_formats)
s->bus_format = info->bus_formats[0];
else
s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
s->output_type = DRM_MODE_CONNECTOR_DSI;
s->output_if = dsi->id ? VOP_OUTPUT_IF_MIPI1 : VOP_OUTPUT_IF_MIPI0;
s->bus_flags = info->bus_flags;
/* rk356x series drive mipi pixdata on posedge */
if (dsi->pdata->soc_type == RK3568) {
s->bus_flags &= ~DRM_BUS_FLAG_PIXDATA_NEGEDGE;
s->bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
}
s->tv_state = &conn_state->tv;
s->eotf = TRADITIONAL_GAMMA_SDR;
s->color_space = V4L2_COLORSPACE_DEFAULT;
if (dsi->slave) {
s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
if (dsi->data_swap)
s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
s->output_if |= VOP_OUTPUT_IF_MIPI1;
}
/* dual link dsi for rk3399 */
if (dsi->id && !dsi->dphy.phy)
s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
return 0;
}
static void
dw_mipi_dsi_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *connector_state)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
drm_mode_copy(&dsi->mode, &crtc_state->adjusted_mode);
if (dsi->slave)
drm_mode_copy(&dsi->slave->mode, &crtc_state->adjusted_mode);
}
static int dw_mipi_dsi_loader_protect(struct dw_mipi_dsi *dsi, bool on)
{
if (on)
pm_runtime_get_sync(dsi->dev);
else
pm_runtime_put(dsi->dev);
if (dsi->slave)
dw_mipi_dsi_loader_protect(dsi->slave, on);
return 0;
}
static int dw_mipi_dsi_encoder_loader_protect(struct drm_encoder *encoder,
bool on)
{
struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
if (dsi->panel)
drm_panel_loader_protect(dsi->panel, on);
return dw_mipi_dsi_loader_protect(dsi, on);
}
static const struct drm_encoder_helper_funcs
dw_mipi_dsi_encoder_helper_funcs = {
.enable = dw_mipi_dsi_encoder_enable,
.disable = dw_mipi_dsi_encoder_disable,
.atomic_check = dw_mipi_dsi_encoder_atomic_check,
.atomic_mode_set = dw_mipi_dsi_encoder_atomic_mode_set,
.loader_protect = dw_mipi_dsi_encoder_loader_protect,
};
static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector)
{
struct dw_mipi_dsi *dsi = con_to_dsi(connector);
return drm_panel_get_modes(dsi->panel);
}
static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
.get_modes = dw_mipi_dsi_connector_get_modes,
};
static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static int
dw_mipi_dsi_atomic_connector_get_property(struct drm_connector *connector,
const struct drm_connector_state *state,
struct drm_property *property,
uint64_t *val)
{
struct dw_mipi_dsi *dsi = con_to_dsi(connector);
struct rockchip_drm_private *private = connector->dev->dev_private;
if (property == private->connector_id_prop) {
*val = dsi->id;
return 0;
}
DRM_ERROR("failed to get rockchip dsi property\n");
return -EINVAL;
}
static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = dw_mipi_dsi_drm_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_get_property = dw_mipi_dsi_atomic_connector_get_property,
};
static int dw_mipi_dsi_dual_channel_probe(struct dw_mipi_dsi *dsi)
{
struct device_node *np;
struct platform_device *secondary;
np = of_parse_phandle(dsi->dev->of_node, "rockchip,dual-channel", 0);
if (np) {
dsi->data_swap = of_property_read_bool(dsi->dev->of_node,
"rockchip,data-swap");
secondary = of_find_device_by_node(np);
dsi->slave = platform_get_drvdata(secondary);
of_node_put(np);
if (!dsi->slave)
return -EPROBE_DEFER;
dsi->slave->master = dsi;
dsi->lanes /= 2;
dsi->slave->lanes = dsi->lanes;
dsi->slave->channel = dsi->channel;
dsi->slave->format = dsi->format;
dsi->slave->mode_flags = dsi->mode_flags;
}
return 0;
}
static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
void *data)
{
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
struct drm_device *drm = data;
struct drm_encoder *encoder = &dsi->encoder;
struct drm_connector *connector = &dsi->connector;
int ret;
ret = dw_mipi_dsi_dual_channel_probe(dsi);
if (ret)
return ret;
if (dsi->master)
return 0;
ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1,
&dsi->panel, &dsi->bridge);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to find panel or bridge: %d\n", ret);
return ret;
}
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
dev->of_node);
/*
* If we failed to find the CRTC(s) which this encoder is
* supposed to be connected to, it's because the CRTC has
* not been registered yet. Defer probing, and hope that
* the required CRTC is added later.
*/
if (encoder->possible_crtcs == 0)
return -EPROBE_DEFER;
ret = drm_encoder_init(drm, encoder, &dw_mipi_dsi_encoder_funcs,
DRM_MODE_ENCODER_DSI, NULL);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to initialize encoder\n");
return ret;
}
drm_encoder_helper_add(encoder, &dw_mipi_dsi_encoder_helper_funcs);
if (dsi->panel) {
struct rockchip_drm_private *private = drm->dev_private;
ret = drm_connector_init(drm, connector, &dw_mipi_dsi_atomic_connector_funcs,
DRM_MODE_CONNECTOR_DSI);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to initialize connector\n");
goto encoder_cleanup;
}
drm_connector_helper_add(connector,
&dw_mipi_dsi_connector_helper_funcs);
drm_connector_attach_encoder(connector, encoder);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to attach encoder: %d\n", ret);
goto connector_cleanup;
}
ret = drm_panel_attach(dsi->panel, connector);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to attach panel: %d\n", ret);
goto connector_cleanup;
}
dsi->sub_dev.connector = &dsi->connector;
dsi->sub_dev.of_node = dev->of_node;
rockchip_drm_register_sub_dev(&dsi->sub_dev);
drm_object_attach_property(&connector->base, private->connector_id_prop, 0);
} else {
dsi->bridge->driver_private = &dsi->host;
dsi->bridge->encoder = encoder;
ret = drm_bridge_attach(encoder, dsi->bridge, NULL);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to attach bridge: %d\n", ret);
goto encoder_cleanup;
}
encoder->bridge = dsi->bridge;
}
pm_runtime_enable(dsi->dev);
if (dsi->slave)
pm_runtime_enable(dsi->slave->dev);
return 0;
connector_cleanup:
connector->funcs->destroy(connector);
encoder_cleanup:
encoder->funcs->destroy(encoder);
return ret;
}
static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
void *data)
{
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
if (dsi->sub_dev.connector)
rockchip_drm_unregister_sub_dev(&dsi->sub_dev);
pm_runtime_disable(dsi->dev);
if (dsi->slave)
pm_runtime_disable(dsi->slave->dev);
if (dsi->panel)
drm_panel_detach(dsi->panel);
dsi->connector.funcs->destroy(&dsi->connector);
dsi->encoder.funcs->destroy(&dsi->encoder);
}
static const struct component_ops dw_mipi_dsi_ops = {
.bind = dw_mipi_dsi_bind,
.unbind = dw_mipi_dsi_unbind,
};
static const char * const dphy_error[] = {
"ErrEsc escape entry error from Lane 0",
"ErrSyncEsc low-power data transmission synchronization error from Lane 0",
"the ErrControl error from Lane 0",
"LP0 contention error ErrContentionLP0 from Lane 0",
"LP1 contention error ErrContentionLP1 from Lane 0",
};
static const char * const ack_with_err[] = {
"the SoT error from the Acknowledge error report",
"the SoT Sync error from the Acknowledge error report",
"the EoT Sync error from the Acknowledge error report",
"the Escape Mode Entry Command error from the Acknowledge error report",
"the LP Transmit Sync error from the Acknowledge error report",
"the Peripheral Timeout error from the Acknowledge Error report",
"the False Control error from the Acknowledge error report",
"the reserved (specific to device) from the Acknowledge error report",
"the ECC error, single-bit (detected and corrected) from the Acknowledge error report",
"the ECC error, multi-bit (detected, not corrected) from the Acknowledge error report",
"the checksum error (long packet only) from the Acknowledge error report",
"the not recognized DSI data type from the Acknowledge error report",
"the DSI VC ID Invalid from the Acknowledge error report",
"the invalid transmission length from the Acknowledge error report",
"the reserved (specific to device) from the Acknowledge error report",
"the DSI protocol violation from the Acknowledge error report",
};
static const char * const error_report[] = {
"Host reports that the configured timeout counter for the high-speed transmission has expired",
"Host reports that the configured timeout counter for the low-power reception has expired",
"Host reports that a received packet contains a single bit error",
"Host reports that a received packet contains multiple ECC errors",
"Host reports that a received long packet has a CRC error in its payload",
"Host receives a transmission that does not end in the expected by boundaries",
"Host receives a transmission that does not end with an End of Transmission packet",
"An overflow occurs in the DPI pixel payload FIFO",
"An overflow occurs in the Generic command FIFO",
"An overflow occurs in the Generic write payload FIFO",
"An underflow occurs in the Generic write payload FIFO",
"An underflow occurs in the Generic read FIFO",
"An overflow occurs in the Generic read FIFO",
};
static irqreturn_t dw_mipi_dsi_irq_handler(int irq, void *dev_id)
{
struct dw_mipi_dsi *dsi = dev_id;
u32 int_st0, int_st1;
unsigned int i;
regmap_read(dsi->regmap, DSI_INT_ST0, &int_st0);
regmap_read(dsi->regmap, DSI_INT_ST1, &int_st1);
for (i = 0; i < ARRAY_SIZE(ack_with_err); i++)
if (int_st0 & BIT(i))
DRM_DEV_DEBUG(dsi->dev, "%s\n", ack_with_err[i]);
for (i = 0; i < ARRAY_SIZE(dphy_error); i++)
if (int_st0 & BIT(16 + i))
DRM_DEV_DEBUG(dsi->dev, "%s\n", dphy_error[i]);
for (i = 0; i < ARRAY_SIZE(error_report); i++)
if (int_st1 & BIT(i))
DRM_DEV_DEBUG(dsi->dev, "%s\n", error_report[i]);
return IRQ_HANDLED;
}
static const struct regmap_config dw_mipi_dsi_regmap_config = {
.name = "host",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.fast_io = true,
.max_register = DSI_MAX_REGISGER,
};
static int dw_mipi_dsi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_mipi_dsi *dsi;
struct resource *res;
void __iomem *regs;
int id;
int ret;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
return -ENOMEM;
id = of_alias_get_id(dev->of_node, "dsi");
if (id < 0)
id = 0;
dsi->dev = dev;
dsi->id = id;
dsi->pdata = of_device_get_match_data(dev);
platform_set_drvdata(pdev, dsi);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
dsi->irq = platform_get_irq(pdev, 0);
if (dsi->irq < 0)
return dsi->irq;
dsi->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(dsi->pclk)) {
ret = PTR_ERR(dsi->pclk);
DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
return ret;
}
dsi->hclk = devm_clk_get_optional(dev, "hclk");
if (IS_ERR(dsi->hclk)) {
ret = PTR_ERR(dsi->hclk);
DRM_DEV_ERROR(dev, "Unable to get hclk: %d\n", ret);
return ret;
}
dsi->regmap = devm_regmap_init_mmio(dev, regs,
&dw_mipi_dsi_regmap_config);
if (IS_ERR(dsi->regmap)) {
ret = PTR_ERR(dsi->regmap);
DRM_DEV_ERROR(dev, "failed to init register map: %d\n", ret);
return ret;
}
dsi->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
if (IS_ERR(dsi->grf)) {
ret = PTR_ERR(dsi->grf);
DRM_DEV_ERROR(dsi->dev, "Unable to get grf: %d\n", ret);
return ret;
}
dsi->rst = devm_reset_control_get(dev, "apb");
if (IS_ERR(dsi->rst)) {
ret = PTR_ERR(dsi->rst);
DRM_DEV_ERROR(dev,
"Unable to get reset control: %d\n", ret);
return ret;
}
ret = mipi_dphy_attach(dsi);
if (ret)
return ret;
ret = devm_request_irq(dev, dsi->irq, dw_mipi_dsi_irq_handler,
IRQF_SHARED, dev_name(dev), dsi);
if (ret) {
DRM_DEV_ERROR(dev, "failed to request irq: %d\n", ret);
return ret;
}
dsi->host.ops = &dw_mipi_dsi_host_ops;
dsi->host.dev = dev;
ret = mipi_dsi_host_register(&dsi->host);
if (ret) {
DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
return ret;
}
return component_add(&pdev->dev, &dw_mipi_dsi_ops);
}
static int dw_mipi_dsi_remove(struct platform_device *pdev)
{
struct dw_mipi_dsi *dsi = platform_get_drvdata(pdev);
component_del(dsi->dev, &dw_mipi_dsi_ops);
mipi_dsi_host_unregister(&dsi->host);
return 0;
}
static int __maybe_unused dw_mipi_dsi_runtime_suspend(struct device *dev)
{
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
clk_disable_unprepare(dsi->pclk);
clk_disable_unprepare(dsi->hclk);
clk_disable_unprepare(dsi->dphy.hs_clk);
clk_disable_unprepare(dsi->dphy.ref_clk);
clk_disable_unprepare(dsi->dphy.cfg_clk);
return 0;
}
static int __maybe_unused dw_mipi_dsi_runtime_resume(struct device *dev)
{
struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
clk_prepare_enable(dsi->dphy.cfg_clk);
clk_prepare_enable(dsi->dphy.ref_clk);
clk_prepare_enable(dsi->dphy.hs_clk);
clk_prepare_enable(dsi->hclk);
clk_prepare_enable(dsi->pclk);
return 0;
}
static const struct dev_pm_ops dw_mipi_dsi_pm_ops = {
SET_RUNTIME_PM_OPS(dw_mipi_dsi_runtime_suspend,
dw_mipi_dsi_runtime_resume, NULL)
};
static const u32 px30_dsi_grf_reg_fields[MAX_FIELDS] = {
[DPIUPDATECFG] = GRF_REG_FIELD(0x0434, 7, 7),
[DPICOLORM] = GRF_REG_FIELD(0x0434, 3, 3),
[DPISHUTDN] = GRF_REG_FIELD(0x0434, 2, 2),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0438, 7, 10),
[FORCERXMODE] = GRF_REG_FIELD(0x0438, 6, 6),
[TURNDISABLE] = GRF_REG_FIELD(0x0438, 5, 5),
[VOPSEL] = GRF_REG_FIELD(0x0438, 0, 0),
};
static const struct dw_mipi_dsi_plat_data px30_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = px30_dsi_grf_reg_fields,
.max_bit_rate_per_lane = 1000000000UL,
.soc_type = PX30,
};
static const u32 rk1808_dsi_grf_reg_fields[MAX_FIELDS] = {
[MASTERSLAVEZ] = GRF_REG_FIELD(0x0440, 8, 8),
[DPIUPDATECFG] = GRF_REG_FIELD(0x0440, 7, 7),
[DPICOLORM] = GRF_REG_FIELD(0x0440, 3, 3),
[DPISHUTDN] = GRF_REG_FIELD(0x0440, 2, 2),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0444, 7, 10),
[FORCERXMODE] = GRF_REG_FIELD(0x0444, 6, 6),
[TURNDISABLE] = GRF_REG_FIELD(0x0444, 5, 5),
};
static const struct dw_mipi_dsi_plat_data rk1808_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = rk1808_dsi_grf_reg_fields,
.max_bit_rate_per_lane = 2000000000UL,
.soc_type = RK1808,
};
static const u32 rk3128_dsi_grf_reg_fields[MAX_FIELDS] = {
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0150, 10, 13),
[FORCERXMODE] = GRF_REG_FIELD(0x0150, 9, 9),
[TURNDISABLE] = GRF_REG_FIELD(0x0150, 8, 8),
[DPICOLORM] = GRF_REG_FIELD(0x0150, 5, 5),
[DPISHUTDN] = GRF_REG_FIELD(0x0150, 4, 4),
};
static const struct dw_mipi_dsi_plat_data rk3128_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = rk3128_dsi_grf_reg_fields,
.max_bit_rate_per_lane = 1000000000UL,
.soc_type = RK3128,
};
static const u32 rk3288_dsi0_grf_reg_fields[MAX_FIELDS] = {
[DPICOLORM] = GRF_REG_FIELD(0x025c, 8, 8),
[DPISHUTDN] = GRF_REG_FIELD(0x025c, 7, 7),
[VOPSEL] = GRF_REG_FIELD(0x025c, 6, 6),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0264, 8, 11),
[FORCERXMODE] = GRF_REG_FIELD(0x0264, 4, 7),
[TURNDISABLE] = GRF_REG_FIELD(0x0264, 0, 3),
[TURNREQUEST] = GRF_REG_FIELD(0x03a4, 8, 10),
[DPIUPDATECFG] = GRF_REG_FIELD(0x03a8, 0, 0),
};
static const u32 rk3288_dsi1_grf_reg_fields[MAX_FIELDS] = {
[DPICOLORM] = GRF_REG_FIELD(0x025c, 11, 11),
[DPISHUTDN] = GRF_REG_FIELD(0x025c, 10, 10),
[VOPSEL] = GRF_REG_FIELD(0x025c, 9, 9),
[ENABLE_N] = GRF_REG_FIELD(0x0268, 12, 15),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0268, 8, 11),
[FORCERXMODE] = GRF_REG_FIELD(0x0268, 4, 7),
[TURNDISABLE] = GRF_REG_FIELD(0x0268, 0, 3),
[BASEDIR] = GRF_REG_FIELD(0x027c, 15, 15),
[MASTERSLAVEZ] = GRF_REG_FIELD(0x027c, 14, 14),
[ENABLECLK] = GRF_REG_FIELD(0x027c, 12, 12),
[TURNREQUEST] = GRF_REG_FIELD(0x03a4, 4, 7),
[DPIUPDATECFG] = GRF_REG_FIELD(0x03a8, 1, 1),
};
static const struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = rk3288_dsi0_grf_reg_fields,
.dsi1_grf_reg_fields = rk3288_dsi1_grf_reg_fields,
.max_bit_rate_per_lane = 1500000000UL,
.soc_type = RK3288,
};
static const u32 rk3368_dsi_grf_reg_fields[MAX_FIELDS] = {
[DPIUPDATECFG] = GRF_REG_FIELD(0x0418, 7, 7),
[DPICOLORM] = GRF_REG_FIELD(0x0418, 3, 3),
[DPISHUTDN] = GRF_REG_FIELD(0x0418, 2, 2),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x041c, 7, 10),
[FORCERXMODE] = GRF_REG_FIELD(0x041c, 6, 6),
[TURNDISABLE] = GRF_REG_FIELD(0x041c, 5, 5),
};
static const struct dw_mipi_dsi_plat_data rk3368_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = rk3368_dsi_grf_reg_fields,
.max_bit_rate_per_lane = 1000000000UL,
.soc_type = RK3368,
};
static const u32 rk3399_dsi0_grf_reg_fields[MAX_FIELDS] = {
[DPIUPDATECFG] = GRF_REG_FIELD(0x6224, 15, 15),
[DPISHUTDN] = GRF_REG_FIELD(0x6224, 14, 14),
[DPICOLORM] = GRF_REG_FIELD(0x6224, 13, 13),
[VOPSEL] = GRF_REG_FIELD(0x6250, 0, 0),
[TURNREQUEST] = GRF_REG_FIELD(0x6258, 12, 15),
[TURNDISABLE] = GRF_REG_FIELD(0x6258, 8, 11),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x6258, 4, 7),
[FORCERXMODE] = GRF_REG_FIELD(0x6258, 0, 3),
};
static const u32 rk3399_dsi1_grf_reg_fields[MAX_FIELDS] = {
[VOPSEL] = GRF_REG_FIELD(0x6250, 4, 4),
[DPIUPDATECFG] = GRF_REG_FIELD(0x6250, 3, 3),
[DPISHUTDN] = GRF_REG_FIELD(0x6250, 2, 2),
[DPICOLORM] = GRF_REG_FIELD(0x6250, 1, 1),
[TURNDISABLE] = GRF_REG_FIELD(0x625c, 12, 15),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x625c, 8, 11),
[FORCERXMODE] = GRF_REG_FIELD(0x625c, 4, 7),
[ENABLE_N] = GRF_REG_FIELD(0x625c, 0, 3),
[MASTERSLAVEZ] = GRF_REG_FIELD(0x6260, 7, 7),
[ENABLECLK] = GRF_REG_FIELD(0x6260, 6, 6),
[BASEDIR] = GRF_REG_FIELD(0x6260, 5, 5),
[TURNREQUEST] = GRF_REG_FIELD(0x6260, 0, 3),
};
static const struct dw_mipi_dsi_plat_data rk3399_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = rk3399_dsi0_grf_reg_fields,
.dsi1_grf_reg_fields = rk3399_dsi1_grf_reg_fields,
.max_bit_rate_per_lane = 1500000000UL,
.soc_type = RK3399,
};
static const u32 rk3568_dsi0_grf_reg_fields[MAX_FIELDS] = {
[DPIUPDATECFG] = GRF_REG_FIELD(0x0360, 2, 2),
[DPICOLORM] = GRF_REG_FIELD(0x0360, 1, 1),
[DPISHUTDN] = GRF_REG_FIELD(0x0360, 0, 0),
[SKEWCALHS] = GRF_REG_FIELD(0x0368, 11, 15),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x0368, 4, 7),
[TURNDISABLE] = GRF_REG_FIELD(0x0368, 2, 2),
[FORCERXMODE] = GRF_REG_FIELD(0x0368, 0, 0),
};
static const u32 rk3568_dsi1_grf_reg_fields[MAX_FIELDS] = {
[DPIUPDATECFG] = GRF_REG_FIELD(0x0360, 10, 10),
[DPICOLORM] = GRF_REG_FIELD(0x0360, 9, 9),
[DPISHUTDN] = GRF_REG_FIELD(0x0360, 8, 8),
[SKEWCALHS] = GRF_REG_FIELD(0x036c, 11, 15),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x036c, 4, 7),
[TURNDISABLE] = GRF_REG_FIELD(0x036c, 2, 2),
[FORCERXMODE] = GRF_REG_FIELD(0x036c, 0, 0),
};
static const struct dw_mipi_dsi_plat_data rk3568_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = rk3568_dsi0_grf_reg_fields,
.dsi1_grf_reg_fields = rk3568_dsi1_grf_reg_fields,
.max_bit_rate_per_lane = 1200000000UL,
.soc_type = RK3568,
};
static const u32 rv1126_dsi_grf_reg_fields[MAX_FIELDS] = {
[DPIUPDATECFG] = GRF_REG_FIELD(0x0008, 5, 5),
[DPISHUTDN] = GRF_REG_FIELD(0x0008, 4, 4),
[DPICOLORM] = GRF_REG_FIELD(0x0008, 3, 3),
[FORCETXSTOPMODE] = GRF_REG_FIELD(0x10220, 4, 7),
[TURNDISABLE] = GRF_REG_FIELD(0x10220, 2, 2),
[FORCERXMODE] = GRF_REG_FIELD(0x10220, 0, 0),
};
static const struct dw_mipi_dsi_plat_data rv1126_mipi_dsi_plat_data = {
.dsi0_grf_reg_fields = rv1126_dsi_grf_reg_fields,
.max_bit_rate_per_lane = 1000000000UL,
.soc_type = RV1126,
};
static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
{
.compatible = "rockchip,px30-mipi-dsi",
.data = &px30_mipi_dsi_plat_data,
}, {
.compatible = "rockchip,rk1808-mipi-dsi",
.data = &rk1808_mipi_dsi_plat_data,
}, {
.compatible = "rockchip,rk3128-mipi-dsi",
.data = &rk3128_mipi_dsi_plat_data,
}, {
.compatible = "rockchip,rk3288-mipi-dsi",
.data = &rk3288_mipi_dsi_plat_data,
}, {
.compatible = "rockchip,rk3368-mipi-dsi",
.data = &rk3368_mipi_dsi_plat_data,
}, {
.compatible = "rockchip,rk3399-mipi-dsi",
.data = &rk3399_mipi_dsi_plat_data,
}, {
.compatible = "rockchip,rk3568-mipi-dsi",
.data = &rk3568_mipi_dsi_plat_data,
}, {
.compatible = "rockchip,rv1126-mipi-dsi",
.data = &rv1126_mipi_dsi_plat_data,
},
{}
};
MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
struct platform_driver dw_mipi_dsi_driver = {
.probe = dw_mipi_dsi_probe,
.remove = dw_mipi_dsi_remove,
.driver = {
.of_match_table = dw_mipi_dsi_dt_ids,
.name = "dw-mipi-dsi",
.pm = &dw_mipi_dsi_pm_ops,
},
};