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

drm/mcde: Enable the DSI link with display

The MCDE DSI link hardware which is modeled like a bridge
in DRM, connected further to the panel bridge, creating
a pipeline.

We have been using the .pre_enable(), .enable(),
.disable() and .post_disable() callbacks from the bridge
to set this up in a chained manner: first the display
controller goes online and then in successive order
each bridge in the pipeline. Inside DRM it works
like this:

drm_atomic_helper_commit_tail()
drm_atomic_helper_commit_modeset_enables()
struct drm_crtc_helper_funcs .atomic_enable()
struct drm_simple_display_pipe_funcs .enable()
MCDE display enable call
drm_atomic_bridge_chain_enable()
struct drm_bridge_funcs .pre_enable()
mcde_dsi_bridge_pre_enable()
panel_bridge_pre_enable()
struct drm_panel_funcs .prepare()
struct drm_bridge_funcs .enable()
mcde_dsi_bridge_enable()
panel_bridge_enable()
struct drm_panel_funcs .enable()

A similar sequence is executed for disabling.

Unfortunately this is not what the hardware needs: at
a certain stage in the enablement of the display
controller the DSI link needs to come up to support
video mode, else something (like a FIFO flow) locks
up the hardware and we never get picture.

Fix this by simply leaving the pre|enable and
post|disable callbacks unused, and establish two
cross-calls from the display controller to bring up
the DSI link at the right place in the display
bring-up sequence and vice versa in the shutdown
sequence.

For command mode displays, it works just fine to
also enable the display flow early. The only time
we hold it back right now is in one-shot mode,
on-demand display updates.

When combined with the previous patch and some patches
for the S6E63M0 display controller to support DSI
mode, this gives working display on the Samsung
GT-I8190 (Golden) phone. It has also been tested working
on the Samsung GT-S7710 (Skomer) phone.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: newbytee@protonmail.com
Cc: Stephan Gerhold <stephan@gerhold.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20200808223122.1492124-4-linus.walleij@linaro.org

