Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/bridge/synopsys: dsi: driver-specific configuration of phy timings

The timing values for dw-dsi are often dependent on the used display and
according to Philippe Cornu will most likely also depend on the used phy
technology in the soc-specific implementation.

To solve this and allow specific implementations to define them as needed
add a new get_timing callback to phy_ops and call this from the dphy_timing
function to retrieve the necessary values for the specific mode.

Right now this handles the hs2lp + lp2hs where Rockchip SoCs need handling
according to the phy speed, while STM seems to be ok with static values.

changes in v5:
- rebase on 5.5-rc1
- merge into px30 dsi series to prevent ordering conflicts

changes in v4:
- rebase to make it directly fit on top of drm-misc-next after all

changes in v3:
- check existence of phy_ops->get_timing in __dw_mipi_dsi_probe()
- emit actual error when get_timing() call fails
- add tags from Philippe and Yannick

changes in v2:
- add driver-specific handling, don't force all bridge users to use
the same timings, as suggested by Philippe

Suggested-by: Philippe Cornu <philippe.cornu@st.com>
Signed-off-by: Heiko Stuebner <heiko.stuebner@theobroma-systems.com>
Reviewed-by: Philippe Cornu <philippe.cornu@st.com>
Tested-by: Yannick Fertre <yannick.fertre@st.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20191209143130.4553-2-heiko@sntech.de

authored by

Heiko Stuebner and committed by
Heiko Stuebner
25ed8aeb 2156873f

