15fc537bfSLinus Walleij // SPDX-License-Identifier: GPL-2.0 25fc537bfSLinus Walleij /* 35fc537bfSLinus Walleij * Copyright (C) 2018 Linus Walleij <linus.walleij@linaro.org> 45fc537bfSLinus Walleij * Parts of this file were based on the MCDE driver by Marcus Lorentzon 55fc537bfSLinus Walleij * (C) ST-Ericsson SA 2013 65fc537bfSLinus Walleij */ 75fc537bfSLinus Walleij #include <linux/clk.h> 85fc537bfSLinus Walleij #include <linux/delay.h> 95fc537bfSLinus Walleij #include <linux/dma-buf.h> 10c4842d4dSLinus Walleij #include <linux/regulator/consumer.h> 115fc537bfSLinus Walleij 125fc537bfSLinus Walleij #include <drm/drm_device.h> 135fc537bfSLinus Walleij #include <drm/drm_fb_cma_helper.h> 145fc537bfSLinus Walleij #include <drm/drm_fourcc.h> 155fc537bfSLinus Walleij #include <drm/drm_gem_cma_helper.h> 165fc537bfSLinus Walleij #include <drm/drm_gem_framebuffer_helper.h> 175fc537bfSLinus Walleij #include <drm/drm_mipi_dsi.h> 185fc537bfSLinus Walleij #include <drm/drm_simple_kms_helper.h> 195fc537bfSLinus Walleij #include <drm/drm_vblank.h> 205fc537bfSLinus Walleij #include <video/mipi_display.h> 215fc537bfSLinus Walleij 225fc537bfSLinus Walleij #include "mcde_drm.h" 235fc537bfSLinus Walleij #include "mcde_display_regs.h" 245fc537bfSLinus Walleij 255fc537bfSLinus Walleij enum mcde_fifo { 265fc537bfSLinus Walleij MCDE_FIFO_A, 275fc537bfSLinus Walleij MCDE_FIFO_B, 285fc537bfSLinus Walleij /* TODO: implement FIFO C0 and FIFO C1 */ 295fc537bfSLinus Walleij }; 305fc537bfSLinus Walleij 315fc537bfSLinus Walleij enum mcde_channel { 325fc537bfSLinus Walleij MCDE_CHANNEL_0 = 0, 335fc537bfSLinus Walleij MCDE_CHANNEL_1, 345fc537bfSLinus Walleij MCDE_CHANNEL_2, 355fc537bfSLinus Walleij MCDE_CHANNEL_3, 365fc537bfSLinus Walleij }; 375fc537bfSLinus Walleij 385fc537bfSLinus Walleij enum mcde_extsrc { 395fc537bfSLinus Walleij MCDE_EXTSRC_0 = 0, 405fc537bfSLinus Walleij MCDE_EXTSRC_1, 415fc537bfSLinus Walleij MCDE_EXTSRC_2, 425fc537bfSLinus Walleij MCDE_EXTSRC_3, 435fc537bfSLinus Walleij MCDE_EXTSRC_4, 445fc537bfSLinus Walleij MCDE_EXTSRC_5, 455fc537bfSLinus Walleij MCDE_EXTSRC_6, 465fc537bfSLinus Walleij MCDE_EXTSRC_7, 475fc537bfSLinus Walleij MCDE_EXTSRC_8, 485fc537bfSLinus Walleij MCDE_EXTSRC_9, 495fc537bfSLinus Walleij }; 505fc537bfSLinus Walleij 515fc537bfSLinus Walleij enum mcde_overlay { 525fc537bfSLinus Walleij MCDE_OVERLAY_0 = 0, 535fc537bfSLinus Walleij MCDE_OVERLAY_1, 545fc537bfSLinus Walleij MCDE_OVERLAY_2, 555fc537bfSLinus Walleij MCDE_OVERLAY_3, 565fc537bfSLinus Walleij MCDE_OVERLAY_4, 575fc537bfSLinus Walleij MCDE_OVERLAY_5, 585fc537bfSLinus Walleij }; 595fc537bfSLinus Walleij 605fc537bfSLinus Walleij enum mcde_dsi_formatter { 615fc537bfSLinus Walleij MCDE_DSI_FORMATTER_0 = 0, 625fc537bfSLinus Walleij MCDE_DSI_FORMATTER_1, 635fc537bfSLinus Walleij MCDE_DSI_FORMATTER_2, 645fc537bfSLinus Walleij }; 655fc537bfSLinus Walleij 665fc537bfSLinus Walleij void mcde_display_irq(struct mcde *mcde) 675fc537bfSLinus Walleij { 685fc537bfSLinus Walleij u32 mispp, misovl, mischnl; 69bb5ce9a0SDan Carpenter bool vblank = false; 705fc537bfSLinus Walleij 715fc537bfSLinus Walleij /* Handle display IRQs */ 725fc537bfSLinus Walleij mispp = readl(mcde->regs + MCDE_MISPP); 735fc537bfSLinus Walleij misovl = readl(mcde->regs + MCDE_MISOVL); 745fc537bfSLinus Walleij mischnl = readl(mcde->regs + MCDE_MISCHNL); 755fc537bfSLinus Walleij 765fc537bfSLinus Walleij /* 775fc537bfSLinus Walleij * Handle IRQs from the DSI link. All IRQs from the DSI links 785fc537bfSLinus Walleij * are just latched onto the MCDE IRQ line, so we need to traverse 795fc537bfSLinus Walleij * any active DSI masters and check if an IRQ is originating from 805fc537bfSLinus Walleij * them. 815fc537bfSLinus Walleij * 825fc537bfSLinus Walleij * TODO: Currently only one DSI link is supported. 835fc537bfSLinus Walleij */ 845fc537bfSLinus Walleij if (mcde_dsi_irq(mcde->mdsi)) { 855fc537bfSLinus Walleij u32 val; 865fc537bfSLinus Walleij 875fc537bfSLinus Walleij /* 885fc537bfSLinus Walleij * In oneshot mode we do not send continuous updates 895fc537bfSLinus Walleij * to the display, instead we only push out updates when 905fc537bfSLinus Walleij * the update function is called, then we disable the 915fc537bfSLinus Walleij * flow on the channel once we get the TE IRQ. 925fc537bfSLinus Walleij */ 93709c2773SLinus Walleij if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) { 945fc537bfSLinus Walleij spin_lock(&mcde->flow_lock); 955fc537bfSLinus Walleij if (--mcde->flow_active == 0) { 965fc537bfSLinus Walleij dev_dbg(mcde->dev, "TE0 IRQ\n"); 975fc537bfSLinus Walleij /* Disable FIFO A flow */ 985fc537bfSLinus Walleij val = readl(mcde->regs + MCDE_CRA0); 995fc537bfSLinus Walleij val &= ~MCDE_CRX0_FLOEN; 1005fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_CRA0); 1015fc537bfSLinus Walleij } 1025fc537bfSLinus Walleij spin_unlock(&mcde->flow_lock); 1035fc537bfSLinus Walleij } 1045fc537bfSLinus Walleij } 1055fc537bfSLinus Walleij 1065fc537bfSLinus Walleij /* Vblank from one of the channels */ 1075fc537bfSLinus Walleij if (mispp & MCDE_PP_VCMPA) { 1085fc537bfSLinus Walleij dev_dbg(mcde->dev, "chnl A vblank IRQ\n"); 1095fc537bfSLinus Walleij vblank = true; 1105fc537bfSLinus Walleij } 1115fc537bfSLinus Walleij if (mispp & MCDE_PP_VCMPB) { 1125fc537bfSLinus Walleij dev_dbg(mcde->dev, "chnl B vblank IRQ\n"); 1135fc537bfSLinus Walleij vblank = true; 1145fc537bfSLinus Walleij } 1155fc537bfSLinus Walleij if (mispp & MCDE_PP_VCMPC0) 1165fc537bfSLinus Walleij dev_dbg(mcde->dev, "chnl C0 vblank IRQ\n"); 1175fc537bfSLinus Walleij if (mispp & MCDE_PP_VCMPC1) 1185fc537bfSLinus Walleij dev_dbg(mcde->dev, "chnl C1 vblank IRQ\n"); 1195fc537bfSLinus Walleij if (mispp & MCDE_PP_VSCC0) 1205fc537bfSLinus Walleij dev_dbg(mcde->dev, "chnl C0 TE IRQ\n"); 1215fc537bfSLinus Walleij if (mispp & MCDE_PP_VSCC1) 1225fc537bfSLinus Walleij dev_dbg(mcde->dev, "chnl C1 TE IRQ\n"); 1235fc537bfSLinus Walleij writel(mispp, mcde->regs + MCDE_RISPP); 1245fc537bfSLinus Walleij 1255fc537bfSLinus Walleij if (vblank) 1265fc537bfSLinus Walleij drm_crtc_handle_vblank(&mcde->pipe.crtc); 1275fc537bfSLinus Walleij 1285fc537bfSLinus Walleij if (misovl) 1295fc537bfSLinus Walleij dev_info(mcde->dev, "some stray overlay IRQ %08x\n", misovl); 1305fc537bfSLinus Walleij writel(misovl, mcde->regs + MCDE_RISOVL); 1315fc537bfSLinus Walleij 1325fc537bfSLinus Walleij if (mischnl) 1335fc537bfSLinus Walleij dev_info(mcde->dev, "some stray channel error IRQ %08x\n", 1345fc537bfSLinus Walleij mischnl); 1355fc537bfSLinus Walleij writel(mischnl, mcde->regs + MCDE_RISCHNL); 1365fc537bfSLinus Walleij } 1375fc537bfSLinus Walleij 1385fc537bfSLinus Walleij void mcde_display_disable_irqs(struct mcde *mcde) 1395fc537bfSLinus Walleij { 1405fc537bfSLinus Walleij /* Disable all IRQs */ 1415fc537bfSLinus Walleij writel(0, mcde->regs + MCDE_IMSCPP); 1425fc537bfSLinus Walleij writel(0, mcde->regs + MCDE_IMSCOVL); 1435fc537bfSLinus Walleij writel(0, mcde->regs + MCDE_IMSCCHNL); 1445fc537bfSLinus Walleij 1455fc537bfSLinus Walleij /* Clear any pending IRQs */ 1465fc537bfSLinus Walleij writel(0xFFFFFFFF, mcde->regs + MCDE_RISPP); 1475fc537bfSLinus Walleij writel(0xFFFFFFFF, mcde->regs + MCDE_RISOVL); 1485fc537bfSLinus Walleij writel(0xFFFFFFFF, mcde->regs + MCDE_RISCHNL); 1495fc537bfSLinus Walleij } 1505fc537bfSLinus Walleij 1515fc537bfSLinus Walleij static int mcde_display_check(struct drm_simple_display_pipe *pipe, 1525fc537bfSLinus Walleij struct drm_plane_state *pstate, 1535fc537bfSLinus Walleij struct drm_crtc_state *cstate) 1545fc537bfSLinus Walleij { 1555fc537bfSLinus Walleij const struct drm_display_mode *mode = &cstate->mode; 1565fc537bfSLinus Walleij struct drm_framebuffer *old_fb = pipe->plane.state->fb; 1575fc537bfSLinus Walleij struct drm_framebuffer *fb = pstate->fb; 1585fc537bfSLinus Walleij 1595fc537bfSLinus Walleij if (fb) { 1605fc537bfSLinus Walleij u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0); 1615fc537bfSLinus Walleij 1625fc537bfSLinus Walleij /* FB base address must be dword aligned. */ 1635fc537bfSLinus Walleij if (offset & 3) { 1645fc537bfSLinus Walleij DRM_DEBUG_KMS("FB not 32-bit aligned\n"); 1655fc537bfSLinus Walleij return -EINVAL; 1665fc537bfSLinus Walleij } 1675fc537bfSLinus Walleij 1685fc537bfSLinus Walleij /* 1695fc537bfSLinus Walleij * There's no pitch register, the mode's hdisplay 1705fc537bfSLinus Walleij * controls this. 1715fc537bfSLinus Walleij */ 1725fc537bfSLinus Walleij if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) { 1735fc537bfSLinus Walleij DRM_DEBUG_KMS("can't handle pitches\n"); 1745fc537bfSLinus Walleij return -EINVAL; 1755fc537bfSLinus Walleij } 1765fc537bfSLinus Walleij 1775fc537bfSLinus Walleij /* 1785fc537bfSLinus Walleij * We can't change the FB format in a flicker-free 1795fc537bfSLinus Walleij * manner (and only update it during CRTC enable). 1805fc537bfSLinus Walleij */ 1815fc537bfSLinus Walleij if (old_fb && old_fb->format != fb->format) 1825fc537bfSLinus Walleij cstate->mode_changed = true; 1835fc537bfSLinus Walleij } 1845fc537bfSLinus Walleij 1855fc537bfSLinus Walleij return 0; 1865fc537bfSLinus Walleij } 1875fc537bfSLinus Walleij 1885fc537bfSLinus Walleij static int mcde_configure_extsrc(struct mcde *mcde, enum mcde_extsrc src, 1895fc537bfSLinus Walleij u32 format) 1905fc537bfSLinus Walleij { 1915fc537bfSLinus Walleij u32 val; 1925fc537bfSLinus Walleij u32 conf; 1935fc537bfSLinus Walleij u32 cr; 1945fc537bfSLinus Walleij 1955fc537bfSLinus Walleij switch (src) { 1965fc537bfSLinus Walleij case MCDE_EXTSRC_0: 1975fc537bfSLinus Walleij conf = MCDE_EXTSRC0CONF; 1985fc537bfSLinus Walleij cr = MCDE_EXTSRC0CR; 1995fc537bfSLinus Walleij break; 2005fc537bfSLinus Walleij case MCDE_EXTSRC_1: 2015fc537bfSLinus Walleij conf = MCDE_EXTSRC1CONF; 2025fc537bfSLinus Walleij cr = MCDE_EXTSRC1CR; 2035fc537bfSLinus Walleij break; 2045fc537bfSLinus Walleij case MCDE_EXTSRC_2: 2055fc537bfSLinus Walleij conf = MCDE_EXTSRC2CONF; 2065fc537bfSLinus Walleij cr = MCDE_EXTSRC2CR; 2075fc537bfSLinus Walleij break; 2085fc537bfSLinus Walleij case MCDE_EXTSRC_3: 2095fc537bfSLinus Walleij conf = MCDE_EXTSRC3CONF; 2105fc537bfSLinus Walleij cr = MCDE_EXTSRC3CR; 2115fc537bfSLinus Walleij break; 2125fc537bfSLinus Walleij case MCDE_EXTSRC_4: 2135fc537bfSLinus Walleij conf = MCDE_EXTSRC4CONF; 2145fc537bfSLinus Walleij cr = MCDE_EXTSRC4CR; 2155fc537bfSLinus Walleij break; 2165fc537bfSLinus Walleij case MCDE_EXTSRC_5: 2175fc537bfSLinus Walleij conf = MCDE_EXTSRC5CONF; 2185fc537bfSLinus Walleij cr = MCDE_EXTSRC5CR; 2195fc537bfSLinus Walleij break; 2205fc537bfSLinus Walleij case MCDE_EXTSRC_6: 2215fc537bfSLinus Walleij conf = MCDE_EXTSRC6CONF; 2225fc537bfSLinus Walleij cr = MCDE_EXTSRC6CR; 2235fc537bfSLinus Walleij break; 2245fc537bfSLinus Walleij case MCDE_EXTSRC_7: 2255fc537bfSLinus Walleij conf = MCDE_EXTSRC7CONF; 2265fc537bfSLinus Walleij cr = MCDE_EXTSRC7CR; 2275fc537bfSLinus Walleij break; 2285fc537bfSLinus Walleij case MCDE_EXTSRC_8: 2295fc537bfSLinus Walleij conf = MCDE_EXTSRC8CONF; 2305fc537bfSLinus Walleij cr = MCDE_EXTSRC8CR; 2315fc537bfSLinus Walleij break; 2325fc537bfSLinus Walleij case MCDE_EXTSRC_9: 2335fc537bfSLinus Walleij conf = MCDE_EXTSRC9CONF; 2345fc537bfSLinus Walleij cr = MCDE_EXTSRC9CR; 2355fc537bfSLinus Walleij break; 2365fc537bfSLinus Walleij } 2375fc537bfSLinus Walleij 2385fc537bfSLinus Walleij /* 2395fc537bfSLinus Walleij * Configure external source 0 one buffer (buffer 0) 2405fc537bfSLinus Walleij * primary overlay ID 0. 2415fc537bfSLinus Walleij * From mcde_hw.c ovly_update_registers() in the vendor tree 2425fc537bfSLinus Walleij */ 2435fc537bfSLinus Walleij val = 0 << MCDE_EXTSRCXCONF_BUF_ID_SHIFT; 2445fc537bfSLinus Walleij val |= 1 << MCDE_EXTSRCXCONF_BUF_NB_SHIFT; 2455fc537bfSLinus Walleij val |= 0 << MCDE_EXTSRCXCONF_PRI_OVLID_SHIFT; 246*77f512bdSLinus Walleij 2475fc537bfSLinus Walleij switch (format) { 2485fc537bfSLinus Walleij case DRM_FORMAT_ARGB8888: 2495fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 << 2505fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 2515fc537bfSLinus Walleij break; 2525fc537bfSLinus Walleij case DRM_FORMAT_ABGR8888: 2535fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 << 2545fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 255*77f512bdSLinus Walleij val |= MCDE_EXTSRCXCONF_BGR; 2565fc537bfSLinus Walleij break; 2575fc537bfSLinus Walleij case DRM_FORMAT_XRGB8888: 2585fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 << 2595fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 2605fc537bfSLinus Walleij break; 2615fc537bfSLinus Walleij case DRM_FORMAT_XBGR8888: 2625fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 << 2635fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 264*77f512bdSLinus Walleij val |= MCDE_EXTSRCXCONF_BGR; 2655fc537bfSLinus Walleij break; 2665fc537bfSLinus Walleij case DRM_FORMAT_RGB888: 2675fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_RGB888 << 2685fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 2695fc537bfSLinus Walleij break; 2705fc537bfSLinus Walleij case DRM_FORMAT_BGR888: 2715fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_RGB888 << 2725fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 273*77f512bdSLinus Walleij val |= MCDE_EXTSRCXCONF_BGR; 2745fc537bfSLinus Walleij break; 2755fc537bfSLinus Walleij case DRM_FORMAT_ARGB4444: 2765fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 << 2775fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 2785fc537bfSLinus Walleij break; 2795fc537bfSLinus Walleij case DRM_FORMAT_ABGR4444: 2805fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 << 2815fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 282*77f512bdSLinus Walleij val |= MCDE_EXTSRCXCONF_BGR; 2835fc537bfSLinus Walleij break; 2845fc537bfSLinus Walleij case DRM_FORMAT_XRGB4444: 2855fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_RGB444 << 2865fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 2875fc537bfSLinus Walleij break; 2885fc537bfSLinus Walleij case DRM_FORMAT_XBGR4444: 2895fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_RGB444 << 2905fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 291*77f512bdSLinus Walleij val |= MCDE_EXTSRCXCONF_BGR; 2925fc537bfSLinus Walleij break; 2935fc537bfSLinus Walleij case DRM_FORMAT_XRGB1555: 2945fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 << 2955fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 2965fc537bfSLinus Walleij break; 2975fc537bfSLinus Walleij case DRM_FORMAT_XBGR1555: 2985fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 << 2995fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 300*77f512bdSLinus Walleij val |= MCDE_EXTSRCXCONF_BGR; 3015fc537bfSLinus Walleij break; 3025fc537bfSLinus Walleij case DRM_FORMAT_RGB565: 3035fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_RGB565 << 3045fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 3055fc537bfSLinus Walleij break; 3065fc537bfSLinus Walleij case DRM_FORMAT_BGR565: 3075fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_RGB565 << 3085fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 309*77f512bdSLinus Walleij val |= MCDE_EXTSRCXCONF_BGR; 3105fc537bfSLinus Walleij break; 3115fc537bfSLinus Walleij case DRM_FORMAT_YUV422: 3125fc537bfSLinus Walleij val |= MCDE_EXTSRCXCONF_BPP_YCBCR422 << 3135fc537bfSLinus Walleij MCDE_EXTSRCXCONF_BPP_SHIFT; 3145fc537bfSLinus Walleij break; 3155fc537bfSLinus Walleij default: 3165fc537bfSLinus Walleij dev_err(mcde->dev, "Unknown pixel format 0x%08x\n", 3175fc537bfSLinus Walleij format); 3185fc537bfSLinus Walleij return -EINVAL; 3195fc537bfSLinus Walleij } 3205fc537bfSLinus Walleij writel(val, mcde->regs + conf); 3215fc537bfSLinus Walleij 3225fc537bfSLinus Walleij /* Software select, primary */ 3235fc537bfSLinus Walleij val = MCDE_EXTSRCXCR_SEL_MOD_SOFTWARE_SEL; 3245fc537bfSLinus Walleij val |= MCDE_EXTSRCXCR_MULTIOVL_CTRL_PRIMARY; 3255fc537bfSLinus Walleij writel(val, mcde->regs + cr); 3265fc537bfSLinus Walleij 3275fc537bfSLinus Walleij return 0; 3285fc537bfSLinus Walleij } 3295fc537bfSLinus Walleij 3305fc537bfSLinus Walleij static void mcde_configure_overlay(struct mcde *mcde, enum mcde_overlay ovl, 3315fc537bfSLinus Walleij enum mcde_extsrc src, 3325fc537bfSLinus Walleij enum mcde_channel ch, 3335fc537bfSLinus Walleij const struct drm_display_mode *mode, 33444c3867aSLinus Walleij u32 format, int cpp) 3355fc537bfSLinus Walleij { 3365fc537bfSLinus Walleij u32 val; 3375fc537bfSLinus Walleij u32 conf1; 3385fc537bfSLinus Walleij u32 conf2; 3395fc537bfSLinus Walleij u32 crop; 3405fc537bfSLinus Walleij u32 ljinc; 3415fc537bfSLinus Walleij u32 cr; 3425fc537bfSLinus Walleij u32 comp; 34344c3867aSLinus Walleij u32 pixel_fetcher_watermark; 3445fc537bfSLinus Walleij 3455fc537bfSLinus Walleij switch (ovl) { 3465fc537bfSLinus Walleij case MCDE_OVERLAY_0: 3475fc537bfSLinus Walleij conf1 = MCDE_OVL0CONF; 3485fc537bfSLinus Walleij conf2 = MCDE_OVL0CONF2; 3495fc537bfSLinus Walleij crop = MCDE_OVL0CROP; 3505fc537bfSLinus Walleij ljinc = MCDE_OVL0LJINC; 3515fc537bfSLinus Walleij cr = MCDE_OVL0CR; 3525fc537bfSLinus Walleij comp = MCDE_OVL0COMP; 3535fc537bfSLinus Walleij break; 3545fc537bfSLinus Walleij case MCDE_OVERLAY_1: 3555fc537bfSLinus Walleij conf1 = MCDE_OVL1CONF; 3565fc537bfSLinus Walleij conf2 = MCDE_OVL1CONF2; 3575fc537bfSLinus Walleij crop = MCDE_OVL1CROP; 3585fc537bfSLinus Walleij ljinc = MCDE_OVL1LJINC; 3595fc537bfSLinus Walleij cr = MCDE_OVL1CR; 3605fc537bfSLinus Walleij comp = MCDE_OVL1COMP; 3615fc537bfSLinus Walleij break; 3625fc537bfSLinus Walleij case MCDE_OVERLAY_2: 3635fc537bfSLinus Walleij conf1 = MCDE_OVL2CONF; 3645fc537bfSLinus Walleij conf2 = MCDE_OVL2CONF2; 3655fc537bfSLinus Walleij crop = MCDE_OVL2CROP; 3665fc537bfSLinus Walleij ljinc = MCDE_OVL2LJINC; 3675fc537bfSLinus Walleij cr = MCDE_OVL2CR; 3685fc537bfSLinus Walleij comp = MCDE_OVL2COMP; 3695fc537bfSLinus Walleij break; 3705fc537bfSLinus Walleij case MCDE_OVERLAY_3: 3715fc537bfSLinus Walleij conf1 = MCDE_OVL3CONF; 3725fc537bfSLinus Walleij conf2 = MCDE_OVL3CONF2; 3735fc537bfSLinus Walleij crop = MCDE_OVL3CROP; 3745fc537bfSLinus Walleij ljinc = MCDE_OVL3LJINC; 3755fc537bfSLinus Walleij cr = MCDE_OVL3CR; 3765fc537bfSLinus Walleij comp = MCDE_OVL3COMP; 3775fc537bfSLinus Walleij break; 3785fc537bfSLinus Walleij case MCDE_OVERLAY_4: 3795fc537bfSLinus Walleij conf1 = MCDE_OVL4CONF; 3805fc537bfSLinus Walleij conf2 = MCDE_OVL4CONF2; 3815fc537bfSLinus Walleij crop = MCDE_OVL4CROP; 3825fc537bfSLinus Walleij ljinc = MCDE_OVL4LJINC; 3835fc537bfSLinus Walleij cr = MCDE_OVL4CR; 3845fc537bfSLinus Walleij comp = MCDE_OVL4COMP; 3855fc537bfSLinus Walleij break; 3865fc537bfSLinus Walleij case MCDE_OVERLAY_5: 3875fc537bfSLinus Walleij conf1 = MCDE_OVL5CONF; 3885fc537bfSLinus Walleij conf2 = MCDE_OVL5CONF2; 3895fc537bfSLinus Walleij crop = MCDE_OVL5CROP; 3905fc537bfSLinus Walleij ljinc = MCDE_OVL5LJINC; 3915fc537bfSLinus Walleij cr = MCDE_OVL5CR; 3925fc537bfSLinus Walleij comp = MCDE_OVL5COMP; 3935fc537bfSLinus Walleij break; 3945fc537bfSLinus Walleij } 3955fc537bfSLinus Walleij 3965fc537bfSLinus Walleij val = mode->hdisplay << MCDE_OVLXCONF_PPL_SHIFT; 3975fc537bfSLinus Walleij val |= mode->vdisplay << MCDE_OVLXCONF_LPF_SHIFT; 3985fc537bfSLinus Walleij /* Use external source 0 that we just configured */ 3995fc537bfSLinus Walleij val |= src << MCDE_OVLXCONF_EXTSRC_ID_SHIFT; 4005fc537bfSLinus Walleij writel(val, mcde->regs + conf1); 4015fc537bfSLinus Walleij 4025fc537bfSLinus Walleij val = MCDE_OVLXCONF2_BP_PER_PIXEL_ALPHA; 4035fc537bfSLinus Walleij val |= 0xff << MCDE_OVLXCONF2_ALPHAVALUE_SHIFT; 4045fc537bfSLinus Walleij /* OPQ: overlay is opaque */ 4055fc537bfSLinus Walleij switch (format) { 4065fc537bfSLinus Walleij case DRM_FORMAT_ARGB8888: 4075fc537bfSLinus Walleij case DRM_FORMAT_ABGR8888: 4085fc537bfSLinus Walleij case DRM_FORMAT_ARGB4444: 4095fc537bfSLinus Walleij case DRM_FORMAT_ABGR4444: 4105fc537bfSLinus Walleij case DRM_FORMAT_XRGB1555: 4115fc537bfSLinus Walleij case DRM_FORMAT_XBGR1555: 4125fc537bfSLinus Walleij /* No OPQ */ 4135fc537bfSLinus Walleij break; 4145fc537bfSLinus Walleij case DRM_FORMAT_XRGB8888: 4155fc537bfSLinus Walleij case DRM_FORMAT_XBGR8888: 4165fc537bfSLinus Walleij case DRM_FORMAT_RGB888: 4175fc537bfSLinus Walleij case DRM_FORMAT_BGR888: 4185fc537bfSLinus Walleij case DRM_FORMAT_RGB565: 4195fc537bfSLinus Walleij case DRM_FORMAT_BGR565: 4205fc537bfSLinus Walleij case DRM_FORMAT_YUV422: 4215fc537bfSLinus Walleij val |= MCDE_OVLXCONF2_OPQ; 4225fc537bfSLinus Walleij break; 4235fc537bfSLinus Walleij default: 4245fc537bfSLinus Walleij dev_err(mcde->dev, "Unknown pixel format 0x%08x\n", 4255fc537bfSLinus Walleij format); 4265fc537bfSLinus Walleij break; 4275fc537bfSLinus Walleij } 42844c3867aSLinus Walleij 42944c3867aSLinus Walleij /* 43044c3867aSLinus Walleij * Pixel fetch watermark level is max 0x1FFF pixels. 43144c3867aSLinus Walleij * Two basic rules should be followed: 43244c3867aSLinus Walleij * 1. The value should be at least 256 bits. 43344c3867aSLinus Walleij * 2. The sum of all active overlays pixelfetch watermark level 43444c3867aSLinus Walleij * multiplied with bits per pixel, should be lower than the 43544c3867aSLinus Walleij * size of input_fifo_size in bits. 43644c3867aSLinus Walleij * 3. The value should be a multiple of a line (256 bits). 43744c3867aSLinus Walleij */ 43844c3867aSLinus Walleij switch (cpp) { 43944c3867aSLinus Walleij case 2: 44044c3867aSLinus Walleij pixel_fetcher_watermark = 128; 44144c3867aSLinus Walleij break; 44244c3867aSLinus Walleij case 3: 44344c3867aSLinus Walleij pixel_fetcher_watermark = 96; 44444c3867aSLinus Walleij break; 44544c3867aSLinus Walleij case 4: 44644c3867aSLinus Walleij pixel_fetcher_watermark = 48; 44744c3867aSLinus Walleij break; 44844c3867aSLinus Walleij default: 44944c3867aSLinus Walleij pixel_fetcher_watermark = 48; 45044c3867aSLinus Walleij break; 45144c3867aSLinus Walleij } 45244c3867aSLinus Walleij dev_dbg(mcde->dev, "pixel fetcher watermark level %d pixels\n", 45344c3867aSLinus Walleij pixel_fetcher_watermark); 45444c3867aSLinus Walleij val |= pixel_fetcher_watermark << MCDE_OVLXCONF2_PIXELFETCHERWATERMARKLEVEL_SHIFT; 4555fc537bfSLinus Walleij writel(val, mcde->regs + conf2); 4565fc537bfSLinus Walleij 4575fc537bfSLinus Walleij /* Number of bytes to fetch per line */ 4585fc537bfSLinus Walleij writel(mcde->stride, mcde->regs + ljinc); 4595fc537bfSLinus Walleij /* No cropping */ 4605fc537bfSLinus Walleij writel(0, mcde->regs + crop); 4615fc537bfSLinus Walleij 4625fc537bfSLinus Walleij /* Set up overlay control register */ 4635fc537bfSLinus Walleij val = MCDE_OVLXCR_OVLEN; 4645fc537bfSLinus Walleij val |= MCDE_OVLXCR_COLCCTRL_DISABLED; 4655fc537bfSLinus Walleij val |= MCDE_OVLXCR_BURSTSIZE_8W << 4665fc537bfSLinus Walleij MCDE_OVLXCR_BURSTSIZE_SHIFT; 4675fc537bfSLinus Walleij val |= MCDE_OVLXCR_MAXOUTSTANDING_8_REQ << 4685fc537bfSLinus Walleij MCDE_OVLXCR_MAXOUTSTANDING_SHIFT; 4695fc537bfSLinus Walleij /* Not using rotation but set it up anyways */ 4705fc537bfSLinus Walleij val |= MCDE_OVLXCR_ROTBURSTSIZE_8W << 4715fc537bfSLinus Walleij MCDE_OVLXCR_ROTBURSTSIZE_SHIFT; 4725fc537bfSLinus Walleij writel(val, mcde->regs + cr); 4735fc537bfSLinus Walleij 4745fc537bfSLinus Walleij /* 4755fc537bfSLinus Walleij * Set up the overlay compositor to route the overlay out to 4765fc537bfSLinus Walleij * the desired channel 4775fc537bfSLinus Walleij */ 4785fc537bfSLinus Walleij val = ch << MCDE_OVLXCOMP_CH_ID_SHIFT; 4795fc537bfSLinus Walleij writel(val, mcde->regs + comp); 4805fc537bfSLinus Walleij } 4815fc537bfSLinus Walleij 4825fc537bfSLinus Walleij static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch, 4835fc537bfSLinus Walleij enum mcde_fifo fifo, 4845fc537bfSLinus Walleij const struct drm_display_mode *mode) 4855fc537bfSLinus Walleij { 4865fc537bfSLinus Walleij u32 val; 4875fc537bfSLinus Walleij u32 conf; 4885fc537bfSLinus Walleij u32 sync; 4895fc537bfSLinus Walleij u32 stat; 4905fc537bfSLinus Walleij u32 bgcol; 4915fc537bfSLinus Walleij u32 mux; 4925fc537bfSLinus Walleij 4935fc537bfSLinus Walleij switch (ch) { 4945fc537bfSLinus Walleij case MCDE_CHANNEL_0: 4955fc537bfSLinus Walleij conf = MCDE_CHNL0CONF; 4965fc537bfSLinus Walleij sync = MCDE_CHNL0SYNCHMOD; 4975fc537bfSLinus Walleij stat = MCDE_CHNL0STAT; 4985fc537bfSLinus Walleij bgcol = MCDE_CHNL0BCKGNDCOL; 4995fc537bfSLinus Walleij mux = MCDE_CHNL0MUXING; 5005fc537bfSLinus Walleij break; 5015fc537bfSLinus Walleij case MCDE_CHANNEL_1: 5025fc537bfSLinus Walleij conf = MCDE_CHNL1CONF; 5035fc537bfSLinus Walleij sync = MCDE_CHNL1SYNCHMOD; 5045fc537bfSLinus Walleij stat = MCDE_CHNL1STAT; 5055fc537bfSLinus Walleij bgcol = MCDE_CHNL1BCKGNDCOL; 5065fc537bfSLinus Walleij mux = MCDE_CHNL1MUXING; 5075fc537bfSLinus Walleij break; 5085fc537bfSLinus Walleij case MCDE_CHANNEL_2: 5095fc537bfSLinus Walleij conf = MCDE_CHNL2CONF; 5105fc537bfSLinus Walleij sync = MCDE_CHNL2SYNCHMOD; 5115fc537bfSLinus Walleij stat = MCDE_CHNL2STAT; 5125fc537bfSLinus Walleij bgcol = MCDE_CHNL2BCKGNDCOL; 5135fc537bfSLinus Walleij mux = MCDE_CHNL2MUXING; 5145fc537bfSLinus Walleij break; 5155fc537bfSLinus Walleij case MCDE_CHANNEL_3: 5165fc537bfSLinus Walleij conf = MCDE_CHNL3CONF; 5175fc537bfSLinus Walleij sync = MCDE_CHNL3SYNCHMOD; 5185fc537bfSLinus Walleij stat = MCDE_CHNL3STAT; 5195fc537bfSLinus Walleij bgcol = MCDE_CHNL3BCKGNDCOL; 5205fc537bfSLinus Walleij mux = MCDE_CHNL3MUXING; 5215fc537bfSLinus Walleij return; 5225fc537bfSLinus Walleij } 5235fc537bfSLinus Walleij 5245fc537bfSLinus Walleij /* Set up channel 0 sync (based on chnl_update_registers()) */ 525709c2773SLinus Walleij switch (mcde->flow_mode) { 526709c2773SLinus Walleij case MCDE_COMMAND_ONESHOT_FLOW: 527709c2773SLinus Walleij /* Oneshot is achieved with software sync */ 5285fc537bfSLinus Walleij val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SOFTWARE 5295fc537bfSLinus Walleij << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; 530709c2773SLinus Walleij break; 531709c2773SLinus Walleij case MCDE_COMMAND_TE_FLOW: 532709c2773SLinus Walleij val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE 533709c2773SLinus Walleij << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; 534d920e8daSStephan Gerhold val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 535d920e8daSStephan Gerhold << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; 536709c2773SLinus Walleij break; 537709c2773SLinus Walleij case MCDE_COMMAND_BTA_TE_FLOW: 538709c2773SLinus Walleij val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE 539709c2773SLinus Walleij << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; 540709c2773SLinus Walleij /* 541709c2773SLinus Walleij * TODO: 542709c2773SLinus Walleij * The vendor driver uses the formatter as sync source 543709c2773SLinus Walleij * for BTA TE mode. Test to use TE if you have a panel 544709c2773SLinus Walleij * that uses this mode. 545709c2773SLinus Walleij */ 5465fc537bfSLinus Walleij val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER 5475fc537bfSLinus Walleij << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; 548709c2773SLinus Walleij break; 549709c2773SLinus Walleij case MCDE_VIDEO_TE_FLOW: 550709c2773SLinus Walleij val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE 551709c2773SLinus Walleij << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; 552709c2773SLinus Walleij val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_TE0 553709c2773SLinus Walleij << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; 554709c2773SLinus Walleij break; 555709c2773SLinus Walleij case MCDE_VIDEO_FORMATTER_FLOW: 556709c2773SLinus Walleij val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE 557709c2773SLinus Walleij << MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT; 558709c2773SLinus Walleij val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER 559709c2773SLinus Walleij << MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT; 560709c2773SLinus Walleij break; 561709c2773SLinus Walleij default: 562709c2773SLinus Walleij dev_err(mcde->dev, "unknown flow mode %d\n", 563709c2773SLinus Walleij mcde->flow_mode); 564709c2773SLinus Walleij break; 565709c2773SLinus Walleij } 566d920e8daSStephan Gerhold 5675fc537bfSLinus Walleij writel(val, mcde->regs + sync); 5685fc537bfSLinus Walleij 5695fc537bfSLinus Walleij /* Set up pixels per line and lines per frame */ 5705fc537bfSLinus Walleij val = (mode->hdisplay - 1) << MCDE_CHNLXCONF_PPL_SHIFT; 5715fc537bfSLinus Walleij val |= (mode->vdisplay - 1) << MCDE_CHNLXCONF_LPF_SHIFT; 5725fc537bfSLinus Walleij writel(val, mcde->regs + conf); 5735fc537bfSLinus Walleij 5745fc537bfSLinus Walleij /* 5755fc537bfSLinus Walleij * Normalize color conversion: 5765fc537bfSLinus Walleij * black background, OLED conversion disable on channel 5775fc537bfSLinus Walleij */ 5785fc537bfSLinus Walleij val = MCDE_CHNLXSTAT_CHNLBLBCKGND_EN | 5795fc537bfSLinus Walleij MCDE_CHNLXSTAT_CHNLRD; 5805fc537bfSLinus Walleij writel(val, mcde->regs + stat); 5815fc537bfSLinus Walleij writel(0, mcde->regs + bgcol); 5825fc537bfSLinus Walleij 5835fc537bfSLinus Walleij /* Set up muxing: connect the channel to the desired FIFO */ 5845fc537bfSLinus Walleij switch (fifo) { 5855fc537bfSLinus Walleij case MCDE_FIFO_A: 5865fc537bfSLinus Walleij writel(MCDE_CHNLXMUXING_FIFO_ID_FIFO_A, 5875fc537bfSLinus Walleij mcde->regs + mux); 5885fc537bfSLinus Walleij break; 5895fc537bfSLinus Walleij case MCDE_FIFO_B: 5905fc537bfSLinus Walleij writel(MCDE_CHNLXMUXING_FIFO_ID_FIFO_B, 5915fc537bfSLinus Walleij mcde->regs + mux); 5925fc537bfSLinus Walleij break; 5935fc537bfSLinus Walleij } 5945fc537bfSLinus Walleij } 5955fc537bfSLinus Walleij 5965fc537bfSLinus Walleij static void mcde_configure_fifo(struct mcde *mcde, enum mcde_fifo fifo, 5975fc537bfSLinus Walleij enum mcde_dsi_formatter fmt, 5985fc537bfSLinus Walleij int fifo_wtrmrk) 5995fc537bfSLinus Walleij { 6005fc537bfSLinus Walleij u32 val; 6015fc537bfSLinus Walleij u32 ctrl; 6025fc537bfSLinus Walleij u32 cr0, cr1; 6035fc537bfSLinus Walleij 6045fc537bfSLinus Walleij switch (fifo) { 6055fc537bfSLinus Walleij case MCDE_FIFO_A: 6065fc537bfSLinus Walleij ctrl = MCDE_CTRLA; 6075fc537bfSLinus Walleij cr0 = MCDE_CRA0; 6085fc537bfSLinus Walleij cr1 = MCDE_CRA1; 6095fc537bfSLinus Walleij break; 6105fc537bfSLinus Walleij case MCDE_FIFO_B: 6115fc537bfSLinus Walleij ctrl = MCDE_CTRLB; 6125fc537bfSLinus Walleij cr0 = MCDE_CRB0; 6135fc537bfSLinus Walleij cr1 = MCDE_CRB1; 6145fc537bfSLinus Walleij break; 6155fc537bfSLinus Walleij } 6165fc537bfSLinus Walleij 6175fc537bfSLinus Walleij val = fifo_wtrmrk << MCDE_CTRLX_FIFOWTRMRK_SHIFT; 6185fc537bfSLinus Walleij /* We only support DSI formatting for now */ 6195fc537bfSLinus Walleij val |= MCDE_CTRLX_FORMTYPE_DSI << 6205fc537bfSLinus Walleij MCDE_CTRLX_FORMTYPE_SHIFT; 6215fc537bfSLinus Walleij 6225fc537bfSLinus Walleij /* Select the formatter to use for this FIFO */ 6235fc537bfSLinus Walleij val |= fmt << MCDE_CTRLX_FORMID_SHIFT; 6245fc537bfSLinus Walleij writel(val, mcde->regs + ctrl); 6255fc537bfSLinus Walleij 6265fc537bfSLinus Walleij /* Blend source with Alpha 0xff on FIFO */ 6275fc537bfSLinus Walleij val = MCDE_CRX0_BLENDEN | 6285fc537bfSLinus Walleij 0xff << MCDE_CRX0_ALPHABLEND_SHIFT; 6295fc537bfSLinus Walleij writel(val, mcde->regs + cr0); 6305fc537bfSLinus Walleij 6315fc537bfSLinus Walleij /* Set-up from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() */ 6325fc537bfSLinus Walleij 6335fc537bfSLinus Walleij /* Use the MCDE clock for this FIFO */ 6345fc537bfSLinus Walleij val = MCDE_CRX1_CLKSEL_MCDECLK << MCDE_CRX1_CLKSEL_SHIFT; 6355fc537bfSLinus Walleij 6365fc537bfSLinus Walleij /* TODO: when adding DPI support add OUTBPP etc here */ 6375fc537bfSLinus Walleij writel(val, mcde->regs + cr1); 6385fc537bfSLinus Walleij }; 6395fc537bfSLinus Walleij 6405fc537bfSLinus Walleij static void mcde_configure_dsi_formatter(struct mcde *mcde, 6415fc537bfSLinus Walleij enum mcde_dsi_formatter fmt, 6425fc537bfSLinus Walleij u32 formatter_frame, 6435fc537bfSLinus Walleij int pkt_size) 6445fc537bfSLinus Walleij { 6455fc537bfSLinus Walleij u32 val; 6465fc537bfSLinus Walleij u32 conf0; 6475fc537bfSLinus Walleij u32 frame; 6485fc537bfSLinus Walleij u32 pkt; 6495fc537bfSLinus Walleij u32 sync; 6505fc537bfSLinus Walleij u32 cmdw; 6515fc537bfSLinus Walleij u32 delay0, delay1; 6525fc537bfSLinus Walleij 6535fc537bfSLinus Walleij switch (fmt) { 6545fc537bfSLinus Walleij case MCDE_DSI_FORMATTER_0: 6555fc537bfSLinus Walleij conf0 = MCDE_DSIVID0CONF0; 6565fc537bfSLinus Walleij frame = MCDE_DSIVID0FRAME; 6575fc537bfSLinus Walleij pkt = MCDE_DSIVID0PKT; 6585fc537bfSLinus Walleij sync = MCDE_DSIVID0SYNC; 6595fc537bfSLinus Walleij cmdw = MCDE_DSIVID0CMDW; 6605fc537bfSLinus Walleij delay0 = MCDE_DSIVID0DELAY0; 6615fc537bfSLinus Walleij delay1 = MCDE_DSIVID0DELAY1; 6625fc537bfSLinus Walleij break; 6635fc537bfSLinus Walleij case MCDE_DSI_FORMATTER_1: 6645fc537bfSLinus Walleij conf0 = MCDE_DSIVID1CONF0; 6655fc537bfSLinus Walleij frame = MCDE_DSIVID1FRAME; 6665fc537bfSLinus Walleij pkt = MCDE_DSIVID1PKT; 6675fc537bfSLinus Walleij sync = MCDE_DSIVID1SYNC; 6685fc537bfSLinus Walleij cmdw = MCDE_DSIVID1CMDW; 6695fc537bfSLinus Walleij delay0 = MCDE_DSIVID1DELAY0; 6705fc537bfSLinus Walleij delay1 = MCDE_DSIVID1DELAY1; 6715fc537bfSLinus Walleij break; 6725fc537bfSLinus Walleij case MCDE_DSI_FORMATTER_2: 6735fc537bfSLinus Walleij conf0 = MCDE_DSIVID2CONF0; 6745fc537bfSLinus Walleij frame = MCDE_DSIVID2FRAME; 6755fc537bfSLinus Walleij pkt = MCDE_DSIVID2PKT; 6765fc537bfSLinus Walleij sync = MCDE_DSIVID2SYNC; 6775fc537bfSLinus Walleij cmdw = MCDE_DSIVID2CMDW; 6785fc537bfSLinus Walleij delay0 = MCDE_DSIVID2DELAY0; 6795fc537bfSLinus Walleij delay1 = MCDE_DSIVID2DELAY1; 6805fc537bfSLinus Walleij break; 6815fc537bfSLinus Walleij } 6825fc537bfSLinus Walleij 6835fc537bfSLinus Walleij /* 6845fc537bfSLinus Walleij * Enable formatter 6855fc537bfSLinus Walleij * 8 bit commands and DCS commands (notgen = not generic) 6865fc537bfSLinus Walleij */ 6875fc537bfSLinus Walleij val = MCDE_DSICONF0_CMD8 | MCDE_DSICONF0_DCSVID_NOTGEN; 6885fc537bfSLinus Walleij if (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) 6895fc537bfSLinus Walleij val |= MCDE_DSICONF0_VID_MODE_VID; 6905fc537bfSLinus Walleij switch (mcde->mdsi->format) { 6915fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB888: 6925fc537bfSLinus Walleij val |= MCDE_DSICONF0_PACKING_RGB888 << 6935fc537bfSLinus Walleij MCDE_DSICONF0_PACKING_SHIFT; 6945fc537bfSLinus Walleij break; 6955fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB666: 6965fc537bfSLinus Walleij val |= MCDE_DSICONF0_PACKING_RGB666 << 6975fc537bfSLinus Walleij MCDE_DSICONF0_PACKING_SHIFT; 6985fc537bfSLinus Walleij break; 6995fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB666_PACKED: 700*77f512bdSLinus Walleij dev_err(mcde->dev, 701*77f512bdSLinus Walleij "we cannot handle the packed RGB666 format\n"); 702*77f512bdSLinus Walleij val |= MCDE_DSICONF0_PACKING_RGB666 << 7035fc537bfSLinus Walleij MCDE_DSICONF0_PACKING_SHIFT; 7045fc537bfSLinus Walleij break; 7055fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB565: 7065fc537bfSLinus Walleij val |= MCDE_DSICONF0_PACKING_RGB565 << 7075fc537bfSLinus Walleij MCDE_DSICONF0_PACKING_SHIFT; 7085fc537bfSLinus Walleij break; 7095fc537bfSLinus Walleij default: 7105fc537bfSLinus Walleij dev_err(mcde->dev, "unknown DSI format\n"); 7115fc537bfSLinus Walleij return; 7125fc537bfSLinus Walleij } 7135fc537bfSLinus Walleij writel(val, mcde->regs + conf0); 7145fc537bfSLinus Walleij 7155fc537bfSLinus Walleij writel(formatter_frame, mcde->regs + frame); 7165fc537bfSLinus Walleij writel(pkt_size, mcde->regs + pkt); 7175fc537bfSLinus Walleij writel(0, mcde->regs + sync); 7185fc537bfSLinus Walleij /* Define the MIPI command: we want to write into display memory */ 7195fc537bfSLinus Walleij val = MIPI_DCS_WRITE_MEMORY_CONTINUE << 7205fc537bfSLinus Walleij MCDE_DSIVIDXCMDW_CMDW_CONTINUE_SHIFT; 7215fc537bfSLinus Walleij val |= MIPI_DCS_WRITE_MEMORY_START << 7225fc537bfSLinus Walleij MCDE_DSIVIDXCMDW_CMDW_START_SHIFT; 7235fc537bfSLinus Walleij writel(val, mcde->regs + cmdw); 7245fc537bfSLinus Walleij 7255fc537bfSLinus Walleij /* 7265fc537bfSLinus Walleij * FIXME: the vendor driver has some hack around this value in 7275fc537bfSLinus Walleij * CMD mode with autotrig. 7285fc537bfSLinus Walleij */ 7295fc537bfSLinus Walleij writel(0, mcde->regs + delay0); 7305fc537bfSLinus Walleij writel(0, mcde->regs + delay1); 7315fc537bfSLinus Walleij } 7325fc537bfSLinus Walleij 7335fc537bfSLinus Walleij static void mcde_enable_fifo(struct mcde *mcde, enum mcde_fifo fifo) 7345fc537bfSLinus Walleij { 7355fc537bfSLinus Walleij u32 val; 7365fc537bfSLinus Walleij u32 cr; 7375fc537bfSLinus Walleij 7385fc537bfSLinus Walleij switch (fifo) { 7395fc537bfSLinus Walleij case MCDE_FIFO_A: 7405fc537bfSLinus Walleij cr = MCDE_CRA0; 7415fc537bfSLinus Walleij break; 7425fc537bfSLinus Walleij case MCDE_FIFO_B: 7435fc537bfSLinus Walleij cr = MCDE_CRB0; 7445fc537bfSLinus Walleij break; 7455fc537bfSLinus Walleij default: 7465fc537bfSLinus Walleij dev_err(mcde->dev, "cannot enable FIFO %c\n", 7475fc537bfSLinus Walleij 'A' + fifo); 7485fc537bfSLinus Walleij return; 7495fc537bfSLinus Walleij } 7505fc537bfSLinus Walleij 7515fc537bfSLinus Walleij spin_lock(&mcde->flow_lock); 7525fc537bfSLinus Walleij val = readl(mcde->regs + cr); 7535fc537bfSLinus Walleij val |= MCDE_CRX0_FLOEN; 7545fc537bfSLinus Walleij writel(val, mcde->regs + cr); 7555fc537bfSLinus Walleij mcde->flow_active++; 7565fc537bfSLinus Walleij spin_unlock(&mcde->flow_lock); 7575fc537bfSLinus Walleij } 7585fc537bfSLinus Walleij 7595fc537bfSLinus Walleij static void mcde_disable_fifo(struct mcde *mcde, enum mcde_fifo fifo, 7605fc537bfSLinus Walleij bool wait_for_drain) 7615fc537bfSLinus Walleij { 7625fc537bfSLinus Walleij int timeout = 100; 7635fc537bfSLinus Walleij u32 val; 7645fc537bfSLinus Walleij u32 cr; 7655fc537bfSLinus Walleij 7665fc537bfSLinus Walleij switch (fifo) { 7675fc537bfSLinus Walleij case MCDE_FIFO_A: 7685fc537bfSLinus Walleij cr = MCDE_CRA0; 7695fc537bfSLinus Walleij break; 7705fc537bfSLinus Walleij case MCDE_FIFO_B: 7715fc537bfSLinus Walleij cr = MCDE_CRB0; 7725fc537bfSLinus Walleij break; 7735fc537bfSLinus Walleij default: 7745fc537bfSLinus Walleij dev_err(mcde->dev, "cannot disable FIFO %c\n", 7755fc537bfSLinus Walleij 'A' + fifo); 7765fc537bfSLinus Walleij return; 7775fc537bfSLinus Walleij } 7785fc537bfSLinus Walleij 7795fc537bfSLinus Walleij spin_lock(&mcde->flow_lock); 7805fc537bfSLinus Walleij val = readl(mcde->regs + cr); 7815fc537bfSLinus Walleij val &= ~MCDE_CRX0_FLOEN; 7825fc537bfSLinus Walleij writel(val, mcde->regs + cr); 7835fc537bfSLinus Walleij mcde->flow_active = 0; 7845fc537bfSLinus Walleij spin_unlock(&mcde->flow_lock); 7855fc537bfSLinus Walleij 7865fc537bfSLinus Walleij if (!wait_for_drain) 7875fc537bfSLinus Walleij return; 7885fc537bfSLinus Walleij 7895fc537bfSLinus Walleij /* Check that we really drained and stopped the flow */ 7905fc537bfSLinus Walleij while (readl(mcde->regs + cr) & MCDE_CRX0_FLOEN) { 7915fc537bfSLinus Walleij usleep_range(1000, 1500); 7925fc537bfSLinus Walleij if (!--timeout) { 7935fc537bfSLinus Walleij dev_err(mcde->dev, 7945fc537bfSLinus Walleij "FIFO timeout while clearing FIFO %c\n", 7955fc537bfSLinus Walleij 'A' + fifo); 7965fc537bfSLinus Walleij return; 7975fc537bfSLinus Walleij } 7985fc537bfSLinus Walleij } 7995fc537bfSLinus Walleij } 8005fc537bfSLinus Walleij 8015fc537bfSLinus Walleij /* 8025fc537bfSLinus Walleij * This drains a pipe i.e. a FIFO connected to a certain channel 8035fc537bfSLinus Walleij */ 8045fc537bfSLinus Walleij static void mcde_drain_pipe(struct mcde *mcde, enum mcde_fifo fifo, 8055fc537bfSLinus Walleij enum mcde_channel ch) 8065fc537bfSLinus Walleij { 8075fc537bfSLinus Walleij u32 val; 8085fc537bfSLinus Walleij u32 ctrl; 8095fc537bfSLinus Walleij u32 synsw; 8105fc537bfSLinus Walleij 8115fc537bfSLinus Walleij switch (fifo) { 8125fc537bfSLinus Walleij case MCDE_FIFO_A: 8135fc537bfSLinus Walleij ctrl = MCDE_CTRLA; 8145fc537bfSLinus Walleij break; 8155fc537bfSLinus Walleij case MCDE_FIFO_B: 8165fc537bfSLinus Walleij ctrl = MCDE_CTRLB; 8175fc537bfSLinus Walleij break; 8185fc537bfSLinus Walleij } 8195fc537bfSLinus Walleij 8205fc537bfSLinus Walleij switch (ch) { 8215fc537bfSLinus Walleij case MCDE_CHANNEL_0: 8225fc537bfSLinus Walleij synsw = MCDE_CHNL0SYNCHSW; 8235fc537bfSLinus Walleij break; 8245fc537bfSLinus Walleij case MCDE_CHANNEL_1: 8255fc537bfSLinus Walleij synsw = MCDE_CHNL1SYNCHSW; 8265fc537bfSLinus Walleij break; 8275fc537bfSLinus Walleij case MCDE_CHANNEL_2: 8285fc537bfSLinus Walleij synsw = MCDE_CHNL2SYNCHSW; 8295fc537bfSLinus Walleij break; 8305fc537bfSLinus Walleij case MCDE_CHANNEL_3: 8315fc537bfSLinus Walleij synsw = MCDE_CHNL3SYNCHSW; 8325fc537bfSLinus Walleij return; 8335fc537bfSLinus Walleij } 8345fc537bfSLinus Walleij 8355fc537bfSLinus Walleij val = readl(mcde->regs + ctrl); 8365fc537bfSLinus Walleij if (!(val & MCDE_CTRLX_FIFOEMPTY)) { 8375fc537bfSLinus Walleij dev_err(mcde->dev, "Channel A FIFO not empty (handover)\n"); 8385fc537bfSLinus Walleij /* Attempt to clear the FIFO */ 8395fc537bfSLinus Walleij mcde_enable_fifo(mcde, fifo); 8405fc537bfSLinus Walleij /* Trigger a software sync out on respective channel (0-3) */ 8415fc537bfSLinus Walleij writel(MCDE_CHNLXSYNCHSW_SW_TRIG, mcde->regs + synsw); 8425fc537bfSLinus Walleij /* Disable FIFO A flow again */ 8435fc537bfSLinus Walleij mcde_disable_fifo(mcde, fifo, true); 8445fc537bfSLinus Walleij } 8455fc537bfSLinus Walleij } 8465fc537bfSLinus Walleij 8475fc537bfSLinus Walleij static int mcde_dsi_get_pkt_div(int ppl, int fifo_size) 8485fc537bfSLinus Walleij { 8495fc537bfSLinus Walleij /* 8505fc537bfSLinus Walleij * DSI command mode line packets should be split into an even number of 8515fc537bfSLinus Walleij * packets smaller than or equal to the fifo size. 8525fc537bfSLinus Walleij */ 8535fc537bfSLinus Walleij int div; 8545fc537bfSLinus Walleij const int max_div = DIV_ROUND_UP(MCDE_MAX_WIDTH, fifo_size); 8555fc537bfSLinus Walleij 8565fc537bfSLinus Walleij for (div = 1; div < max_div; div++) 8575fc537bfSLinus Walleij if (ppl % div == 0 && ppl / div <= fifo_size) 8585fc537bfSLinus Walleij return div; 8595fc537bfSLinus Walleij return 1; 8605fc537bfSLinus Walleij } 8615fc537bfSLinus Walleij 8625fc537bfSLinus Walleij static void mcde_display_enable(struct drm_simple_display_pipe *pipe, 8635fc537bfSLinus Walleij struct drm_crtc_state *cstate, 8645fc537bfSLinus Walleij struct drm_plane_state *plane_state) 8655fc537bfSLinus Walleij { 8665fc537bfSLinus Walleij struct drm_crtc *crtc = &pipe->crtc; 8675fc537bfSLinus Walleij struct drm_plane *plane = &pipe->plane; 8685fc537bfSLinus Walleij struct drm_device *drm = crtc->dev; 869b48fd568SLinus Walleij struct mcde *mcde = to_mcde(drm); 8705fc537bfSLinus Walleij const struct drm_display_mode *mode = &cstate->mode; 8715fc537bfSLinus Walleij struct drm_framebuffer *fb = plane->state->fb; 8725fc537bfSLinus Walleij u32 format = fb->format->format; 8735fc537bfSLinus Walleij u32 formatter_ppl = mode->hdisplay; /* pixels per line */ 8745fc537bfSLinus Walleij u32 formatter_lpf = mode->vdisplay; /* lines per frame */ 8755fc537bfSLinus Walleij int pkt_size, fifo_wtrmrk; 876182c58abSSam Ravnborg int cpp = fb->format->cpp[0]; 8775fc537bfSLinus Walleij int formatter_cpp; 8785fc537bfSLinus Walleij struct drm_format_name_buf tmp; 8795fc537bfSLinus Walleij u32 formatter_frame; 8805fc537bfSLinus Walleij u32 pkt_div; 8815fc537bfSLinus Walleij u32 val; 882c4842d4dSLinus Walleij int ret; 883c4842d4dSLinus Walleij 884c4842d4dSLinus Walleij /* This powers up the entire MCDE block and the DSI hardware */ 885c4842d4dSLinus Walleij ret = regulator_enable(mcde->epod); 886c4842d4dSLinus Walleij if (ret) { 887c4842d4dSLinus Walleij dev_err(drm->dev, "can't re-enable EPOD regulator\n"); 888c4842d4dSLinus Walleij return; 889c4842d4dSLinus Walleij } 8905fc537bfSLinus Walleij 8915fc537bfSLinus Walleij dev_info(drm->dev, "enable MCDE, %d x %d format %s\n", 8925fc537bfSLinus Walleij mode->hdisplay, mode->vdisplay, 8935fc537bfSLinus Walleij drm_get_format_name(format, &tmp)); 8945fc537bfSLinus Walleij if (!mcde->mdsi) { 8955fc537bfSLinus Walleij /* TODO: deal with this for non-DSI output */ 8965fc537bfSLinus Walleij dev_err(drm->dev, "no DSI master attached!\n"); 8975fc537bfSLinus Walleij return; 8985fc537bfSLinus Walleij } 8995fc537bfSLinus Walleij 900c4842d4dSLinus Walleij /* Set up the main control, watermark level at 7 */ 901c4842d4dSLinus Walleij val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT; 902c4842d4dSLinus Walleij /* 24 bits DPI: connect LSB Ch B to D[0:7] */ 903c4842d4dSLinus Walleij val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT; 904c4842d4dSLinus Walleij /* TV out: connect LSB Ch B to D[8:15] */ 905c4842d4dSLinus Walleij val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT; 906c4842d4dSLinus Walleij /* Don't care about this muxing */ 907c4842d4dSLinus Walleij val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT; 908c4842d4dSLinus Walleij /* 24 bits DPI: connect MID Ch B to D[24:31] */ 909c4842d4dSLinus Walleij val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT; 910c4842d4dSLinus Walleij /* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */ 911c4842d4dSLinus Walleij val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT; 912c4842d4dSLinus Walleij /* Syncmux bits zero: DPI channel A and B on output pins A and B resp */ 913c4842d4dSLinus Walleij writel(val, mcde->regs + MCDE_CONF0); 914c4842d4dSLinus Walleij 915c4842d4dSLinus Walleij /* Clear any pending interrupts */ 916c4842d4dSLinus Walleij mcde_display_disable_irqs(mcde); 917c4842d4dSLinus Walleij writel(0, mcde->regs + MCDE_IMSCERR); 918c4842d4dSLinus Walleij writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR); 919c4842d4dSLinus Walleij 9205fc537bfSLinus Walleij dev_info(drm->dev, "output in %s mode, format %dbpp\n", 9215fc537bfSLinus Walleij (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? 9225fc537bfSLinus Walleij "VIDEO" : "CMD", 9235fc537bfSLinus Walleij mipi_dsi_pixel_format_to_bpp(mcde->mdsi->format)); 9245fc537bfSLinus Walleij formatter_cpp = 9255fc537bfSLinus Walleij mipi_dsi_pixel_format_to_bpp(mcde->mdsi->format) / 8; 9265fc537bfSLinus Walleij dev_info(drm->dev, "overlay CPP %d bytes, DSI CPP %d bytes\n", 9275fc537bfSLinus Walleij cpp, 9285fc537bfSLinus Walleij formatter_cpp); 9295fc537bfSLinus Walleij 9305fc537bfSLinus Walleij /* Calculations from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() */ 9315fc537bfSLinus Walleij 9325fc537bfSLinus Walleij /* 9335fc537bfSLinus Walleij * Set up FIFO A watermark level: 9345fc537bfSLinus Walleij * 128 for LCD 32bpp video mode 9355fc537bfSLinus Walleij * 48 for LCD 32bpp command mode 9365fc537bfSLinus Walleij * 128 for LCD 16bpp video mode 9375fc537bfSLinus Walleij * 64 for LCD 16bpp command mode 9385fc537bfSLinus Walleij * 128 for HDMI 32bpp 9395fc537bfSLinus Walleij * 192 for HDMI 16bpp 9405fc537bfSLinus Walleij */ 9415fc537bfSLinus Walleij fifo_wtrmrk = mode->hdisplay; 9425fc537bfSLinus Walleij if (mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 9435fc537bfSLinus Walleij fifo_wtrmrk = min(fifo_wtrmrk, 128); 9445fc537bfSLinus Walleij pkt_div = 1; 9455fc537bfSLinus Walleij } else { 9465fc537bfSLinus Walleij fifo_wtrmrk = min(fifo_wtrmrk, 48); 9475fc537bfSLinus Walleij /* The FIFO is 640 entries deep on this v3 hardware */ 9485fc537bfSLinus Walleij pkt_div = mcde_dsi_get_pkt_div(mode->hdisplay, 640); 9495fc537bfSLinus Walleij } 9505fc537bfSLinus Walleij dev_dbg(drm->dev, "FIFO watermark after flooring: %d bytes\n", 9515fc537bfSLinus Walleij fifo_wtrmrk); 9525fc537bfSLinus Walleij dev_dbg(drm->dev, "Packet divisor: %d bytes\n", pkt_div); 9535fc537bfSLinus Walleij 9545fc537bfSLinus Walleij /* NOTE: pkt_div is 1 for video mode */ 9555fc537bfSLinus Walleij pkt_size = (formatter_ppl * formatter_cpp) / pkt_div; 9565fc537bfSLinus Walleij /* Commands CMD8 need one extra byte */ 9575fc537bfSLinus Walleij if (!(mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO)) 9585fc537bfSLinus Walleij pkt_size++; 9595fc537bfSLinus Walleij 9605fc537bfSLinus Walleij dev_dbg(drm->dev, "DSI packet size: %d * %d bytes per line\n", 9615fc537bfSLinus Walleij pkt_size, pkt_div); 9625fc537bfSLinus Walleij dev_dbg(drm->dev, "Overlay frame size: %u bytes\n", 9635fc537bfSLinus Walleij mode->hdisplay * mode->vdisplay * cpp); 9645fc537bfSLinus Walleij mcde->stride = mode->hdisplay * cpp; 9655fc537bfSLinus Walleij dev_dbg(drm->dev, "Overlay line stride: %u bytes\n", 9665fc537bfSLinus Walleij mcde->stride); 9675fc537bfSLinus Walleij /* NOTE: pkt_div is 1 for video mode */ 9685fc537bfSLinus Walleij formatter_frame = pkt_size * pkt_div * formatter_lpf; 9695fc537bfSLinus Walleij dev_dbg(drm->dev, "Formatter frame size: %u bytes\n", formatter_frame); 9705fc537bfSLinus Walleij 9715fc537bfSLinus Walleij /* Drain the FIFO A + channel 0 pipe so we have a clean slate */ 9725fc537bfSLinus Walleij mcde_drain_pipe(mcde, MCDE_FIFO_A, MCDE_CHANNEL_0); 9735fc537bfSLinus Walleij 9745fc537bfSLinus Walleij /* 9755fc537bfSLinus Walleij * We set up our display pipeline: 9765fc537bfSLinus Walleij * EXTSRC 0 -> OVERLAY 0 -> CHANNEL 0 -> FIFO A -> DSI FORMATTER 0 9775fc537bfSLinus Walleij * 9785fc537bfSLinus Walleij * First configure the external source (memory) on external source 0 9795fc537bfSLinus Walleij * using the desired bitstream/bitmap format 9805fc537bfSLinus Walleij */ 9815fc537bfSLinus Walleij mcde_configure_extsrc(mcde, MCDE_EXTSRC_0, format); 9825fc537bfSLinus Walleij 9835fc537bfSLinus Walleij /* 9845fc537bfSLinus Walleij * Configure overlay 0 according to format and mode and take input 9855fc537bfSLinus Walleij * from external source 0 and route the output of this overlay to 9865fc537bfSLinus Walleij * channel 0 9875fc537bfSLinus Walleij */ 9885fc537bfSLinus Walleij mcde_configure_overlay(mcde, MCDE_OVERLAY_0, MCDE_EXTSRC_0, 98944c3867aSLinus Walleij MCDE_CHANNEL_0, mode, format, cpp); 9905fc537bfSLinus Walleij 9915fc537bfSLinus Walleij /* 9925fc537bfSLinus Walleij * Configure pixel-per-line and line-per-frame for channel 0 and then 9935fc537bfSLinus Walleij * route channel 0 to FIFO A 9945fc537bfSLinus Walleij */ 9955fc537bfSLinus Walleij mcde_configure_channel(mcde, MCDE_CHANNEL_0, MCDE_FIFO_A, mode); 9965fc537bfSLinus Walleij 9975fc537bfSLinus Walleij /* Configure FIFO A to use DSI formatter 0 */ 9985fc537bfSLinus Walleij mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0, 9995fc537bfSLinus Walleij fifo_wtrmrk); 10005fc537bfSLinus Walleij 100142bac89aSLinus Walleij /* 100242bac89aSLinus Walleij * This brings up the DSI bridge which is tightly connected 100342bac89aSLinus Walleij * to the MCDE DSI formatter. 100442bac89aSLinus Walleij * 100542bac89aSLinus Walleij * FIXME: if we want to use another formatter, such as DPI, 100642bac89aSLinus Walleij * we need to be more elaborate here and select the appropriate 100742bac89aSLinus Walleij * bridge. 100842bac89aSLinus Walleij */ 100942bac89aSLinus Walleij mcde_dsi_enable(mcde->bridge); 101042bac89aSLinus Walleij 10115fc537bfSLinus Walleij /* Configure the DSI formatter 0 for the DSI panel output */ 10125fc537bfSLinus Walleij mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0, 10135fc537bfSLinus Walleij formatter_frame, pkt_size); 10145fc537bfSLinus Walleij 1015709c2773SLinus Walleij switch (mcde->flow_mode) { 1016709c2773SLinus Walleij case MCDE_COMMAND_TE_FLOW: 1017709c2773SLinus Walleij case MCDE_COMMAND_BTA_TE_FLOW: 1018709c2773SLinus Walleij case MCDE_VIDEO_TE_FLOW: 1019709c2773SLinus Walleij /* We are using TE in some comination */ 10205fc537bfSLinus Walleij if (mode->flags & DRM_MODE_FLAG_NVSYNC) 10215fc537bfSLinus Walleij val = MCDE_VSCRC_VSPOL; 10225fc537bfSLinus Walleij else 10235fc537bfSLinus Walleij val = 0; 10245fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_VSCRC0); 10255fc537bfSLinus Walleij /* Enable VSYNC capture on TE0 */ 10265fc537bfSLinus Walleij val = readl(mcde->regs + MCDE_CRC); 10275fc537bfSLinus Walleij val |= MCDE_CRC_SYCEN0; 10285fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_CRC); 1029709c2773SLinus Walleij break; 1030709c2773SLinus Walleij default: 1031709c2773SLinus Walleij /* No TE capture */ 1032709c2773SLinus Walleij break; 1033768859c2SStephan Gerhold } 10345fc537bfSLinus Walleij 10355fc537bfSLinus Walleij drm_crtc_vblank_on(crtc); 10365fc537bfSLinus Walleij 1037d920e8daSStephan Gerhold /* 103842bac89aSLinus Walleij * If we're using oneshot mode we don't start the flow 103942bac89aSLinus Walleij * until each time the display is given an update, and 104042bac89aSLinus Walleij * then we disable it immediately after. For all other 104142bac89aSLinus Walleij * modes (command or video) we start the FIFO flow 104242bac89aSLinus Walleij * right here. This is necessary for the hardware to 104342bac89aSLinus Walleij * behave right. 1044d920e8daSStephan Gerhold */ 104542bac89aSLinus Walleij if (mcde->flow_mode != MCDE_COMMAND_ONESHOT_FLOW) { 1046d920e8daSStephan Gerhold mcde_enable_fifo(mcde, MCDE_FIFO_A); 1047709c2773SLinus Walleij dev_dbg(mcde->dev, "started MCDE video FIFO flow\n"); 1048709c2773SLinus Walleij } 1049d920e8daSStephan Gerhold 105042bac89aSLinus Walleij /* Enable MCDE with automatic clock gating */ 1051c4842d4dSLinus Walleij val = readl(mcde->regs + MCDE_CR); 1052c4842d4dSLinus Walleij val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN; 1053c4842d4dSLinus Walleij writel(val, mcde->regs + MCDE_CR); 1054c4842d4dSLinus Walleij 10555fc537bfSLinus Walleij dev_info(drm->dev, "MCDE display is enabled\n"); 10565fc537bfSLinus Walleij } 10575fc537bfSLinus Walleij 10585fc537bfSLinus Walleij static void mcde_display_disable(struct drm_simple_display_pipe *pipe) 10595fc537bfSLinus Walleij { 10605fc537bfSLinus Walleij struct drm_crtc *crtc = &pipe->crtc; 10615fc537bfSLinus Walleij struct drm_device *drm = crtc->dev; 1062fd7ee85cSDaniel Vetter struct mcde *mcde = to_mcde(drm); 106397de8636SStephan Gerhold struct drm_pending_vblank_event *event; 1064c4842d4dSLinus Walleij int ret; 10655fc537bfSLinus Walleij 10665fc537bfSLinus Walleij drm_crtc_vblank_off(crtc); 10675fc537bfSLinus Walleij 10685fc537bfSLinus Walleij /* Disable FIFO A flow */ 10695fc537bfSLinus Walleij mcde_disable_fifo(mcde, MCDE_FIFO_A, true); 10705fc537bfSLinus Walleij 107142bac89aSLinus Walleij /* This disables the DSI bridge */ 107242bac89aSLinus Walleij mcde_dsi_disable(mcde->bridge); 107342bac89aSLinus Walleij 107497de8636SStephan Gerhold event = crtc->state->event; 107597de8636SStephan Gerhold if (event) { 107697de8636SStephan Gerhold crtc->state->event = NULL; 107797de8636SStephan Gerhold 107897de8636SStephan Gerhold spin_lock_irq(&crtc->dev->event_lock); 107997de8636SStephan Gerhold drm_crtc_send_vblank_event(crtc, event); 108097de8636SStephan Gerhold spin_unlock_irq(&crtc->dev->event_lock); 108197de8636SStephan Gerhold } 108297de8636SStephan Gerhold 1083c4842d4dSLinus Walleij ret = regulator_disable(mcde->epod); 1084c4842d4dSLinus Walleij if (ret) 1085c4842d4dSLinus Walleij dev_err(drm->dev, "can't disable EPOD regulator\n"); 1086c4842d4dSLinus Walleij /* Make sure we are powered down (before we may power up again) */ 1087c4842d4dSLinus Walleij usleep_range(50000, 70000); 1088c4842d4dSLinus Walleij 10895fc537bfSLinus Walleij dev_info(drm->dev, "MCDE display is disabled\n"); 10905fc537bfSLinus Walleij } 10915fc537bfSLinus Walleij 1092ea66a9beSLinus Walleij static void mcde_start_flow(struct mcde *mcde) 10935fc537bfSLinus Walleij { 1094709c2773SLinus Walleij /* Request a TE ACK only in TE+BTA mode */ 1095709c2773SLinus Walleij if (mcde->flow_mode == MCDE_COMMAND_BTA_TE_FLOW) 10965fc537bfSLinus Walleij mcde_dsi_te_request(mcde->mdsi); 10975fc537bfSLinus Walleij 10985fc537bfSLinus Walleij /* Enable FIFO A flow */ 10995fc537bfSLinus Walleij mcde_enable_fifo(mcde, MCDE_FIFO_A); 11005fc537bfSLinus Walleij 11015fc537bfSLinus Walleij /* 11025fc537bfSLinus Walleij * If oneshot mode is enabled, the flow will be disabled 11035fc537bfSLinus Walleij * when the TE0 IRQ arrives in the interrupt handler. Otherwise 11045fc537bfSLinus Walleij * updates are continuously streamed to the display after this 11055fc537bfSLinus Walleij * point. 11065fc537bfSLinus Walleij */ 11075fc537bfSLinus Walleij 1108709c2773SLinus Walleij if (mcde->flow_mode == MCDE_COMMAND_ONESHOT_FLOW) { 11095fc537bfSLinus Walleij /* Trigger a software sync out on channel 0 */ 11105fc537bfSLinus Walleij writel(MCDE_CHNLXSYNCHSW_SW_TRIG, 11115fc537bfSLinus Walleij mcde->regs + MCDE_CHNL0SYNCHSW); 11125fc537bfSLinus Walleij 11135fc537bfSLinus Walleij /* 11145fc537bfSLinus Walleij * Disable FIFO A flow again: since we are using TE sync we 11155fc537bfSLinus Walleij * need to wait for the FIFO to drain before we continue 11165fc537bfSLinus Walleij * so repeated calls to this function will not cause a mess 11175fc537bfSLinus Walleij * in the hardware by pushing updates will updates are going 11185fc537bfSLinus Walleij * on already. 11195fc537bfSLinus Walleij */ 11205fc537bfSLinus Walleij mcde_disable_fifo(mcde, MCDE_FIFO_A, true); 1121709c2773SLinus Walleij } 11225fc537bfSLinus Walleij 1123709c2773SLinus Walleij dev_dbg(mcde->dev, "started MCDE FIFO flow\n"); 11245fc537bfSLinus Walleij } 11255fc537bfSLinus Walleij 11265fc537bfSLinus Walleij static void mcde_set_extsrc(struct mcde *mcde, u32 buffer_address) 11275fc537bfSLinus Walleij { 11285fc537bfSLinus Walleij /* Write bitmap base address to register */ 11295fc537bfSLinus Walleij writel(buffer_address, mcde->regs + MCDE_EXTSRCXA0); 11305fc537bfSLinus Walleij /* 11315fc537bfSLinus Walleij * Base address for next line this is probably only used 11325fc537bfSLinus Walleij * in interlace modes. 11335fc537bfSLinus Walleij */ 11345fc537bfSLinus Walleij writel(buffer_address + mcde->stride, mcde->regs + MCDE_EXTSRCXA1); 11355fc537bfSLinus Walleij } 11365fc537bfSLinus Walleij 11375fc537bfSLinus Walleij static void mcde_display_update(struct drm_simple_display_pipe *pipe, 11385fc537bfSLinus Walleij struct drm_plane_state *old_pstate) 11395fc537bfSLinus Walleij { 11405fc537bfSLinus Walleij struct drm_crtc *crtc = &pipe->crtc; 11415fc537bfSLinus Walleij struct drm_device *drm = crtc->dev; 1142fd7ee85cSDaniel Vetter struct mcde *mcde = to_mcde(drm); 11435fc537bfSLinus Walleij struct drm_pending_vblank_event *event = crtc->state->event; 11445fc537bfSLinus Walleij struct drm_plane *plane = &pipe->plane; 11455fc537bfSLinus Walleij struct drm_plane_state *pstate = plane->state; 11465fc537bfSLinus Walleij struct drm_framebuffer *fb = pstate->fb; 11475fc537bfSLinus Walleij 11485fc537bfSLinus Walleij /* 11495fc537bfSLinus Walleij * Handle any pending event first, we need to arm the vblank 11505fc537bfSLinus Walleij * interrupt before sending any update to the display so we don't 11515fc537bfSLinus Walleij * miss the interrupt. 11525fc537bfSLinus Walleij */ 11535fc537bfSLinus Walleij if (event) { 11545fc537bfSLinus Walleij crtc->state->event = NULL; 11555fc537bfSLinus Walleij 11565fc537bfSLinus Walleij spin_lock_irq(&crtc->dev->event_lock); 11575fc537bfSLinus Walleij /* 11585fc537bfSLinus Walleij * Hardware must be on before we can arm any vblank event, 11595fc537bfSLinus Walleij * this is not a scanout controller where there is always 11605fc537bfSLinus Walleij * some periodic update going on, it is completely frozen 11615fc537bfSLinus Walleij * until we get an update. If MCDE output isn't yet enabled, 11625fc537bfSLinus Walleij * we just send a vblank dummy event back. 11635fc537bfSLinus Walleij */ 11645fc537bfSLinus Walleij if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) { 11655fc537bfSLinus Walleij dev_dbg(mcde->dev, "arm vblank event\n"); 11665fc537bfSLinus Walleij drm_crtc_arm_vblank_event(crtc, event); 11675fc537bfSLinus Walleij } else { 11685fc537bfSLinus Walleij dev_dbg(mcde->dev, "insert fake vblank event\n"); 11695fc537bfSLinus Walleij drm_crtc_send_vblank_event(crtc, event); 11705fc537bfSLinus Walleij } 11715fc537bfSLinus Walleij 11725fc537bfSLinus Walleij spin_unlock_irq(&crtc->dev->event_lock); 11735fc537bfSLinus Walleij } 11745fc537bfSLinus Walleij 11755fc537bfSLinus Walleij /* 11765fc537bfSLinus Walleij * We do not start sending framebuffer updates before the 11775fc537bfSLinus Walleij * display is enabled. Update events will however be dispatched 11785fc537bfSLinus Walleij * from the DRM core before the display is enabled. 11795fc537bfSLinus Walleij */ 11805fc537bfSLinus Walleij if (fb) { 11815fc537bfSLinus Walleij mcde_set_extsrc(mcde, drm_fb_cma_get_gem_addr(fb, pstate, 0)); 1182709c2773SLinus Walleij dev_info_once(mcde->dev, "first update of display contents\n"); 118342bac89aSLinus Walleij /* 118442bac89aSLinus Walleij * Usually the flow is already active, unless we are in 118542bac89aSLinus Walleij * oneshot mode, then we need to kick the flow right here. 118642bac89aSLinus Walleij */ 118742bac89aSLinus Walleij if (mcde->flow_active == 0) 1188ea66a9beSLinus Walleij mcde_start_flow(mcde); 11895fc537bfSLinus Walleij } else { 11905fc537bfSLinus Walleij /* 11915fc537bfSLinus Walleij * If an update is receieved before the MCDE is enabled 11925fc537bfSLinus Walleij * (before mcde_display_enable() is called) we can't really 11935fc537bfSLinus Walleij * do much with that buffer. 11945fc537bfSLinus Walleij */ 11955fc537bfSLinus Walleij dev_info(mcde->dev, "ignored a display update\n"); 11965fc537bfSLinus Walleij } 11975fc537bfSLinus Walleij } 11985fc537bfSLinus Walleij 11995fc537bfSLinus Walleij static int mcde_display_enable_vblank(struct drm_simple_display_pipe *pipe) 12005fc537bfSLinus Walleij { 12015fc537bfSLinus Walleij struct drm_crtc *crtc = &pipe->crtc; 12025fc537bfSLinus Walleij struct drm_device *drm = crtc->dev; 1203fd7ee85cSDaniel Vetter struct mcde *mcde = to_mcde(drm); 12045fc537bfSLinus Walleij u32 val; 12055fc537bfSLinus Walleij 12065fc537bfSLinus Walleij /* Enable all VBLANK IRQs */ 12075fc537bfSLinus Walleij val = MCDE_PP_VCMPA | 12085fc537bfSLinus Walleij MCDE_PP_VCMPB | 12095fc537bfSLinus Walleij MCDE_PP_VSCC0 | 12105fc537bfSLinus Walleij MCDE_PP_VSCC1 | 12115fc537bfSLinus Walleij MCDE_PP_VCMPC0 | 12125fc537bfSLinus Walleij MCDE_PP_VCMPC1; 12135fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_IMSCPP); 12145fc537bfSLinus Walleij 12155fc537bfSLinus Walleij return 0; 12165fc537bfSLinus Walleij } 12175fc537bfSLinus Walleij 12185fc537bfSLinus Walleij static void mcde_display_disable_vblank(struct drm_simple_display_pipe *pipe) 12195fc537bfSLinus Walleij { 12205fc537bfSLinus Walleij struct drm_crtc *crtc = &pipe->crtc; 12215fc537bfSLinus Walleij struct drm_device *drm = crtc->dev; 1222fd7ee85cSDaniel Vetter struct mcde *mcde = to_mcde(drm); 12235fc537bfSLinus Walleij 12245fc537bfSLinus Walleij /* Disable all VBLANK IRQs */ 12255fc537bfSLinus Walleij writel(0, mcde->regs + MCDE_IMSCPP); 12265fc537bfSLinus Walleij /* Clear any pending IRQs */ 12275fc537bfSLinus Walleij writel(0xFFFFFFFF, mcde->regs + MCDE_RISPP); 12285fc537bfSLinus Walleij } 12295fc537bfSLinus Walleij 12305fc537bfSLinus Walleij static struct drm_simple_display_pipe_funcs mcde_display_funcs = { 12315fc537bfSLinus Walleij .check = mcde_display_check, 12325fc537bfSLinus Walleij .enable = mcde_display_enable, 12335fc537bfSLinus Walleij .disable = mcde_display_disable, 12345fc537bfSLinus Walleij .update = mcde_display_update, 1235768859c2SStephan Gerhold .enable_vblank = mcde_display_enable_vblank, 1236768859c2SStephan Gerhold .disable_vblank = mcde_display_disable_vblank, 12375fc537bfSLinus Walleij .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 12385fc537bfSLinus Walleij }; 12395fc537bfSLinus Walleij 12405fc537bfSLinus Walleij int mcde_display_init(struct drm_device *drm) 12415fc537bfSLinus Walleij { 1242fd7ee85cSDaniel Vetter struct mcde *mcde = to_mcde(drm); 12435fc537bfSLinus Walleij int ret; 12445fc537bfSLinus Walleij static const u32 formats[] = { 12455fc537bfSLinus Walleij DRM_FORMAT_ARGB8888, 12465fc537bfSLinus Walleij DRM_FORMAT_ABGR8888, 12475fc537bfSLinus Walleij DRM_FORMAT_XRGB8888, 12485fc537bfSLinus Walleij DRM_FORMAT_XBGR8888, 12495fc537bfSLinus Walleij DRM_FORMAT_RGB888, 12505fc537bfSLinus Walleij DRM_FORMAT_BGR888, 12515fc537bfSLinus Walleij DRM_FORMAT_ARGB4444, 12525fc537bfSLinus Walleij DRM_FORMAT_ABGR4444, 12535fc537bfSLinus Walleij DRM_FORMAT_XRGB4444, 12545fc537bfSLinus Walleij DRM_FORMAT_XBGR4444, 12555fc537bfSLinus Walleij /* These are actually IRGB1555 so intensity bit is lost */ 12565fc537bfSLinus Walleij DRM_FORMAT_XRGB1555, 12575fc537bfSLinus Walleij DRM_FORMAT_XBGR1555, 12585fc537bfSLinus Walleij DRM_FORMAT_RGB565, 12595fc537bfSLinus Walleij DRM_FORMAT_BGR565, 12605fc537bfSLinus Walleij DRM_FORMAT_YUV422, 12615fc537bfSLinus Walleij }; 12625fc537bfSLinus Walleij 12635fc537bfSLinus Walleij ret = drm_simple_display_pipe_init(drm, &mcde->pipe, 12645fc537bfSLinus Walleij &mcde_display_funcs, 12655fc537bfSLinus Walleij formats, ARRAY_SIZE(formats), 12665fc537bfSLinus Walleij NULL, 12675fc537bfSLinus Walleij mcde->connector); 12685fc537bfSLinus Walleij if (ret) 12695fc537bfSLinus Walleij return ret; 12705fc537bfSLinus Walleij 12715fc537bfSLinus Walleij return 0; 12725fc537bfSLinus Walleij } 12735fc537bfSLinus Walleij EXPORT_SYMBOL_GPL(mcde_display_init); 1274