diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/tc358767.c')
| -rw-r--r-- | drivers/gpu/drm/bridge/tc358767.c | 681 |
1 files changed, 550 insertions, 131 deletions
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 2a58eb271f70..4097fef4b86b 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -41,8 +41,24 @@ /* Registers */ +/* DSI D-PHY Layer registers */ +#define D0W_DPHYCONTTX 0x0004 +#define CLW_DPHYCONTTX 0x0020 +#define D0W_DPHYCONTRX 0x0024 +#define D1W_DPHYCONTRX 0x0028 +#define D2W_DPHYCONTRX 0x002c +#define D3W_DPHYCONTRX 0x0030 +#define COM_DPHYCONTRX 0x0038 +#define CLW_CNTRL 0x0040 +#define D0W_CNTRL 0x0044 +#define D1W_CNTRL 0x0048 +#define D2W_CNTRL 0x004c +#define D3W_CNTRL 0x0050 +#define TESTMODE_CNTRL 0x0054 + /* PPI layer registers */ #define PPI_STARTPPI 0x0104 /* START control bit */ +#define PPI_BUSYPPI 0x0108 /* PPI busy status */ #define PPI_LPTXTIMECNT 0x0114 /* LPTX timing signal */ #define LPX_PERIOD 3 #define PPI_LANEENABLE 0x0134 @@ -59,6 +75,7 @@ /* DSI layer registers */ #define DSI_STARTDSI 0x0204 /* START control bit of DSI-TX */ +#define DSI_BUSYDSI 0x0208 /* DSI busy status */ #define DSI_LANEENABLE 0x0210 /* Enables each lane */ #define DSI_RX_START BIT(0) @@ -69,6 +86,20 @@ #define LANEENABLE_L2EN BIT(1) #define LANEENABLE_L3EN BIT(2) +#define DSI_LANESTATUS0 0x0214 /* DSI lane status 0 */ +#define DSI_LANESTATUS1 0x0218 /* DSI lane status 1 */ +#define DSI_INTSTATUS 0x0220 /* Interrupt Status */ +#define DSI_INTMASK 0x0224 /* Interrupt Mask */ +#define DSI_INTCLR 0x0228 /* Interrupt Clear */ +#define DSI_LPTXTO 0x0230 /* LPTX Time Out Counter */ + +/* DSI General Registers */ +#define DSIERRCNT 0x0300 /* DSI Error Count Register */ + +/* DSI Application Layer Registers */ +#define APLCTRL 0x0400 /* Application layer Control Register */ +#define RDPKTLN 0x0404 /* DSI Read packet Length Register */ + /* Display Parallel Input Interface */ #define DPIPXLFMT 0x0440 #define VS_POL_ACTIVE_LOW (1 << 10) @@ -114,35 +145,39 @@ #define VFUEN BIT(0) /* Video Frame Timing Upload */ /* System */ -#define TC_IDREG 0x0500 -#define SYSSTAT 0x0508 -#define SYSCTRL 0x0510 -#define DP0_AUDSRC_NO_INPUT (0 << 3) -#define DP0_AUDSRC_I2S_RX (1 << 3) -#define DP0_VIDSRC_NO_INPUT (0 << 0) -#define DP0_VIDSRC_DSI_RX (1 << 0) -#define DP0_VIDSRC_DPI_RX (2 << 0) -#define DP0_VIDSRC_COLOR_BAR (3 << 0) -#define SYSRSTENB 0x050c +#define TC_IDREG 0x0500 /* Chip ID and Revision ID */ +#define SYSBOOT 0x0504 /* System BootStrap Status Register */ +#define SYSSTAT 0x0508 /* System Status Register */ +#define SYSRSTENB 0x050c /* System Reset/Enable Register */ #define ENBI2C (1 << 0) #define ENBLCD0 (1 << 2) #define ENBBM (1 << 3) #define ENBDSIRX (1 << 4) #define ENBREG (1 << 5) #define ENBHDCP (1 << 8) -#define GPIOM 0x0540 -#define GPIOC 0x0544 -#define GPIOO 0x0548 -#define GPIOI 0x054c -#define INTCTL_G 0x0560 -#define INTSTS_G 0x0564 +#define SYSCTRL 0x0510 /* System Control Register */ +#define DP0_AUDSRC_NO_INPUT (0 << 3) +#define DP0_AUDSRC_I2S_RX (1 << 3) +#define DP0_VIDSRC_NO_INPUT (0 << 0) +#define DP0_VIDSRC_DSI_RX (1 << 0) +#define DP0_VIDSRC_DPI_RX (2 << 0) +#define DP0_VIDSRC_COLOR_BAR (3 << 0) +#define GPIOM 0x0540 /* GPIO Mode Control Register */ +#define GPIOC 0x0544 /* GPIO Direction Control Register */ +#define GPIOO 0x0548 /* GPIO Output Register */ +#define GPIOI 0x054c /* GPIO Input Register */ +#define INTCTL_G 0x0560 /* General Interrupts Control Register */ +#define INTSTS_G 0x0564 /* General Interrupts Status Register */ #define INT_SYSERR BIT(16) #define INT_GPIO_H(x) (1 << (x == 0 ? 2 : 10)) #define INT_GPIO_LC(x) (1 << (x == 0 ? 3 : 11)) -#define INT_GP0_LCNT 0x0584 -#define INT_GP1_LCNT 0x0588 +#define TEST_INT_C 0x0570 /* Test Interrupts Control Register */ +#define TEST_INT_S 0x0574 /* Test Interrupts Status Register */ + +#define INT_GP0_LCNT 0x0584 /* Interrupt GPIO0 Low Count Value Register */ +#define INT_GP1_LCNT 0x0588 /* Interrupt GPIO1 Low Count Value Register */ /* Control */ #define DP0CTL 0x0600 @@ -152,9 +187,12 @@ #define DP_EN BIT(0) /* Enable DPTX function */ /* Clocks */ -#define DP0_VIDMNGEN0 0x0610 -#define DP0_VIDMNGEN1 0x0614 -#define DP0_VMNGENSTATUS 0x0618 +#define DP0_VIDMNGEN0 0x0610 /* DP0 Video Force M Value Register */ +#define DP0_VIDMNGEN1 0x0614 /* DP0 Video Force N Value Register */ +#define DP0_VMNGENSTATUS 0x0618 /* DP0 Video Current M Value Register */ +#define DP0_AUDMNGEN0 0x0628 /* DP0 Audio Force M Value Register */ +#define DP0_AUDMNGEN1 0x062c /* DP0 Audio Force N Value Register */ +#define DP0_AMNGENSTATUS 0x0630 /* DP0 Audio Current M Value Register */ /* Main Channel */ #define DP0_SECSAMPLE 0x0640 @@ -203,6 +241,10 @@ /* Link Training */ #define DP0_SRCCTRL 0x06a0 +#define DP0_SRCCTRL_PRE1 GENMASK(29, 28) +#define DP0_SRCCTRL_SWG1 GENMASK(25, 24) +#define DP0_SRCCTRL_PRE0 GENMASK(21, 20) +#define DP0_SRCCTRL_SWG0 GENMASK(17, 16) #define DP0_SRCCTRL_SCRMBLDIS BIT(13) #define DP0_SRCCTRL_EN810B BIT(12) #define DP0_SRCCTRL_NOTP (0 << 8) @@ -224,8 +266,24 @@ #define DP0_SNKLTCHGREQ 0x06d4 #define DP0_LTLOOPCTRL 0x06d8 #define DP0_SNKLTCTRL 0x06e4 - -#define DP1_SRCCTRL 0x07a0 +#define DP0_TPATDAT0 0x06e8 /* DP0 Test Pattern bits 29 to 0 */ +#define DP0_TPATDAT1 0x06ec /* DP0 Test Pattern bits 59 to 30 */ +#define DP0_TPATDAT2 0x06f0 /* DP0 Test Pattern bits 89 to 60 */ +#define DP0_TPATDAT3 0x06f4 /* DP0 Test Pattern bits 119 to 90 */ + +#define AUDCFG0 0x0700 /* DP0 Audio Config0 Register */ +#define AUDCFG1 0x0704 /* DP0 Audio Config1 Register */ +#define AUDIFDATA0 0x0708 /* DP0 Audio Info Frame Bytes 3 to 0 */ +#define AUDIFDATA1 0x070c /* DP0 Audio Info Frame Bytes 7 to 4 */ +#define AUDIFDATA2 0x0710 /* DP0 Audio Info Frame Bytes 11 to 8 */ +#define AUDIFDATA3 0x0714 /* DP0 Audio Info Frame Bytes 15 to 12 */ +#define AUDIFDATA4 0x0718 /* DP0 Audio Info Frame Bytes 19 to 16 */ +#define AUDIFDATA5 0x071c /* DP0 Audio Info Frame Bytes 23 to 20 */ +#define AUDIFDATA6 0x0720 /* DP0 Audio Info Frame Bytes 27 to 24 */ + +#define DP1_SRCCTRL 0x07a0 /* DP1 Control Register */ +#define DP1_SRCCTRL_PRE GENMASK(21, 20) +#define DP1_SRCCTRL_SWG GENMASK(17, 16) /* PHY */ #define DP_PHY_CTRL 0x0800 @@ -238,6 +296,25 @@ #define PHY_2LANE BIT(2) /* PHY Enable 2 lanes */ #define PHY_A0_EN BIT(1) /* PHY Aux Channel0 Enable */ #define PHY_M0_EN BIT(0) /* PHY Main Channel0 Enable */ +#define DP_PHY_CFG_WR 0x0810 /* DP PHY Configuration Test Write Register */ +#define DP_PHY_CFG_RD 0x0814 /* DP PHY Configuration Test Read Register */ +#define DP0_AUX_PHY_CTRL 0x0820 /* DP0 AUX PHY Control Register */ +#define DP0_MAIN_PHY_DBG 0x0840 /* DP0 Main PHY Test Debug Register */ + +/* I2S */ +#define I2SCFG 0x0880 /* I2S Audio Config 0 Register */ +#define I2SCH0STAT0 0x0888 /* I2S Audio Channel 0 Status Bytes 3 to 0 */ +#define I2SCH0STAT1 0x088c /* I2S Audio Channel 0 Status Bytes 7 to 4 */ +#define I2SCH0STAT2 0x0890 /* I2S Audio Channel 0 Status Bytes 11 to 8 */ +#define I2SCH0STAT3 0x0894 /* I2S Audio Channel 0 Status Bytes 15 to 12 */ +#define I2SCH0STAT4 0x0898 /* I2S Audio Channel 0 Status Bytes 19 to 16 */ +#define I2SCH0STAT5 0x089c /* I2S Audio Channel 0 Status Bytes 23 to 20 */ +#define I2SCH1STAT0 0x08a0 /* I2S Audio Channel 1 Status Bytes 3 to 0 */ +#define I2SCH1STAT1 0x08a4 /* I2S Audio Channel 1 Status Bytes 7 to 4 */ +#define I2SCH1STAT2 0x08a8 /* I2S Audio Channel 1 Status Bytes 11 to 8 */ +#define I2SCH1STAT3 0x08ac /* I2S Audio Channel 1 Status Bytes 15 to 12 */ +#define I2SCH1STAT4 0x08b0 /* I2S Audio Channel 1 Status Bytes 19 to 16 */ +#define I2SCH1STAT5 0x08b4 /* I2S Audio Channel 1 Status Bytes 23 to 20 */ /* PLL */ #define DP0_PLLCTRL 0x0900 @@ -267,6 +344,14 @@ #define COLOR_BAR_MODE_BARS 2 #define PLL_DBG 0x0a04 +enum tc_mode { + mode_dpi_to_edp = BIT(1) | BIT(2), + mode_dpi_to_dp = BIT(1), + mode_dsi_to_edp = BIT(0) | BIT(2), + mode_dsi_to_dp = BIT(0), + mode_dsi_to_dpi = BIT(0) | BIT(1), +}; + static bool tc_test_pattern; module_param_named(test, tc_test_pattern, bool, 0644); @@ -298,6 +383,7 @@ struct tc_data { u32 rev; u8 assr; + u8 pre_emphasis[2]; struct gpio_desc *sd_gpio; struct gpio_desc *reset_gpio; @@ -500,15 +586,15 @@ static int tc_pllupdate(struct tc_data *tc, unsigned int pllctrl) if (ret) return ret; - /* Wait for PLL to lock: up to 2.09 ms, depending on refclk */ - usleep_range(3000, 6000); + /* Wait for PLL to lock: up to 7.5 ms, depending on refclk */ + usleep_range(15000, 20000); return 0; } -static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) +static int tc_pxl_pll_calc(struct tc_data *tc, u32 refclk, u32 pixelclock, + int *out_best_pixelclock, u32 *out_pxl_pllparam) { - int ret; int i_pre, best_pre = 1; int i_post, best_post = 1; int div, best_div = 1; @@ -546,9 +632,14 @@ static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) continue; for (i_post = 0; i_post < ARRAY_SIZE(ext_div); i_post++) { for (div = 1; div <= 16; div++) { - u32 clk; + u32 clk, iclk; u64 tmp; + /* PCLK PLL input unit clock ... 6..40 MHz */ + iclk = refclk / (div * ext_div[i_pre]); + if (iclk < 6000000 || iclk > 40000000) + continue; + tmp = pixelclock * ext_div[i_pre] * ext_div[i_post] * div; do_div(tmp, refclk); @@ -582,8 +673,7 @@ static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) return -EINVAL; } - dev_dbg(tc->dev, "PLL: got %d, delta %d\n", best_pixelclock, - best_delta); + dev_dbg(tc->dev, "PLL: got %d, delta %d\n", best_pixelclock, best_delta); dev_dbg(tc->dev, "PLL: %d / %d / %d * %d / %d\n", refclk, ext_div[best_pre], best_div, best_mul, ext_div[best_post]); @@ -596,11 +686,6 @@ static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) if (best_mul == 128) best_mul = 0; - /* Power up PLL and switch to bypass */ - ret = regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP | PLLEN); - if (ret) - return ret; - pxl_pllparam = vco_hi << 24; /* For PLL VCO >= 300 MHz = 1 */ pxl_pllparam |= ext_div[best_pre] << 20; /* External Pre-divider */ pxl_pllparam |= ext_div[best_post] << 16; /* External Post-divider */ @@ -608,6 +693,29 @@ static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) pxl_pllparam |= best_div << 8; /* Divider for PLL RefClk */ pxl_pllparam |= best_mul; /* Multiplier for PLL */ + if (out_best_pixelclock) + *out_best_pixelclock = best_pixelclock; + + if (out_pxl_pllparam) + *out_pxl_pllparam = pxl_pllparam; + + return 0; +} + +static int tc_pxl_pll_en(struct tc_data *tc, u32 refclk, u32 pixelclock) +{ + u32 pxl_pllparam = 0; + int ret; + + ret = tc_pxl_pll_calc(tc, refclk, pixelclock, NULL, &pxl_pllparam); + if (ret) + return ret; + + /* Power up PLL and switch to bypass */ + ret = regmap_write(tc->regmap, PXL_PLLCTRL, PLLBYP | PLLEN); + if (ret) + return ret; + ret = regmap_write(tc->regmap, PXL_PLLPARAM, pxl_pllparam); if (ret) return ret; @@ -645,7 +753,7 @@ static int tc_stream_clock_calc(struct tc_data *tc) static int tc_set_syspllparam(struct tc_data *tc) { unsigned long rate; - u32 pllparam = SYSCLK_SEL_LSCLK | LSCLK_DIV_2; + u32 pllparam = SYSCLK_SEL_LSCLK | LSCLK_DIV_1; rate = clk_get_rate(tc->refclk); switch (rate) { @@ -809,7 +917,6 @@ static int tc_set_common_video_mode(struct tc_data *tc, upper_margin, lower_margin, vsync_len); dev_dbg(tc->dev, "total: %dx%d\n", mode->htotal, mode->vtotal); - /* * LCD Ctl Frame Size * datasheet is not clear of vsdelay in case of DPI @@ -817,8 +924,8 @@ static int tc_set_common_video_mode(struct tc_data *tc, * sync signals */ ret = regmap_write(tc->regmap, VPCTRL0, - FIELD_PREP(VSDELAY, 0) | - OPXLFMT_RGB888 | FRMSYNC_DISABLED | MSF_DISABLED); + FIELD_PREP(VSDELAY, right_margin + 10) | + OPXLFMT_RGB888 | FRMSYNC_ENABLED | MSF_DISABLED); if (ret) return ret; @@ -998,13 +1105,17 @@ static int tc_main_link_enable(struct tc_data *tc) return ret; } - ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc)); + ret = regmap_write(tc->regmap, DP0_SRCCTRL, + tc_srcctrl(tc) | + FIELD_PREP(DP0_SRCCTRL_PRE0, tc->pre_emphasis[0]) | + FIELD_PREP(DP0_SRCCTRL_PRE1, tc->pre_emphasis[1])); if (ret) return ret; /* SSCG and BW27 on DP1 must be set to the same as on DP0 */ ret = regmap_write(tc->regmap, DP1_SRCCTRL, (tc->link.spread ? DP0_SRCCTRL_SSCG : 0) | - ((tc->link.rate != 162000) ? DP0_SRCCTRL_BW27 : 0)); + ((tc->link.rate != 162000) ? DP0_SRCCTRL_BW27 : 0) | + FIELD_PREP(DP1_SRCCTRL_PRE, tc->pre_emphasis[1])); if (ret) return ret; @@ -1096,8 +1207,10 @@ static int tc_main_link_enable(struct tc_data *tc) goto err_dpcd_write; /* Reset voltage-swing & pre-emphasis */ - tmp[0] = tmp[1] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | - DP_TRAIN_PRE_EMPH_LEVEL_0; + tmp[0] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | + FIELD_PREP(DP_TRAIN_PRE_EMPHASIS_MASK, tc->pre_emphasis[0]); + tmp[1] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0 | + FIELD_PREP(DP_TRAIN_PRE_EMPHASIS_MASK, tc->pre_emphasis[1]); ret = drm_dp_dpcd_write(aux, DP_TRAINING_LANE0_SET, tmp, 2); if (ret < 0) goto err_dpcd_write; @@ -1121,7 +1234,9 @@ static int tc_main_link_enable(struct tc_data *tc) ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | DP0_SRCCTRL_AUTOCORRECT | - DP0_SRCCTRL_TP1); + DP0_SRCCTRL_TP1 | + FIELD_PREP(DP0_SRCCTRL_PRE0, tc->pre_emphasis[0]) | + FIELD_PREP(DP0_SRCCTRL_PRE1, tc->pre_emphasis[1])); if (ret) return ret; @@ -1156,7 +1271,9 @@ static int tc_main_link_enable(struct tc_data *tc) ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc) | DP0_SRCCTRL_SCRMBLDIS | DP0_SRCCTRL_AUTOCORRECT | - DP0_SRCCTRL_TP2); + DP0_SRCCTRL_TP2 | + FIELD_PREP(DP0_SRCCTRL_PRE0, tc->pre_emphasis[0]) | + FIELD_PREP(DP0_SRCCTRL_PRE1, tc->pre_emphasis[1])); if (ret) return ret; @@ -1182,7 +1299,9 @@ static int tc_main_link_enable(struct tc_data *tc) /* Clear Training Pattern, set AutoCorrect Mode = 1 */ ret = regmap_write(tc->regmap, DP0_SRCCTRL, tc_srcctrl(tc) | - DP0_SRCCTRL_AUTOCORRECT); + DP0_SRCCTRL_AUTOCORRECT | + FIELD_PREP(DP0_SRCCTRL_PRE0, tc->pre_emphasis[0]) | + FIELD_PREP(DP0_SRCCTRL_PRE1, tc->pre_emphasis[1])); if (ret) return ret; @@ -1437,9 +1556,8 @@ static int tc_edp_stream_disable(struct tc_data *tc) return 0; } -static void -tc_dpi_bridge_atomic_enable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) +static void tc_dpi_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct tc_data *tc = bridge_to_tc(bridge); @@ -1453,9 +1571,8 @@ tc_dpi_bridge_atomic_enable(struct drm_bridge *bridge, } } -static void -tc_dpi_bridge_atomic_disable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) +static void tc_dpi_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct tc_data *tc = bridge_to_tc(bridge); int ret; @@ -1465,9 +1582,8 @@ tc_dpi_bridge_atomic_disable(struct drm_bridge *bridge, dev_err(tc->dev, "main link stream stop error: %d\n", ret); } -static void -tc_edp_bridge_atomic_enable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) +static void tc_edp_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct tc_data *tc = bridge_to_tc(bridge); int ret; @@ -1492,9 +1608,8 @@ tc_edp_bridge_atomic_enable(struct drm_bridge *bridge, } } -static void -tc_edp_bridge_atomic_disable(struct drm_bridge *bridge, - struct drm_bridge_state *old_bridge_state) +static void tc_edp_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_atomic_state *state) { struct tc_data *tc = bridge_to_tc(bridge); int ret; @@ -1513,6 +1628,18 @@ static int tc_dpi_atomic_check(struct drm_bridge *bridge, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { + struct tc_data *tc = bridge_to_tc(bridge); + int adjusted_clock = 0; + int ret; + + ret = tc_pxl_pll_calc(tc, clk_get_rate(tc->refclk), + crtc_state->mode.clock * 1000, + &adjusted_clock, NULL); + if (ret) + return ret; + + crtc_state->adjusted_mode.clock = adjusted_clock / 1000; + /* DSI->DPI interface clock limitation: upto 100 MHz */ if (crtc_state->adjusted_mode.clock > 100000) return -EINVAL; @@ -1525,6 +1652,18 @@ static int tc_edp_atomic_check(struct drm_bridge *bridge, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { + struct tc_data *tc = bridge_to_tc(bridge); + int adjusted_clock = 0; + int ret; + + ret = tc_pxl_pll_calc(tc, clk_get_rate(tc->refclk), + crtc_state->mode.clock * 1000, + &adjusted_clock, NULL); + if (ret) + return ret; + + crtc_state->adjusted_mode.clock = adjusted_clock / 1000; + /* DPI->(e)DP interface clock limitation: upto 154 MHz */ if (crtc_state->adjusted_mode.clock > 154000) return -EINVAL; @@ -1553,7 +1692,7 @@ tc_edp_mode_valid(struct drm_bridge *bridge, u32 req, avail; u32 bits_per_pixel = 24; - /* DPI interface clock limitation: upto 154 MHz */ + /* DPI->(e)DP interface clock limitation: up to 154 MHz */ if (mode->clock > 154000) return MODE_CLOCK_HIGH; @@ -1572,22 +1711,29 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge, { struct tc_data *tc = bridge_to_tc(bridge); - drm_mode_copy(&tc->mode, mode); + drm_mode_copy(&tc->mode, adj); } -static struct edid *tc_get_edid(struct drm_bridge *bridge, - struct drm_connector *connector) +static const struct drm_edid *tc_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) { struct tc_data *tc = bridge_to_tc(bridge); + int ret; + + ret = tc_get_display_props(tc); + if (ret < 0) { + dev_err(tc->dev, "failed to read display props: %d\n", ret); + return 0; + } - return drm_get_edid(connector, &tc->aux.ddc); + return drm_edid_read_ddc(connector, &tc->aux.ddc); } static int tc_connector_get_modes(struct drm_connector *connector) { struct tc_data *tc = connector_to_tc(connector); int num_modes; - struct edid *edid; + const struct drm_edid *drm_edid; int ret; ret = tc_get_display_props(tc); @@ -1602,9 +1748,10 @@ static int tc_connector_get_modes(struct drm_connector *connector) return num_modes; } - edid = tc_get_edid(&tc->bridge, connector); - num_modes = drm_add_edid_modes(connector, edid); - kfree(edid); + drm_edid = tc_edid_read(&tc->bridge, connector); + drm_edid_connector_update(connector, drm_edid); + num_modes = drm_edid_connector_add_modes(connector); + drm_edid_free(drm_edid); return num_modes; } @@ -1613,7 +1760,8 @@ static const struct drm_connector_helper_funcs tc_connector_helper_funcs = { .get_modes = tc_connector_get_modes, }; -static enum drm_connector_status tc_bridge_detect(struct drm_bridge *bridge) +static enum drm_connector_status +tc_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) { struct tc_data *tc = bridge_to_tc(bridge); bool conn; @@ -1638,7 +1786,7 @@ tc_connector_detect(struct drm_connector *connector, bool force) struct tc_data *tc = connector_to_tc(connector); if (tc->hpd_pin >= 0) - return tc_bridge_detect(&tc->bridge); + return tc_bridge_detect(&tc->bridge, connector); if (tc->panel_bridge) return connector_status_connected; @@ -1656,6 +1804,7 @@ static const struct drm_connector_funcs tc_connector_funcs = { }; static int tc_dpi_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) { struct tc_data *tc = bridge_to_tc(bridge); @@ -1668,6 +1817,7 @@ static int tc_dpi_bridge_attach(struct drm_bridge *bridge, } static int tc_edp_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) { u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; @@ -1726,6 +1876,7 @@ static void tc_edp_bridge_detach(struct drm_bridge *bridge) } #define MAX_INPUT_SEL_FORMATS 1 +#define MAX_OUTPUT_SEL_FORMATS 1 static u32 * tc_dpi_atomic_get_input_bus_fmts(struct drm_bridge *bridge, @@ -1751,6 +1902,28 @@ tc_dpi_atomic_get_input_bus_fmts(struct drm_bridge *bridge, return input_fmts; } +static u32 * +tc_edp_atomic_get_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + u32 *output_fmts; + + *num_output_fmts = 0; + + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts), + GFP_KERNEL); + if (!output_fmts) + return NULL; + + output_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + *num_output_fmts = 1; + + return output_fmts; +} + static const struct drm_bridge_funcs tc_dpi_bridge_funcs = { .attach = tc_dpi_bridge_attach, .mode_valid = tc_dpi_mode_valid, @@ -1773,25 +1946,227 @@ static const struct drm_bridge_funcs tc_edp_bridge_funcs = { .atomic_enable = tc_edp_bridge_atomic_enable, .atomic_disable = tc_edp_bridge_atomic_disable, .detect = tc_bridge_detect, - .get_edid = tc_get_edid, + .edid_read = tc_edid_read, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, + .atomic_get_output_bus_fmts = tc_edp_atomic_get_output_bus_fmts, }; static bool tc_readable_reg(struct device *dev, unsigned int reg) { - return reg != SYSCTRL; + switch (reg) { + /* DSI D-PHY Layer */ + case 0x004: + case 0x020: + case 0x024: + case 0x028: + case 0x02c: + case 0x030: + case 0x038: + case 0x040: + case 0x044: + case 0x048: + case 0x04c: + case 0x050: + case 0x054: + /* DSI PPI Layer */ + case PPI_STARTPPI: + case 0x108: + case 0x110: + case PPI_LPTXTIMECNT: + case PPI_LANEENABLE: + case PPI_TX_RX_TA: + case 0x140: + case PPI_D0S_ATMR: + case PPI_D1S_ATMR: + case 0x14c: + case 0x150: + case PPI_D0S_CLRSIPOCOUNT: + case PPI_D1S_CLRSIPOCOUNT: + case PPI_D2S_CLRSIPOCOUNT: + case PPI_D3S_CLRSIPOCOUNT: + case 0x180: + case 0x184: + case 0x188: + case 0x18c: + case 0x190: + case 0x1a0: + case 0x1a4: + case 0x1a8: + case 0x1ac: + case 0x1b0: + case 0x1c0: + case 0x1c4: + case 0x1c8: + case 0x1cc: + case 0x1d0: + case 0x1e0: + case 0x1e4: + case 0x1f0: + case 0x1f4: + /* DSI Protocol Layer */ + case DSI_STARTDSI: + case DSI_BUSYDSI: + case DSI_LANEENABLE: + case DSI_LANESTATUS0: + case DSI_LANESTATUS1: + case DSI_INTSTATUS: + case 0x224: + case 0x228: + case 0x230: + /* DSI General */ + case DSIERRCNT: + /* DSI Application Layer */ + case 0x400: + case 0x404: + /* DPI */ + case DPIPXLFMT: + /* Parallel Output */ + case POCTRL: + /* Video Path0 Configuration */ + case VPCTRL0: + case HTIM01: + case HTIM02: + case VTIM01: + case VTIM02: + case VFUEN0: + /* System */ + case TC_IDREG: + case 0x504: + case SYSSTAT: + case SYSRSTENB: + case SYSCTRL: + /* I2C */ + case 0x520: + /* GPIO */ + case GPIOM: + case GPIOC: + case GPIOO: + case GPIOI: + /* Interrupt */ + case INTCTL_G: + case INTSTS_G: + case 0x570: + case 0x574: + case INT_GP0_LCNT: + case INT_GP1_LCNT: + /* DisplayPort Control */ + case DP0CTL: + /* DisplayPort Clock */ + case DP0_VIDMNGEN0: + case DP0_VIDMNGEN1: + case DP0_VMNGENSTATUS: + case 0x628: + case 0x62c: + case 0x630: + /* DisplayPort Main Channel */ + case DP0_SECSAMPLE: + case DP0_VIDSYNCDELAY: + case DP0_TOTALVAL: + case DP0_STARTVAL: + case DP0_ACTIVEVAL: + case DP0_SYNCVAL: + case DP0_MISC: + /* DisplayPort Aux Channel */ + case DP0_AUXCFG0: + case DP0_AUXCFG1: + case DP0_AUXADDR: + case 0x66c: + case 0x670: + case 0x674: + case 0x678: + case 0x67c: + case 0x680: + case 0x684: + case 0x688: + case DP0_AUXSTATUS: + case DP0_AUXI2CADR: + /* DisplayPort Link Training */ + case DP0_SRCCTRL: + case DP0_LTSTAT: + case DP0_SNKLTCHGREQ: + case DP0_LTLOOPCTRL: + case DP0_SNKLTCTRL: + case 0x6e8: + case 0x6ec: + case 0x6f0: + case 0x6f4: + /* DisplayPort Audio */ + case 0x700: + case 0x704: + case 0x708: + case 0x70c: + case 0x710: + case 0x714: + case 0x718: + case 0x71c: + case 0x720: + /* DisplayPort Source Control */ + case DP1_SRCCTRL: + /* DisplayPort PHY */ + case DP_PHY_CTRL: + case 0x810: + case 0x814: + case 0x820: + case 0x840: + /* I2S */ + case 0x880: + case 0x888: + case 0x88c: + case 0x890: + case 0x894: + case 0x898: + case 0x89c: + case 0x8a0: + case 0x8a4: + case 0x8a8: + case 0x8ac: + case 0x8b0: + case 0x8b4: + /* PLL */ + case DP0_PLLCTRL: + case DP1_PLLCTRL: + case PXL_PLLCTRL: + case PXL_PLLPARAM: + case SYS_PLLPARAM: + /* HDCP */ + case 0x980: + case 0x984: + case 0x988: + case 0x98c: + case 0x990: + case 0x994: + case 0x998: + case 0x99c: + case 0x9a0: + case 0x9a4: + case 0x9a8: + case 0x9ac: + /* Debug */ + case TSTCTL: + case PLL_DBG: + return true; + } + return false; } static const struct regmap_range tc_volatile_ranges[] = { + regmap_reg_range(PPI_BUSYPPI, PPI_BUSYPPI), + regmap_reg_range(DSI_BUSYDSI, DSI_BUSYDSI), + regmap_reg_range(DSI_LANESTATUS0, DSI_INTSTATUS), + regmap_reg_range(DSIERRCNT, DSIERRCNT), + regmap_reg_range(VFUEN0, VFUEN0), + regmap_reg_range(SYSSTAT, SYSSTAT), + regmap_reg_range(GPIOI, GPIOI), + regmap_reg_range(INTSTS_G, INTSTS_G), + regmap_reg_range(DP0_VMNGENSTATUS, DP0_VMNGENSTATUS), + regmap_reg_range(DP0_AMNGENSTATUS, DP0_AMNGENSTATUS), regmap_reg_range(DP0_AUXWDATA(0), DP0_AUXSTATUS), regmap_reg_range(DP0_LTSTAT, DP0_SNKLTCHGREQ), regmap_reg_range(DP_PHY_CTRL, DP_PHY_CTRL), regmap_reg_range(DP0_PLLCTRL, PXL_PLLCTRL), - regmap_reg_range(VFUEN0, VFUEN0), - regmap_reg_range(INTSTS_G, INTSTS_G), - regmap_reg_range(GPIOI, GPIOI), }; static const struct regmap_access_table tc_volatile_table = { @@ -1799,11 +2174,39 @@ static const struct regmap_access_table tc_volatile_table = { .n_yes_ranges = ARRAY_SIZE(tc_volatile_ranges), }; +static const struct regmap_range tc_precious_ranges[] = { + regmap_reg_range(SYSSTAT, SYSSTAT), +}; + +static const struct regmap_access_table tc_precious_table = { + .yes_ranges = tc_precious_ranges, + .n_yes_ranges = ARRAY_SIZE(tc_precious_ranges), +}; + static bool tc_writeable_reg(struct device *dev, unsigned int reg) { - return (reg != TC_IDREG) && - (reg != DP0_LTSTAT) && - (reg != DP0_SNKLTCHGREQ); + /* RO reg */ + switch (reg) { + case PPI_BUSYPPI: + case DSI_BUSYDSI: + case DSI_LANESTATUS0: + case DSI_LANESTATUS1: + case DSI_INTSTATUS: + case TC_IDREG: + case SYSBOOT: + case SYSSTAT: + case GPIOI: + case DP0_LTSTAT: + case DP0_SNKLTCHGREQ: + return false; + } + /* WO reg */ + switch (reg) { + case DSI_STARTDSI: + case DSI_INTCLR: + return true; + } + return tc_readable_reg(dev, reg); } static const struct regmap_config tc_regmap_config = { @@ -1812,10 +2215,11 @@ static const struct regmap_config tc_regmap_config = { .val_bits = 32, .reg_stride = 4, .max_register = PLL_DBG, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .readable_reg = tc_readable_reg, - .volatile_table = &tc_volatile_table, .writeable_reg = tc_writeable_reg, + .volatile_table = &tc_volatile_table, + .precious_table = &tc_precious_table, .reg_format_endian = REGMAP_ENDIAN_BIG, .val_format_endian = REGMAP_ENDIAN_LITTLE, }; @@ -1841,7 +2245,7 @@ static irqreturn_t tc_irq_handler(int irq, void *arg) dev_err(tc->dev, "syserr %x\n", stat); } - if (tc->hpd_pin >= 0 && tc->bridge.dev) { + if (tc->hpd_pin >= 0 && tc->bridge.dev && tc->aux.drm_dev) { /* * H is triggered when the GPIO goes high. * @@ -1851,11 +2255,11 @@ static irqreturn_t tc_irq_handler(int irq, void *arg) bool h = val & INT_GPIO_H(tc->hpd_pin); bool lc = val & INT_GPIO_LC(tc->hpd_pin); - dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin, - h ? "H" : "", lc ? "LC" : ""); - - if (h || lc) + if (h || lc) { + dev_dbg(tc->dev, "GPIO%d: %s %s\n", tc->hpd_pin, + h ? "H" : "", lc ? "LC" : ""); drm_kms_helper_hotplug_event(tc->bridge.dev); + } } regmap_write(tc->regmap, INTSTS_G, val); @@ -1890,18 +2294,18 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc) if (dsi_lanes < 0) return dsi_lanes; - dsi = mipi_dsi_device_register_full(host, &info); + dsi = devm_mipi_dsi_device_register_full(dev, host, &info); if (IS_ERR(dsi)) return dev_err_probe(dev, PTR_ERR(dsi), "failed to create dsi device\n"); tc->dsi = dsi; - dsi->lanes = dsi_lanes; dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; - ret = mipi_dsi_attach(dsi); + ret = devm_mipi_dsi_attach(dev, dsi); if (ret < 0) { dev_err(dev, "failed to attach dsi to host: %d\n", ret); return ret; @@ -1920,7 +2324,8 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc) /* port@1 is the DPI input/output port */ ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge); if (ret && ret != -ENODEV) - return ret; + return dev_err_probe(dev, ret, + "Could not find DPI panel or bridge\n"); if (panel) { bridge = devm_drm_panel_bridge_add(dev, panel); @@ -1931,7 +2336,6 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc) if (bridge) { tc->panel_bridge = bridge; tc->bridge.type = DRM_MODE_CONNECTOR_DPI; - tc->bridge.funcs = &tc_dpi_bridge_funcs; return 0; } @@ -1948,7 +2352,8 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) /* port@2 is the output port */ ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL); if (ret && ret != -ENODEV) - return ret; + return dev_err_probe(dev, ret, + "Could not find DSI panel or bridge\n"); if (panel) { struct drm_bridge *panel_bridge; @@ -1963,7 +2368,6 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; } - tc->bridge.funcs = &tc_edp_bridge_funcs; if (tc->hpd_pin >= 0) tc->bridge.ops |= DRM_BRIDGE_OP_DETECT; tc->bridge.ops |= DRM_BRIDGE_OP_EDID; @@ -1971,17 +2375,11 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) return 0; } -static int tc_probe_bridge_endpoint(struct tc_data *tc) +static enum tc_mode tc_probe_get_mode(struct device *dev) { - struct device *dev = tc->dev; struct of_endpoint endpoint; struct device_node *node = NULL; - const u8 mode_dpi_to_edp = BIT(1) | BIT(2); - const u8 mode_dpi_to_dp = BIT(1); - const u8 mode_dsi_to_edp = BIT(0) | BIT(2); - const u8 mode_dsi_to_dp = BIT(0); - const u8 mode_dsi_to_dpi = BIT(0) | BIT(1); - u8 mode = 0; + enum tc_mode mode = 0; /* * Determine bridge configuration. @@ -2006,6 +2404,40 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc) mode |= BIT(endpoint.port); } + if (mode != mode_dpi_to_edp && + mode != mode_dpi_to_dp && + mode != mode_dsi_to_dpi && + mode != mode_dsi_to_edp && + mode != mode_dsi_to_dp) { + dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode); + return -EINVAL; + } + + return mode; +} + +static int tc_probe_bridge_endpoint(struct tc_data *tc, enum tc_mode mode) +{ + struct device *dev = tc->dev; + struct of_endpoint endpoint; + struct device_node *node = NULL; + + for_each_endpoint_of_node(dev->of_node, node) { + of_graph_parse_endpoint(node, &endpoint); + if (endpoint.port == 2) { + of_property_read_u8_array(node, "toshiba,pre-emphasis", + tc->pre_emphasis, + ARRAY_SIZE(tc->pre_emphasis)); + + if (tc->pre_emphasis[0] < 0 || tc->pre_emphasis[0] > 2 || + tc->pre_emphasis[1] < 0 || tc->pre_emphasis[1] > 2) { + dev_err(dev, "Incorrect Pre-Emphasis setting, use either 0=0dB 1=3.5dB 2=6dB\n"); + of_node_put(node); + return -EINVAL; + } + } + } + if (mode == mode_dpi_to_edp || mode == mode_dpi_to_dp) { tc->input_connector_dsi = false; return tc_probe_edp_bridge_endpoint(tc); @@ -2017,48 +2449,35 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc) return tc_probe_edp_bridge_endpoint(tc); } - dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode); - + /* Should never happen, mode was validated by tc_probe_get_mode() */ return -EINVAL; } -static void tc_clk_disable(void *data) -{ - struct clk *refclk = data; - - clk_disable_unprepare(refclk); -} - -static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int tc_probe(struct i2c_client *client) { struct device *dev = &client->dev; + const struct drm_bridge_funcs *funcs; struct tc_data *tc; + int mode; int ret; - tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); - if (!tc) - return -ENOMEM; + mode = tc_probe_get_mode(dev); + funcs = (mode == mode_dsi_to_dpi) ? &tc_dpi_bridge_funcs : &tc_edp_bridge_funcs; - tc->dev = dev; - - ret = tc_probe_bridge_endpoint(tc); - if (ret) - return ret; + tc = devm_drm_bridge_alloc(dev, struct tc_data, bridge, funcs); + if (IS_ERR(tc)) + return PTR_ERR(tc); - tc->refclk = devm_clk_get(dev, "ref"); - if (IS_ERR(tc->refclk)) { - ret = PTR_ERR(tc->refclk); - dev_err(dev, "Failed to get refclk: %d\n", ret); - return ret; - } + tc->dev = dev; - ret = clk_prepare_enable(tc->refclk); + ret = tc_probe_bridge_endpoint(tc, mode); if (ret) return ret; - ret = devm_add_action_or_reset(dev, tc_clk_disable, tc->refclk); - if (ret) - return ret; + tc->refclk = devm_clk_get_enabled(dev, "ref"); + if (IS_ERR(tc->refclk)) + return dev_err_probe(dev, PTR_ERR(tc->refclk), + "Failed to get and enable the ref clk\n"); /* tRSTW = 100 cycles , at 13 MHz that is ~7.69 us */ usleep_range(10, 15); @@ -2097,7 +2516,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) } else { if (tc->hpd_pin < 0 || tc->hpd_pin > 1) { dev_err(dev, "failed to parse HPD number\n"); - return ret; + return -EINVAL; } } @@ -2177,7 +2596,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) ret = tc_mipi_dsi_host_attach(tc); if (ret) { drm_bridge_remove(&tc->bridge); - return ret; + return dev_err_probe(dev, ret, "Failed to attach DSI host\n"); } } @@ -2192,7 +2611,7 @@ static void tc_remove(struct i2c_client *client) } static const struct i2c_device_id tc358767_i2c_ids[] = { - { "tc358767", 0 }, + { "tc358767" }, { } }; MODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids); |