+121 -7
+20 -7
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
··· 719 719 720 720 static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi) 721 721 { 722 + const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; 723 + struct dw_mipi_dsi_dphy_timing timing; 722 724 u32 hw_version; 725 + int ret; 726 + 727 + ret = phy_ops->get_timing(dsi->plat_data->priv_data, 728 + dsi->lane_mbps, &timing); 729 + if (ret) 730 + DRM_DEV_ERROR(dsi->dev, "Retrieving phy timings failed\n"); 723 731 724 732 /* 725 733 * TODO dw drv improvements ··· 740 732 hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; 741 733 742 734 if (hw_version >= HWVER_131) { 743 - dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME_V131(0x40) | 744 - PHY_LP2HS_TIME_V131(0x40)); 735 + dsi_write(dsi, DSI_PHY_TMR_CFG, 736 + PHY_HS2LP_TIME_V131(timing.data_hs2lp) | 737 + PHY_LP2HS_TIME_V131(timing.data_lp2hs)); 745 738 dsi_write(dsi, DSI_PHY_TMR_RD_CFG, MAX_RD_TIME_V131(10000)); 746 739 } else { 747 - dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40) | 748 - PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000)); 740 + dsi_write(dsi, DSI_PHY_TMR_CFG, 741 + PHY_HS2LP_TIME(timing.data_hs2lp) | 742 + PHY_LP2HS_TIME(timing.data_lp2hs) | 743 + MAX_RD_TIME(10000)); 749 744 } 750 745 751 - dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40) 752 - | PHY_CLKLP2HS_TIME(0x40)); 746 + dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, 747 + PHY_CLKHS2LP_TIME(timing.clk_hs2lp) | 748 + PHY_CLKLP2HS_TIME(timing.clk_lp2hs)); 753 749 } 754 750 755 751 static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi) ··· 1003 991 dsi->dev = dev; 1004 992 dsi->plat_data = plat_data; 1005 993 1006 - if (!plat_data->phy_ops->init || !plat_data->phy_ops->get_lane_mbps) { 994 + if (!plat_data->phy_ops->init || !plat_data->phy_ops->get_lane_mbps || 995 + !plat_data->phy_ops->get_timing) { 1007 996 DRM_ERROR("Phy not properly configured\n"); 1008 997 return ERR_PTR(-ENODEV); 1009 998 }
+1
drivers/gpu/drm/rockchip/Kconfig
··· 46 46 47 47 config ROCKCHIP_DW_MIPI_DSI 48 48 bool "Rockchip specific extensions for Synopsys DW MIPI DSI" 49 + select GENERIC_PHY_MIPI_DPHY 49 50 help 50 51 This selects support for Rockchip SoC specific extensions 51 52 for the Synopsys DesignWare HDMI driver. If you want to
+78
drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
··· 559 559 return 0; 560 560 } 561 561 562 + struct hstt { 563 + unsigned int maxfreq; 564 + struct dw_mipi_dsi_dphy_timing timing; 565 + }; 566 + 567 + #define HSTT(_maxfreq, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \ 568 + { \ 569 + .maxfreq = _maxfreq, \ 570 + .timing = { \ 571 + .clk_lp2hs = _c_lp2hs, \ 572 + .clk_hs2lp = _c_hs2lp, \ 573 + .data_lp2hs = _d_lp2hs, \ 574 + .data_hs2lp = _d_hs2lp, \ 575 + } \ 576 + } 577 + 578 + /* Table A-3 High-Speed Transition Times */ 579 + struct hstt hstt_table[] = { 580 + HSTT( 90, 32, 20, 26, 13), 581 + HSTT( 100, 35, 23, 28, 14), 582 + HSTT( 110, 32, 22, 26, 13), 583 + HSTT( 130, 31, 20, 27, 13), 584 + HSTT( 140, 33, 22, 26, 14), 585 + HSTT( 150, 33, 21, 26, 14), 586 + HSTT( 170, 32, 20, 27, 13), 587 + HSTT( 180, 36, 23, 30, 15), 588 + HSTT( 200, 40, 22, 33, 15), 589 + HSTT( 220, 40, 22, 33, 15), 590 + HSTT( 240, 44, 24, 36, 16), 591 + HSTT( 250, 48, 24, 38, 17), 592 + HSTT( 270, 48, 24, 38, 17), 593 + HSTT( 300, 50, 27, 41, 18), 594 + HSTT( 330, 56, 28, 45, 18), 595 + HSTT( 360, 59, 28, 48, 19), 596 + HSTT( 400, 61, 30, 50, 20), 597 + HSTT( 450, 67, 31, 55, 21), 598 + HSTT( 500, 73, 31, 59, 22), 599 + HSTT( 550, 79, 36, 63, 24), 600 + HSTT( 600, 83, 37, 68, 25), 601 + HSTT( 650, 90, 38, 73, 27), 602 + HSTT( 700, 95, 40, 77, 28), 603 + HSTT( 750, 102, 40, 84, 28), 604 + HSTT( 800, 106, 42, 87, 30), 605 + HSTT( 850, 113, 44, 93, 31), 606 + HSTT( 900, 118, 47, 98, 32), 607 + HSTT( 950, 124, 47, 102, 34), 608 + HSTT(1000, 130, 49, 107, 35), 609 + HSTT(1050, 135, 51, 111, 37), 610 + HSTT(1100, 139, 51, 114, 38), 611 + HSTT(1150, 146, 54, 120, 40), 612 + HSTT(1200, 153, 57, 125, 41), 613 + HSTT(1250, 158, 58, 130, 42), 614 + HSTT(1300, 163, 58, 135, 44), 615 + HSTT(1350, 168, 60, 140, 45), 616 + HSTT(1400, 172, 64, 144, 47), 617 + HSTT(1450, 176, 65, 148, 48), 618 + HSTT(1500, 181, 66, 153, 50) 619 + }; 620 + 621 + static int 622 + dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, 623 + struct dw_mipi_dsi_dphy_timing *timing) 624 + { 625 + int i; 626 + 627 + for (i = 0; i < ARRAY_SIZE(hstt_table); i++) 628 + if (lane_mbps < hstt_table[i].maxfreq) 629 + break; 630 + 631 + if (i == ARRAY_SIZE(hstt_table)) 632 + i--; 633 + 634 + *timing = hstt_table[i].timing; 635 + 636 + return 0; 637 + } 638 + 562 639 static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_rockchip_phy_ops = { 563 640 .init = dw_mipi_dsi_phy_init, 564 641 .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, 642 + .get_timing = dw_mipi_dsi_phy_get_timing, 565 643 }; 566 644 567 645 static void dw_mipi_dsi_rockchip_config(struct dw_mipi_dsi_rockchip *dsi,
+13
drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
··· 309 309 return 0; 310 310 } 311 311 312 + static int 313 + dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, 314 + struct dw_mipi_dsi_dphy_timing *timing) 315 + { 316 + timing->clk_hs2lp = 0x40; 317 + timing->clk_lp2hs = 0x40; 318 + timing->data_hs2lp = 0x40; 319 + timing->data_lp2hs = 0x40; 320 + 321 + return 0; 322 + } 323 + 312 324 static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = { 313 325 .init = dw_mipi_dsi_phy_init, 314 326 .power_on = dw_mipi_dsi_phy_power_on, 315 327 .power_off = dw_mipi_dsi_phy_power_off, 316 328 .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, 329 + .get_timing = dw_mipi_dsi_phy_get_timing, 317 330 }; 318 331 319 332 static struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = {
+9
include/drm/bridge/dw_mipi_dsi.h
··· 19 19 struct mipi_dsi_device; 20 20 struct platform_device; 21 21 22 + struct dw_mipi_dsi_dphy_timing { 23 + u16 data_hs2lp; 24 + u16 data_lp2hs; 25 + u16 clk_hs2lp; 26 + u16 clk_lp2hs; 27 + }; 28 + 22 29 struct dw_mipi_dsi_phy_ops { 23 30 int (*init)(void *priv_data); 24 31 void (*power_on)(void *priv_data); ··· 34 27 const struct drm_display_mode *mode, 35 28 unsigned long mode_flags, u32 lanes, u32 format, 36 29 unsigned int *lane_mbps); 30 + int (*get_timing)(void *priv_data, unsigned int lane_mbps, 31 + struct dw_mipi_dsi_dphy_timing *timing); 37 32 }; 38 33 39 34 struct dw_mipi_dsi_host_ops {