+47 -35
+28 -8
drivers/gpu/drm/mcde/mcde_display.c
··· 999 999 mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0, 1000 1000 fifo_wtrmrk); 1001 1001 1002 + /* 1003 + * This brings up the DSI bridge which is tightly connected 1004 + * to the MCDE DSI formatter. 1005 + * 1006 + * FIXME: if we want to use another formatter, such as DPI, 1007 + * we need to be more elaborate here and select the appropriate 1008 + * bridge. 1009 + */ 1010 + mcde_dsi_enable(mcde->bridge); 1011 + 1002 1012 /* Configure the DSI formatter 0 for the DSI panel output */ 1003 1013 mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0, 1004 1014 formatter_frame, pkt_size); ··· 1035 1025 1036 1026 drm_crtc_vblank_on(crtc); 1037 1027 1038 - if (mcde_flow_is_video(mcde)) { 1039 - /* 1040 - * Keep FIFO permanently enabled in video mode, 1041 - * otherwise MCDE will stop feeding data to the panel. 1042 - */ 1028 + /* 1029 + * If we're using oneshot mode we don't start the flow 1030 + * until each time the display is given an update, and 1031 + * then we disable it immediately after. For all other 1032 + * modes (command or video) we start the FIFO flow 1033 + * right here. This is necessary for the hardware to 1034 + * behave right. 1035 + */ 1036 + if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) { 1043 1037 mcde_enable_fifo(mcde, MCDE_FIFO_A); 1044 1038 dev_dbg(mcde->dev, "started MCDE video FIFO flow\n"); 1045 1039 } 1046 1040 1047 - /* Enable automatic clock gating */ 1041 + /* Enable MCDE with automatic clock gating */ 1048 1042 val = readl(mcde->regs + MCDE_CR); 1049 1043 val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN; 1050 1044 writel(val, mcde->regs + MCDE_CR); ··· 1068 1054 1069 1055 /* Disable FIFO A flow */ 1070 1056 mcde_disable_fifo(mcde, MCDE_FIFO_A, true); 1057 + 1058 + /* This disables the DSI bridge */ 1059 + mcde_dsi_disable(mcde->bridge); 1071 1060 1072 1061 event = crtc->state->event; 1073 1062 if (event) { ··· 1181 1164 if (fb) { 1182 1165 mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0)); 1183 1166 dev_info_once(mcde->dev, "first update of display contents\n"); 1184 - /* The flow is already active in video mode */ 1185 - if (!mcde_flow_is_video(mcde) && mcde->flow_active == 0) 1167 + /* 1168 + * Usually the flow is already active, unless we are in 1169 + * oneshot mode, then we need to kick the flow right here. 1170 + */ 1171 + if (mcde->flow_active == 0) 1186 1172 mcde_start_flow(mcde); 1187 1173 } else { 1188 1174 /*
+2
drivers/gpu/drm/mcde/mcde_drm.h
··· 97 97 98 98 bool mcde_dsi_irq(struct mipi_dsi_device *mdsi); 99 99 void mcde_dsi_te_request(struct mipi_dsi_device *mdsi); 100 + void mcde_dsi_enable(struct drm_bridge *bridge); 101 + void mcde_dsi_disable(struct drm_bridge *bridge); 100 102 extern struct platform_driver mcde_dsi_driver; 101 103 102 104 void mcde_display_irq(struct mcde *mcde);
+17 -27
drivers/gpu/drm/mcde/mcde_dsi.c
··· 826 826 dev_info(d->dev, "DSI link enabled\n"); 827 827 } 828 828 829 - 830 - static void mcde_dsi_bridge_enable(struct drm_bridge *bridge) 831 - { 832 - struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 833 - u32 val; 834 - 835 - if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 836 - /* Enable video mode */ 837 - val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); 838 - val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN; 839 - writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); 840 - } 841 - 842 - dev_info(d->dev, "enable DSI master\n"); 843 - }; 844 - 845 - static void mcde_dsi_bridge_pre_enable(struct drm_bridge *bridge) 829 + /* 830 + * Notice that this is called from inside the display controller 831 + * and not from the bridge callbacks. 832 + */ 833 + void mcde_dsi_enable(struct drm_bridge *bridge) 846 834 { 847 835 struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 848 836 unsigned long hs_freq, lp_freq; ··· 908 920 val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC; 909 921 val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA; 910 922 writel(val, d->regs + DSI_VID_MODE_STS_CTL); 923 + 924 + /* Enable video mode */ 925 + val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); 926 + val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN; 927 + writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); 911 928 } else { 912 929 /* Command mode, clear IF1 ID */ 913 930 val = readl(d->regs + DSI_CMD_MODE_CTL); ··· 925 932 val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK; 926 933 writel(val, d->regs + DSI_CMD_MODE_CTL); 927 934 } 935 + 936 + dev_info(d->dev, "enabled MCDE DSI master\n"); 928 937 } 929 938 930 939 static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge, ··· 989 994 } 990 995 } 991 996 992 - static void mcde_dsi_bridge_disable(struct drm_bridge *bridge) 997 + /* 998 + * Notice that this is called from inside the display controller 999 + * and not from the bridge callbacks. 1000 + */ 1001 + void mcde_dsi_disable(struct drm_bridge *bridge) 993 1002 { 994 1003 struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 995 1004 u32 val; ··· 1008 1009 /* Stop command mode */ 1009 1010 mcde_dsi_wait_for_command_mode_stop(d); 1010 1011 } 1011 - } 1012 - 1013 - static void mcde_dsi_bridge_post_disable(struct drm_bridge *bridge) 1014 - { 1015 - struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 1016 1012 1017 1013 /* 1018 1014 * Stop clocks and terminate any DSI traffic here so the panel can ··· 1046 1052 static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = { 1047 1053 .attach = mcde_dsi_bridge_attach, 1048 1054 .mode_set = mcde_dsi_bridge_mode_set, 1049 - .disable = mcde_dsi_bridge_disable, 1050 - .enable = mcde_dsi_bridge_enable, 1051 - .pre_enable = mcde_dsi_bridge_pre_enable, 1052 - .post_disable = mcde_dsi_bridge_post_disable, 1053 1055 }; 1054 1056 1055 1057 static int mcde_dsi_bind(struct device *dev, struct device *master,