15fc537bfSLinus Walleij // SPDX-License-Identifier: GPL-2.0+ 25fc537bfSLinus Walleij #include <linux/clk.h> 35fc537bfSLinus Walleij #include <linux/component.h> 45fc537bfSLinus Walleij #include <linux/delay.h> 55fc537bfSLinus Walleij #include <linux/io.h> 65fc537bfSLinus Walleij #include <linux/mfd/syscon.h> 75fc537bfSLinus Walleij #include <linux/module.h> 85fc537bfSLinus Walleij #include <linux/of.h> 95fc537bfSLinus Walleij #include <linux/platform_device.h> 105fc537bfSLinus Walleij #include <linux/regmap.h> 115fc537bfSLinus Walleij #include <linux/regulator/consumer.h> 125fc537bfSLinus Walleij #include <video/mipi_display.h> 135fc537bfSLinus Walleij 145fc537bfSLinus Walleij #include <drm/drm_atomic_helper.h> 155fc537bfSLinus Walleij #include <drm/drm_bridge.h> 165fc537bfSLinus Walleij #include <drm/drm_device.h> 175fc537bfSLinus Walleij #include <drm/drm_drv.h> 185fc537bfSLinus Walleij #include <drm/drm_encoder.h> 195fc537bfSLinus Walleij #include <drm/drm_mipi_dsi.h> 205fc537bfSLinus Walleij #include <drm/drm_modeset_helper_vtables.h> 215fc537bfSLinus Walleij #include <drm/drm_of.h> 225fc537bfSLinus Walleij #include <drm/drm_panel.h> 235fc537bfSLinus Walleij #include <drm/drm_print.h> 245fc537bfSLinus Walleij #include <drm/drm_probe_helper.h> 255fc537bfSLinus Walleij 265fc537bfSLinus Walleij #include "mcde_drm.h" 275fc537bfSLinus Walleij #include "mcde_dsi_regs.h" 285fc537bfSLinus Walleij 295fc537bfSLinus Walleij #define DSI_DEFAULT_LP_FREQ_HZ 19200000 305fc537bfSLinus Walleij #define DSI_DEFAULT_HS_FREQ_HZ 420160000 315fc537bfSLinus Walleij 325fc537bfSLinus Walleij /* PRCMU DSI reset registers */ 335fc537bfSLinus Walleij #define PRCM_DSI_SW_RESET 0x324 345fc537bfSLinus Walleij #define PRCM_DSI_SW_RESET_DSI0_SW_RESETN BIT(0) 355fc537bfSLinus Walleij #define PRCM_DSI_SW_RESET_DSI1_SW_RESETN BIT(1) 365fc537bfSLinus Walleij #define PRCM_DSI_SW_RESET_DSI2_SW_RESETN BIT(2) 375fc537bfSLinus Walleij 385fc537bfSLinus Walleij struct mcde_dsi { 395fc537bfSLinus Walleij struct device *dev; 405fc537bfSLinus Walleij struct mcde *mcde; 415fc537bfSLinus Walleij struct drm_bridge bridge; 425fc537bfSLinus Walleij struct drm_connector connector; 435fc537bfSLinus Walleij struct drm_panel *panel; 445fc537bfSLinus Walleij struct drm_bridge *bridge_out; 455fc537bfSLinus Walleij struct mipi_dsi_host dsi_host; 465fc537bfSLinus Walleij struct mipi_dsi_device *mdsi; 475fc537bfSLinus Walleij struct clk *hs_clk; 485fc537bfSLinus Walleij struct clk *lp_clk; 495fc537bfSLinus Walleij unsigned long hs_freq; 505fc537bfSLinus Walleij unsigned long lp_freq; 515fc537bfSLinus Walleij bool unused; 525fc537bfSLinus Walleij 535fc537bfSLinus Walleij void __iomem *regs; 545fc537bfSLinus Walleij struct regmap *prcmu; 555fc537bfSLinus Walleij }; 565fc537bfSLinus Walleij 575fc537bfSLinus Walleij static inline struct mcde_dsi *bridge_to_mcde_dsi(struct drm_bridge *bridge) 585fc537bfSLinus Walleij { 595fc537bfSLinus Walleij return container_of(bridge, struct mcde_dsi, bridge); 605fc537bfSLinus Walleij } 615fc537bfSLinus Walleij 625fc537bfSLinus Walleij static inline struct mcde_dsi *host_to_mcde_dsi(struct mipi_dsi_host *h) 635fc537bfSLinus Walleij { 645fc537bfSLinus Walleij return container_of(h, struct mcde_dsi, dsi_host); 655fc537bfSLinus Walleij } 665fc537bfSLinus Walleij 675fc537bfSLinus Walleij static inline struct mcde_dsi *connector_to_mcde_dsi(struct drm_connector *c) 685fc537bfSLinus Walleij { 695fc537bfSLinus Walleij return container_of(c, struct mcde_dsi, connector); 705fc537bfSLinus Walleij } 715fc537bfSLinus Walleij 725fc537bfSLinus Walleij bool mcde_dsi_irq(struct mipi_dsi_device *mdsi) 735fc537bfSLinus Walleij { 745fc537bfSLinus Walleij struct mcde_dsi *d; 755fc537bfSLinus Walleij u32 val; 765fc537bfSLinus Walleij bool te_received = false; 775fc537bfSLinus Walleij 785fc537bfSLinus Walleij d = host_to_mcde_dsi(mdsi->host); 795fc537bfSLinus Walleij 805fc537bfSLinus Walleij dev_dbg(d->dev, "%s called\n", __func__); 815fc537bfSLinus Walleij 825fc537bfSLinus Walleij val = readl(d->regs + DSI_DIRECT_CMD_STS_FLAG); 835fc537bfSLinus Walleij if (val) 845fc537bfSLinus Walleij dev_dbg(d->dev, "DSI_DIRECT_CMD_STS_FLAG = %08x\n", val); 855fc537bfSLinus Walleij if (val & DSI_DIRECT_CMD_STS_WRITE_COMPLETED) 865fc537bfSLinus Walleij dev_dbg(d->dev, "direct command write completed\n"); 875fc537bfSLinus Walleij if (val & DSI_DIRECT_CMD_STS_TE_RECEIVED) { 885fc537bfSLinus Walleij te_received = true; 895fc537bfSLinus Walleij dev_dbg(d->dev, "direct command TE received\n"); 905fc537bfSLinus Walleij } 915fc537bfSLinus Walleij if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) 925fc537bfSLinus Walleij dev_err(d->dev, "direct command ACK ERR received\n"); 935fc537bfSLinus Walleij if (val & DSI_DIRECT_CMD_STS_READ_COMPLETED_WITH_ERR) 945fc537bfSLinus Walleij dev_err(d->dev, "direct command read ERR received\n"); 955fc537bfSLinus Walleij /* Mask off the ACK value and clear status */ 965fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_STS_CLR); 975fc537bfSLinus Walleij 985fc537bfSLinus Walleij val = readl(d->regs + DSI_CMD_MODE_STS_FLAG); 995fc537bfSLinus Walleij if (val) 1005fc537bfSLinus Walleij dev_dbg(d->dev, "DSI_CMD_MODE_STS_FLAG = %08x\n", val); 1015fc537bfSLinus Walleij if (val & DSI_CMD_MODE_STS_ERR_NO_TE) 1025fc537bfSLinus Walleij /* This happens all the time (safe to ignore) */ 1035fc537bfSLinus Walleij dev_dbg(d->dev, "CMD mode no TE\n"); 1045fc537bfSLinus Walleij if (val & DSI_CMD_MODE_STS_ERR_TE_MISS) 1055fc537bfSLinus Walleij /* This happens all the time (safe to ignore) */ 1065fc537bfSLinus Walleij dev_dbg(d->dev, "CMD mode TE miss\n"); 1075fc537bfSLinus Walleij if (val & DSI_CMD_MODE_STS_ERR_SDI1_UNDERRUN) 1085fc537bfSLinus Walleij dev_err(d->dev, "CMD mode SD1 underrun\n"); 1095fc537bfSLinus Walleij if (val & DSI_CMD_MODE_STS_ERR_SDI2_UNDERRUN) 1105fc537bfSLinus Walleij dev_err(d->dev, "CMD mode SD2 underrun\n"); 1115fc537bfSLinus Walleij if (val & DSI_CMD_MODE_STS_ERR_UNWANTED_RD) 1125fc537bfSLinus Walleij dev_err(d->dev, "CMD mode unwanted RD\n"); 1135fc537bfSLinus Walleij writel(val, d->regs + DSI_CMD_MODE_STS_CLR); 1145fc537bfSLinus Walleij 1155fc537bfSLinus Walleij val = readl(d->regs + DSI_DIRECT_CMD_RD_STS_FLAG); 1165fc537bfSLinus Walleij if (val) 1175fc537bfSLinus Walleij dev_dbg(d->dev, "DSI_DIRECT_CMD_RD_STS_FLAG = %08x\n", val); 1185fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_RD_STS_CLR); 1195fc537bfSLinus Walleij 1205fc537bfSLinus Walleij val = readl(d->regs + DSI_TG_STS_FLAG); 1215fc537bfSLinus Walleij if (val) 1225fc537bfSLinus Walleij dev_dbg(d->dev, "DSI_TG_STS_FLAG = %08x\n", val); 1235fc537bfSLinus Walleij writel(val, d->regs + DSI_TG_STS_CLR); 1245fc537bfSLinus Walleij 1255fc537bfSLinus Walleij val = readl(d->regs + DSI_VID_MODE_STS_FLAG); 1265fc537bfSLinus Walleij if (val) 1275fc537bfSLinus Walleij dev_err(d->dev, "some video mode error status\n"); 1285fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_MODE_STS_CLR); 1295fc537bfSLinus Walleij 1305fc537bfSLinus Walleij return te_received; 1315fc537bfSLinus Walleij } 1325fc537bfSLinus Walleij 1335fc537bfSLinus Walleij static int mcde_dsi_host_attach(struct mipi_dsi_host *host, 1345fc537bfSLinus Walleij struct mipi_dsi_device *mdsi) 1355fc537bfSLinus Walleij { 1365fc537bfSLinus Walleij struct mcde_dsi *d = host_to_mcde_dsi(host); 1375fc537bfSLinus Walleij 1385fc537bfSLinus Walleij if (mdsi->lanes < 1 || mdsi->lanes > 2) { 1395fc537bfSLinus Walleij DRM_ERROR("dsi device params invalid, 1 or 2 lanes supported\n"); 1405fc537bfSLinus Walleij return -EINVAL; 1415fc537bfSLinus Walleij } 1425fc537bfSLinus Walleij 1435fc537bfSLinus Walleij dev_info(d->dev, "attached DSI device with %d lanes\n", mdsi->lanes); 1445fc537bfSLinus Walleij /* MIPI_DSI_FMT_RGB88 etc */ 1455fc537bfSLinus Walleij dev_info(d->dev, "format %08x, %dbpp\n", mdsi->format, 1465fc537bfSLinus Walleij mipi_dsi_pixel_format_to_bpp(mdsi->format)); 1475fc537bfSLinus Walleij dev_info(d->dev, "mode flags: %08lx\n", mdsi->mode_flags); 1485fc537bfSLinus Walleij 1495fc537bfSLinus Walleij d->mdsi = mdsi; 1505fc537bfSLinus Walleij if (d->mcde) 1515fc537bfSLinus Walleij d->mcde->mdsi = mdsi; 1525fc537bfSLinus Walleij 1535fc537bfSLinus Walleij return 0; 1545fc537bfSLinus Walleij } 1555fc537bfSLinus Walleij 1565fc537bfSLinus Walleij static int mcde_dsi_host_detach(struct mipi_dsi_host *host, 1575fc537bfSLinus Walleij struct mipi_dsi_device *mdsi) 1585fc537bfSLinus Walleij { 1595fc537bfSLinus Walleij struct mcde_dsi *d = host_to_mcde_dsi(host); 1605fc537bfSLinus Walleij 1615fc537bfSLinus Walleij d->mdsi = NULL; 1625fc537bfSLinus Walleij if (d->mcde) 1635fc537bfSLinus Walleij d->mcde->mdsi = NULL; 1645fc537bfSLinus Walleij 1655fc537bfSLinus Walleij return 0; 1665fc537bfSLinus Walleij } 1675fc537bfSLinus Walleij 1685fc537bfSLinus Walleij #define MCDE_DSI_HOST_IS_READ(type) \ 1695fc537bfSLinus Walleij ((type == MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM) || \ 1705fc537bfSLinus Walleij (type == MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM) || \ 1715fc537bfSLinus Walleij (type == MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM) || \ 1725fc537bfSLinus Walleij (type == MIPI_DSI_DCS_READ)) 1735fc537bfSLinus Walleij 1745fc537bfSLinus Walleij static ssize_t mcde_dsi_host_transfer(struct mipi_dsi_host *host, 1755fc537bfSLinus Walleij const struct mipi_dsi_msg *msg) 1765fc537bfSLinus Walleij { 1775fc537bfSLinus Walleij struct mcde_dsi *d = host_to_mcde_dsi(host); 1785fc537bfSLinus Walleij const u32 loop_delay_us = 10; /* us */ 1795fc537bfSLinus Walleij const u8 *tx = msg->tx_buf; 1805fc537bfSLinus Walleij u32 loop_counter; 1815fc537bfSLinus Walleij size_t txlen; 1825fc537bfSLinus Walleij u32 val; 1835fc537bfSLinus Walleij int ret; 1845fc537bfSLinus Walleij int i; 1855fc537bfSLinus Walleij 1865fc537bfSLinus Walleij txlen = msg->tx_len; 1875fc537bfSLinus Walleij if (txlen > 12) { 1885fc537bfSLinus Walleij dev_err(d->dev, 1895fc537bfSLinus Walleij "dunno how to write more than 12 bytes yet\n"); 1905fc537bfSLinus Walleij return -EIO; 1915fc537bfSLinus Walleij } 1925fc537bfSLinus Walleij 1935fc537bfSLinus Walleij dev_dbg(d->dev, 194182c58abSSam Ravnborg "message to channel %d, %zd bytes", 1955fc537bfSLinus Walleij msg->channel, 1965fc537bfSLinus Walleij txlen); 1975fc537bfSLinus Walleij 1985fc537bfSLinus Walleij /* Command "nature" */ 1995fc537bfSLinus Walleij if (MCDE_DSI_HOST_IS_READ(msg->type)) 2005fc537bfSLinus Walleij /* MCTL_MAIN_DATA_CTL already set up */ 2015fc537bfSLinus Walleij val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_READ; 2025fc537bfSLinus Walleij else 2035fc537bfSLinus Walleij val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_WRITE; 2045fc537bfSLinus Walleij /* 2055fc537bfSLinus Walleij * More than 2 bytes will not fit in a single packet, so it's 2065fc537bfSLinus Walleij * time to set the "long not short" bit. One byte is used by 2075fc537bfSLinus Walleij * the MIPI DCS command leaving just one byte for the payload 2085fc537bfSLinus Walleij * in a short package. 2095fc537bfSLinus Walleij */ 2105fc537bfSLinus Walleij if (mipi_dsi_packet_format_is_long(msg->type)) 2115fc537bfSLinus Walleij val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LONGNOTSHORT; 2125fc537bfSLinus Walleij val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT; 2135fc537bfSLinus Walleij /* Add one to the length for the MIPI DCS command */ 2145fc537bfSLinus Walleij val |= txlen 2155fc537bfSLinus Walleij << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT; 2165fc537bfSLinus Walleij val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN; 2175fc537bfSLinus Walleij val |= msg->type << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT; 2185fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS); 2195fc537bfSLinus Walleij 2205fc537bfSLinus Walleij /* MIPI DCS command is part of the data */ 2215fc537bfSLinus Walleij if (txlen > 0) { 2225fc537bfSLinus Walleij val = 0; 2235fc537bfSLinus Walleij for (i = 0; i < 4 && i < txlen; i++) 2245fc537bfSLinus Walleij val |= tx[i] << (i & 3) * 8; 2255fc537bfSLinus Walleij } 2265fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_WRDAT0); 2275fc537bfSLinus Walleij if (txlen > 4) { 2285fc537bfSLinus Walleij val = 0; 2295fc537bfSLinus Walleij for (i = 0; i < 4 && (i + 4) < txlen; i++) 2305fc537bfSLinus Walleij val |= tx[i + 4] << (i & 3) * 8; 2315fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_WRDAT1); 2325fc537bfSLinus Walleij } 2335fc537bfSLinus Walleij if (txlen > 8) { 2345fc537bfSLinus Walleij val = 0; 2355fc537bfSLinus Walleij for (i = 0; i < 4 && (i + 8) < txlen; i++) 2365fc537bfSLinus Walleij val |= tx[i + 8] << (i & 3) * 8; 2375fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_WRDAT2); 2385fc537bfSLinus Walleij } 2395fc537bfSLinus Walleij if (txlen > 12) { 2405fc537bfSLinus Walleij val = 0; 2415fc537bfSLinus Walleij for (i = 0; i < 4 && (i + 12) < txlen; i++) 2425fc537bfSLinus Walleij val |= tx[i + 12] << (i & 3) * 8; 2435fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_WRDAT3); 2445fc537bfSLinus Walleij } 2455fc537bfSLinus Walleij 2465fc537bfSLinus Walleij writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR); 2475fc537bfSLinus Walleij writel(~0, d->regs + DSI_CMD_MODE_STS_CLR); 2485fc537bfSLinus Walleij /* Send command */ 2495fc537bfSLinus Walleij writel(1, d->regs + DSI_DIRECT_CMD_SEND); 2505fc537bfSLinus Walleij 2515fc537bfSLinus Walleij loop_counter = 1000 * 1000 / loop_delay_us; 2525fc537bfSLinus Walleij while (!(readl(d->regs + DSI_DIRECT_CMD_STS) & 2535fc537bfSLinus Walleij DSI_DIRECT_CMD_STS_WRITE_COMPLETED) 2545fc537bfSLinus Walleij && --loop_counter) 2555fc537bfSLinus Walleij usleep_range(loop_delay_us, (loop_delay_us * 3) / 2); 2565fc537bfSLinus Walleij 2575fc537bfSLinus Walleij if (!loop_counter) { 2585fc537bfSLinus Walleij dev_err(d->dev, "DSI write timeout!\n"); 2595fc537bfSLinus Walleij return -ETIME; 2605fc537bfSLinus Walleij } 2615fc537bfSLinus Walleij 2625fc537bfSLinus Walleij val = readl(d->regs + DSI_DIRECT_CMD_STS); 2635fc537bfSLinus Walleij if (val & DSI_DIRECT_CMD_STS_ACKNOWLEDGE_WITH_ERR_RECEIVED) { 2645fc537bfSLinus Walleij val >>= DSI_DIRECT_CMD_STS_ACK_VAL_SHIFT; 2655fc537bfSLinus Walleij dev_err(d->dev, "error during transmission: %04x\n", 2665fc537bfSLinus Walleij val); 2675fc537bfSLinus Walleij return -EIO; 2685fc537bfSLinus Walleij } 2695fc537bfSLinus Walleij 2705fc537bfSLinus Walleij if (!MCDE_DSI_HOST_IS_READ(msg->type)) { 2715fc537bfSLinus Walleij /* Return number of bytes written */ 2725fc537bfSLinus Walleij if (mipi_dsi_packet_format_is_long(msg->type)) 2735fc537bfSLinus Walleij ret = 4 + txlen; 2745fc537bfSLinus Walleij else 2755fc537bfSLinus Walleij ret = 4; 2765fc537bfSLinus Walleij } else { 2775fc537bfSLinus Walleij /* OK this is a read command, get the response */ 2785fc537bfSLinus Walleij u32 rdsz; 2795fc537bfSLinus Walleij u32 rddat; 2805fc537bfSLinus Walleij u8 *rx = msg->rx_buf; 2815fc537bfSLinus Walleij 2825fc537bfSLinus Walleij rdsz = readl(d->regs + DSI_DIRECT_CMD_RD_PROPERTY); 2835fc537bfSLinus Walleij rdsz &= DSI_DIRECT_CMD_RD_PROPERTY_RD_SIZE_MASK; 2845fc537bfSLinus Walleij rddat = readl(d->regs + DSI_DIRECT_CMD_RDDAT); 2855fc537bfSLinus Walleij for (i = 0; i < 4 && i < rdsz; i++) 2865fc537bfSLinus Walleij rx[i] = (rddat >> (i * 8)) & 0xff; 2875fc537bfSLinus Walleij ret = rdsz; 2885fc537bfSLinus Walleij } 2895fc537bfSLinus Walleij 2905fc537bfSLinus Walleij writel(~0, d->regs + DSI_DIRECT_CMD_STS_CLR); 2915fc537bfSLinus Walleij writel(~0, d->regs + DSI_CMD_MODE_STS_CLR); 2925fc537bfSLinus Walleij 2935fc537bfSLinus Walleij return ret; 2945fc537bfSLinus Walleij } 2955fc537bfSLinus Walleij 2965fc537bfSLinus Walleij static const struct mipi_dsi_host_ops mcde_dsi_host_ops = { 2975fc537bfSLinus Walleij .attach = mcde_dsi_host_attach, 2985fc537bfSLinus Walleij .detach = mcde_dsi_host_detach, 2995fc537bfSLinus Walleij .transfer = mcde_dsi_host_transfer, 3005fc537bfSLinus Walleij }; 3015fc537bfSLinus Walleij 3025fc537bfSLinus Walleij /* This sends a direct (short) command to request TE */ 3035fc537bfSLinus Walleij void mcde_dsi_te_request(struct mipi_dsi_device *mdsi) 3045fc537bfSLinus Walleij { 3055fc537bfSLinus Walleij struct mcde_dsi *d; 3065fc537bfSLinus Walleij u32 val; 3075fc537bfSLinus Walleij 3085fc537bfSLinus Walleij d = host_to_mcde_dsi(mdsi->host); 3095fc537bfSLinus Walleij 3105fc537bfSLinus Walleij /* Command "nature" TE request */ 3115fc537bfSLinus Walleij val = DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_NAT_TE_REQ; 3125fc537bfSLinus Walleij val |= 0 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_ID_SHIFT; 3135fc537bfSLinus Walleij val |= 2 << DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_SIZE_SHIFT; 3145fc537bfSLinus Walleij val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_LP_EN; 3155fc537bfSLinus Walleij val |= DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_DCS_SHORT_WRITE_1 << 3165fc537bfSLinus Walleij DSI_DIRECT_CMD_MAIN_SETTINGS_CMD_HEAD_SHIFT; 3175fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_MAIN_SETTINGS); 3185fc537bfSLinus Walleij 3195fc537bfSLinus Walleij /* Clear TE reveived and error status bits and enables them */ 3205fc537bfSLinus Walleij writel(DSI_DIRECT_CMD_STS_CLR_TE_RECEIVED_CLR | 3215fc537bfSLinus Walleij DSI_DIRECT_CMD_STS_CLR_ACKNOWLEDGE_WITH_ERR_RECEIVED_CLR, 3225fc537bfSLinus Walleij d->regs + DSI_DIRECT_CMD_STS_CLR); 3235fc537bfSLinus Walleij val = readl(d->regs + DSI_DIRECT_CMD_STS_CTL); 3245fc537bfSLinus Walleij val |= DSI_DIRECT_CMD_STS_CTL_TE_RECEIVED_EN; 3255fc537bfSLinus Walleij val |= DSI_DIRECT_CMD_STS_CTL_ACKNOWLEDGE_WITH_ERR_EN; 3265fc537bfSLinus Walleij writel(val, d->regs + DSI_DIRECT_CMD_STS_CTL); 3275fc537bfSLinus Walleij 3285fc537bfSLinus Walleij /* Clear and enable no TE or TE missing status */ 3295fc537bfSLinus Walleij writel(DSI_CMD_MODE_STS_CLR_ERR_NO_TE_CLR | 3305fc537bfSLinus Walleij DSI_CMD_MODE_STS_CLR_ERR_TE_MISS_CLR, 3315fc537bfSLinus Walleij d->regs + DSI_CMD_MODE_STS_CLR); 3325fc537bfSLinus Walleij val = readl(d->regs + DSI_CMD_MODE_STS_CTL); 3335fc537bfSLinus Walleij val |= DSI_CMD_MODE_STS_CTL_ERR_NO_TE_EN; 3345fc537bfSLinus Walleij val |= DSI_CMD_MODE_STS_CTL_ERR_TE_MISS_EN; 3355fc537bfSLinus Walleij writel(val, d->regs + DSI_CMD_MODE_STS_CTL); 3365fc537bfSLinus Walleij 3375fc537bfSLinus Walleij /* Send this TE request command */ 3385fc537bfSLinus Walleij writel(1, d->regs + DSI_DIRECT_CMD_SEND); 3395fc537bfSLinus Walleij } 3405fc537bfSLinus Walleij 3415fc537bfSLinus Walleij static void mcde_dsi_setup_video_mode(struct mcde_dsi *d, 3425fc537bfSLinus Walleij const struct drm_display_mode *mode) 3435fc537bfSLinus Walleij { 3445fc537bfSLinus Walleij u8 bpp = mipi_dsi_pixel_format_to_bpp(d->mdsi->format); 3455fc537bfSLinus Walleij u64 bpl; 3465fc537bfSLinus Walleij u32 hfp; 3475fc537bfSLinus Walleij u32 hbp; 3485fc537bfSLinus Walleij u32 hsa; 3495fc537bfSLinus Walleij u32 blkline_pck, line_duration; 3505fc537bfSLinus Walleij u32 blkeol_pck, blkeol_duration; 3515fc537bfSLinus Walleij u32 val; 3525fc537bfSLinus Walleij 3535fc537bfSLinus Walleij val = 0; 3545fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 3555fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_BURST_MODE; 3565fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { 3575fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_SYNC_PULSE_ACTIVE; 3585fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_SYNC_PULSE_HORIZONTAL; 3595fc537bfSLinus Walleij } 3605fc537bfSLinus Walleij /* RGB header and pixel mode */ 3615fc537bfSLinus Walleij switch (d->mdsi->format) { 3625fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB565: 3635fc537bfSLinus Walleij val |= MIPI_DSI_PACKED_PIXEL_STREAM_16 << 3645fc537bfSLinus Walleij DSI_VID_MAIN_CTL_HEADER_SHIFT; 3655fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_16BITS; 3665fc537bfSLinus Walleij break; 3675fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB666_PACKED: 3685fc537bfSLinus Walleij val |= MIPI_DSI_PACKED_PIXEL_STREAM_18 << 3695fc537bfSLinus Walleij DSI_VID_MAIN_CTL_HEADER_SHIFT; 3705fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS; 3715fc537bfSLinus Walleij break; 3725fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB666: 3735fc537bfSLinus Walleij val |= MIPI_DSI_PIXEL_STREAM_3BYTE_18 3745fc537bfSLinus Walleij << DSI_VID_MAIN_CTL_HEADER_SHIFT; 3755fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_18BITS_LOOSE; 3765fc537bfSLinus Walleij break; 3775fc537bfSLinus Walleij case MIPI_DSI_FMT_RGB888: 3785fc537bfSLinus Walleij val |= MIPI_DSI_PACKED_PIXEL_STREAM_24 << 3795fc537bfSLinus Walleij DSI_VID_MAIN_CTL_HEADER_SHIFT; 3805fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_VID_PIXEL_MODE_24BITS; 3815fc537bfSLinus Walleij break; 3825fc537bfSLinus Walleij default: 3835fc537bfSLinus Walleij dev_err(d->dev, "unknown pixel mode\n"); 3845fc537bfSLinus Walleij return; 3855fc537bfSLinus Walleij } 3865fc537bfSLinus Walleij 3875fc537bfSLinus Walleij /* TODO: TVG could be enabled here */ 3885fc537bfSLinus Walleij 3895fc537bfSLinus Walleij /* Send blanking packet */ 3905fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_REG_BLKLINE_MODE_LP_0; 3915fc537bfSLinus Walleij /* Send EOL packet */ 3925fc537bfSLinus Walleij val |= DSI_VID_MAIN_CTL_REG_BLKEOL_MODE_LP_0; 3935fc537bfSLinus Walleij /* Recovery mode 1 */ 3945fc537bfSLinus Walleij val |= 1 << DSI_VID_MAIN_CTL_RECOVERY_MODE_SHIFT; 3955fc537bfSLinus Walleij /* All other fields zero */ 3965fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_MAIN_CTL); 3975fc537bfSLinus Walleij 3985fc537bfSLinus Walleij /* Vertical frame parameters are pretty straight-forward */ 3995fc537bfSLinus Walleij val = mode->vdisplay << DSI_VID_VSIZE_VSA_LENGTH_SHIFT; 4005fc537bfSLinus Walleij /* vertical front porch */ 4015fc537bfSLinus Walleij val |= (mode->vsync_start - mode->vdisplay) 4025fc537bfSLinus Walleij << DSI_VID_VSIZE_VFP_LENGTH_SHIFT; 4035fc537bfSLinus Walleij /* vertical sync active */ 4045fc537bfSLinus Walleij val |= (mode->vsync_end - mode->vsync_start) 4055fc537bfSLinus Walleij << DSI_VID_VSIZE_VACT_LENGTH_SHIFT; 4065fc537bfSLinus Walleij /* vertical back porch */ 4075fc537bfSLinus Walleij val |= (mode->vtotal - mode->vsync_end) 4085fc537bfSLinus Walleij << DSI_VID_VSIZE_VBP_LENGTH_SHIFT; 4095fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_VSIZE); 4105fc537bfSLinus Walleij 4115fc537bfSLinus Walleij /* 4125fc537bfSLinus Walleij * Horizontal frame parameters: 4135fc537bfSLinus Walleij * horizontal resolution is given in pixels and must be re-calculated 4145fc537bfSLinus Walleij * into bytes since this is what the hardware expects. 4155fc537bfSLinus Walleij * 4165fc537bfSLinus Walleij * 6 + 2 is HFP header + checksum 4175fc537bfSLinus Walleij */ 4185fc537bfSLinus Walleij hfp = (mode->hsync_start - mode->hdisplay) * bpp - 6 - 2; 4195fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { 4205fc537bfSLinus Walleij /* 4215fc537bfSLinus Walleij * 6 is HBP header + checksum 4225fc537bfSLinus Walleij * 4 is RGB header + checksum 4235fc537bfSLinus Walleij */ 4245fc537bfSLinus Walleij hbp = (mode->htotal - mode->hsync_end) * bpp - 4 - 6; 4255fc537bfSLinus Walleij /* 4265fc537bfSLinus Walleij * 6 is HBP header + checksum 4275fc537bfSLinus Walleij * 4 is HSW packet bytes 4285fc537bfSLinus Walleij * 4 is RGB header + checksum 4295fc537bfSLinus Walleij */ 4305fc537bfSLinus Walleij hsa = (mode->hsync_end - mode->hsync_start) * bpp - 4 - 4 - 6; 4315fc537bfSLinus Walleij } else { 4325fc537bfSLinus Walleij /* 4335fc537bfSLinus Walleij * HBP includes both back porch and sync 4345fc537bfSLinus Walleij * 6 is HBP header + checksum 4355fc537bfSLinus Walleij * 4 is HSW packet bytes 4365fc537bfSLinus Walleij * 4 is RGB header + checksum 4375fc537bfSLinus Walleij */ 4385fc537bfSLinus Walleij hbp = (mode->htotal - mode->hsync_start) * bpp - 4 - 4 - 6; 4395fc537bfSLinus Walleij /* HSA is not considered in this mode and set to 0 */ 4405fc537bfSLinus Walleij hsa = 0; 4415fc537bfSLinus Walleij } 4425fc537bfSLinus Walleij dev_dbg(d->dev, "hfp: %u, hbp: %u, hsa: %u\n", 4435fc537bfSLinus Walleij hfp, hbp, hsa); 4445fc537bfSLinus Walleij 4455fc537bfSLinus Walleij /* Frame parameters: horizontal sync active */ 4465fc537bfSLinus Walleij val = hsa << DSI_VID_HSIZE1_HSA_LENGTH_SHIFT; 4475fc537bfSLinus Walleij /* horizontal back porch */ 4485fc537bfSLinus Walleij val |= hbp << DSI_VID_HSIZE1_HBP_LENGTH_SHIFT; 4495fc537bfSLinus Walleij /* horizontal front porch */ 4505fc537bfSLinus Walleij val |= hfp << DSI_VID_HSIZE1_HFP_LENGTH_SHIFT; 4515fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_HSIZE1); 4525fc537bfSLinus Walleij 4535fc537bfSLinus Walleij /* RGB data length (bytes on one scanline) */ 4545fc537bfSLinus Walleij val = mode->hdisplay * (bpp / 8); 4555fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_HSIZE2); 4565fc537bfSLinus Walleij 4575fc537bfSLinus Walleij /* TODO: further adjustments for TVG mode here */ 4585fc537bfSLinus Walleij 4595fc537bfSLinus Walleij /* 4605fc537bfSLinus Walleij * EOL packet length from bits per line calculations: pixel clock 4615fc537bfSLinus Walleij * is given in kHz, calculate the time between two pixels in 4625fc537bfSLinus Walleij * picoseconds. 4635fc537bfSLinus Walleij */ 4645fc537bfSLinus Walleij bpl = mode->clock * mode->htotal; 4655fc537bfSLinus Walleij bpl *= (d->hs_freq / 8); 4665fc537bfSLinus Walleij do_div(bpl, 1000000); /* microseconds */ 4675fc537bfSLinus Walleij do_div(bpl, 1000000); /* seconds */ 4685fc537bfSLinus Walleij bpl *= d->mdsi->lanes; 4695fc537bfSLinus Walleij dev_dbg(d->dev, "calculated bytes per line: %llu\n", bpl); 4705fc537bfSLinus Walleij /* 4715fc537bfSLinus Walleij * 6 is header + checksum, header = 4 bytes, checksum = 2 bytes 4725fc537bfSLinus Walleij * 4 is short packet for vsync/hsync 4735fc537bfSLinus Walleij */ 4745fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { 4755fc537bfSLinus Walleij /* Fixme: isn't the hsync width in pixels? */ 4765fc537bfSLinus Walleij blkline_pck = bpl - (mode->hsync_end - mode->hsync_start) - 6; 4775fc537bfSLinus Walleij val = blkline_pck << DSI_VID_BLKSIZE2_BLKLINE_PULSE_PCK_SHIFT; 4785fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_BLKSIZE2); 4795fc537bfSLinus Walleij } else { 4805fc537bfSLinus Walleij blkline_pck = bpl - 4 - 6; 4815fc537bfSLinus Walleij val = blkline_pck << DSI_VID_BLKSIZE1_BLKLINE_EVENT_PCK_SHIFT; 4825fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_BLKSIZE1); 4835fc537bfSLinus Walleij } 4845fc537bfSLinus Walleij 4855fc537bfSLinus Walleij line_duration = (blkline_pck + 6) / d->mdsi->lanes; 4865fc537bfSLinus Walleij dev_dbg(d->dev, "line duration %u\n", line_duration); 4875fc537bfSLinus Walleij val = line_duration << DSI_VID_DPHY_TIME_REG_LINE_DURATION_SHIFT; 4885fc537bfSLinus Walleij /* 4895fc537bfSLinus Walleij * This is the time to perform LP->HS on D-PHY 4905fc537bfSLinus Walleij * FIXME: nowhere to get this from: DT property on the DSI? 4915fc537bfSLinus Walleij */ 4925fc537bfSLinus Walleij val |= 0 << DSI_VID_DPHY_TIME_REG_WAKEUP_TIME_SHIFT; 4935fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_DPHY_TIME); 4945fc537bfSLinus Walleij 4955fc537bfSLinus Walleij /* Calculate block end of line */ 4965fc537bfSLinus Walleij blkeol_pck = bpl - mode->hdisplay * bpp - 6; 4975fc537bfSLinus Walleij blkeol_duration = (blkeol_pck + 6) / d->mdsi->lanes; 4985fc537bfSLinus Walleij dev_dbg(d->dev, "blkeol pck: %u, duration: %u\n", 4995fc537bfSLinus Walleij blkeol_pck, blkeol_duration); 5005fc537bfSLinus Walleij 5015fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { 5025fc537bfSLinus Walleij /* Set up EOL clock for burst mode */ 5035fc537bfSLinus Walleij val = readl(d->regs + DSI_VID_BLKSIZE1); 5045fc537bfSLinus Walleij val |= blkeol_pck << DSI_VID_BLKSIZE1_BLKEOL_PCK_SHIFT; 5055fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_BLKSIZE1); 5065fc537bfSLinus Walleij writel(blkeol_pck, d->regs + DSI_VID_VCA_SETTING2); 5075fc537bfSLinus Walleij 5085fc537bfSLinus Walleij writel(blkeol_duration, d->regs + DSI_VID_PCK_TIME); 5095fc537bfSLinus Walleij writel(blkeol_duration - 6, d->regs + DSI_VID_VCA_SETTING1); 5105fc537bfSLinus Walleij } 5115fc537bfSLinus Walleij 5125fc537bfSLinus Walleij /* Maximum line limit */ 5135fc537bfSLinus Walleij val = readl(d->regs + DSI_VID_VCA_SETTING2); 5145fc537bfSLinus Walleij val |= blkline_pck << 5155fc537bfSLinus Walleij DSI_VID_VCA_SETTING2_EXACT_BURST_LIMIT_SHIFT; 5165fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_VCA_SETTING2); 5175fc537bfSLinus Walleij 5185fc537bfSLinus Walleij /* Put IF1 into video mode */ 5195fc537bfSLinus Walleij val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); 5205fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_DATA_CTL_IF1_MODE; 5215fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); 5225fc537bfSLinus Walleij 5235fc537bfSLinus Walleij /* Disable command mode on IF1 */ 5245fc537bfSLinus Walleij val = readl(d->regs + DSI_CMD_MODE_CTL); 5255fc537bfSLinus Walleij val &= ~DSI_CMD_MODE_CTL_IF1_LP_EN; 5265fc537bfSLinus Walleij writel(val, d->regs + DSI_CMD_MODE_CTL); 5275fc537bfSLinus Walleij 5285fc537bfSLinus Walleij /* Enable some error interrupts */ 5295fc537bfSLinus Walleij val = readl(d->regs + DSI_VID_MODE_STS_CTL); 5305fc537bfSLinus Walleij val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_VSYNC; 5315fc537bfSLinus Walleij val |= DSI_VID_MODE_STS_CTL_ERR_MISSING_DATA; 5325fc537bfSLinus Walleij writel(val, d->regs + DSI_VID_MODE_STS_CTL); 5335fc537bfSLinus Walleij 5345fc537bfSLinus Walleij /* Enable video mode */ 5355fc537bfSLinus Walleij val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); 5365fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_DATA_CTL_VID_EN; 5375fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); 5385fc537bfSLinus Walleij } 5395fc537bfSLinus Walleij 5405fc537bfSLinus Walleij static void mcde_dsi_start(struct mcde_dsi *d) 5415fc537bfSLinus Walleij { 5425fc537bfSLinus Walleij unsigned long hs_freq; 5435fc537bfSLinus Walleij u32 val; 5445fc537bfSLinus Walleij int i; 5455fc537bfSLinus Walleij 5465fc537bfSLinus Walleij /* No integration mode */ 5475fc537bfSLinus Walleij writel(0, d->regs + DSI_MCTL_INTEGRATION_MODE); 5485fc537bfSLinus Walleij 5495fc537bfSLinus Walleij /* Enable the DSI port, from drivers/video/mcde/dsilink_v2.c */ 5505fc537bfSLinus Walleij val = DSI_MCTL_MAIN_DATA_CTL_LINK_EN | 5515fc537bfSLinus Walleij DSI_MCTL_MAIN_DATA_CTL_BTA_EN | 5525fc537bfSLinus Walleij DSI_MCTL_MAIN_DATA_CTL_READ_EN | 5535fc537bfSLinus Walleij DSI_MCTL_MAIN_DATA_CTL_REG_TE_EN; 5545fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET) 5555fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_DATA_CTL_HOST_EOT_GEN; 5565fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); 5575fc537bfSLinus Walleij 5585fc537bfSLinus Walleij /* Set a high command timeout, clear other fields */ 5595fc537bfSLinus Walleij val = 0x3ff << DSI_CMD_MODE_CTL_TE_TIMEOUT_SHIFT; 5605fc537bfSLinus Walleij writel(val, d->regs + DSI_CMD_MODE_CTL); 5615fc537bfSLinus Walleij 5625fc537bfSLinus Walleij /* 5635fc537bfSLinus Walleij * UI_X4 is described as "unit interval times four" 5645fc537bfSLinus Walleij * I guess since DSI packets are 4 bytes wide, one unit 5655fc537bfSLinus Walleij * is one byte. 5665fc537bfSLinus Walleij */ 5675fc537bfSLinus Walleij hs_freq = clk_get_rate(d->hs_clk); 5685fc537bfSLinus Walleij hs_freq /= 1000000; /* MHz */ 5695fc537bfSLinus Walleij val = 4000 / hs_freq; 5705fc537bfSLinus Walleij dev_dbg(d->dev, "UI value: %d\n", val); 5715fc537bfSLinus Walleij val <<= DSI_MCTL_DPHY_STATIC_UI_X4_SHIFT; 5725fc537bfSLinus Walleij val &= DSI_MCTL_DPHY_STATIC_UI_X4_MASK; 5735fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_DPHY_STATIC); 5745fc537bfSLinus Walleij 5755fc537bfSLinus Walleij /* 5765fc537bfSLinus Walleij * Enable clocking: 0x0f (something?) between each burst, 5775fc537bfSLinus Walleij * enable the second lane if needed, enable continuous clock if 5785fc537bfSLinus Walleij * needed, enable switch into ULPM (ultra-low power mode) on 5795fc537bfSLinus Walleij * all the lines. 5805fc537bfSLinus Walleij */ 5815fc537bfSLinus Walleij val = 0x0f << DSI_MCTL_MAIN_PHY_CTL_WAIT_BURST_TIME_SHIFT; 5825fc537bfSLinus Walleij if (d->mdsi->lanes == 2) 5835fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_PHY_CTL_LANE2_EN; 5845fc537bfSLinus Walleij if (!(d->mdsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) 5855fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_PHY_CTL_CLK_CONTINUOUS; 5865fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_PHY_CTL_CLK_ULPM_EN | 5875fc537bfSLinus Walleij DSI_MCTL_MAIN_PHY_CTL_DAT1_ULPM_EN | 5885fc537bfSLinus Walleij DSI_MCTL_MAIN_PHY_CTL_DAT2_ULPM_EN; 5895fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_MAIN_PHY_CTL); 5905fc537bfSLinus Walleij 5915fc537bfSLinus Walleij val = (1 << DSI_MCTL_ULPOUT_TIME_CKLANE_ULPOUT_TIME_SHIFT) | 5925fc537bfSLinus Walleij (1 << DSI_MCTL_ULPOUT_TIME_DATA_ULPOUT_TIME_SHIFT); 5935fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_ULPOUT_TIME); 5945fc537bfSLinus Walleij 5955fc537bfSLinus Walleij writel(DSI_DPHY_LANES_TRIM_DPHY_SPECS_90_81B_0_90, 5965fc537bfSLinus Walleij d->regs + DSI_DPHY_LANES_TRIM); 5975fc537bfSLinus Walleij 5985fc537bfSLinus Walleij /* High PHY timeout */ 5995fc537bfSLinus Walleij val = (0x0f << DSI_MCTL_DPHY_TIMEOUT_CLK_DIV_SHIFT) | 6005fc537bfSLinus Walleij (0x3fff << DSI_MCTL_DPHY_TIMEOUT_HSTX_TO_VAL_SHIFT) | 6015fc537bfSLinus Walleij (0x3fff << DSI_MCTL_DPHY_TIMEOUT_LPRX_TO_VAL_SHIFT); 6025fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_DPHY_TIMEOUT); 6035fc537bfSLinus Walleij 6045fc537bfSLinus Walleij val = DSI_MCTL_MAIN_EN_PLL_START | 6055fc537bfSLinus Walleij DSI_MCTL_MAIN_EN_CKLANE_EN | 6065fc537bfSLinus Walleij DSI_MCTL_MAIN_EN_DAT1_EN | 6075fc537bfSLinus Walleij DSI_MCTL_MAIN_EN_IF1_EN; 6085fc537bfSLinus Walleij if (d->mdsi->lanes == 2) 6095fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_EN_DAT2_EN; 6105fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_MAIN_EN); 6115fc537bfSLinus Walleij 6125fc537bfSLinus Walleij /* Wait for the PLL to lock and the clock and data lines to come up */ 6135fc537bfSLinus Walleij i = 0; 6145fc537bfSLinus Walleij val = DSI_MCTL_MAIN_STS_PLL_LOCK | 6155fc537bfSLinus Walleij DSI_MCTL_MAIN_STS_CLKLANE_READY | 6165fc537bfSLinus Walleij DSI_MCTL_MAIN_STS_DAT1_READY; 6175fc537bfSLinus Walleij if (d->mdsi->lanes == 2) 6185fc537bfSLinus Walleij val |= DSI_MCTL_MAIN_STS_DAT2_READY; 6195fc537bfSLinus Walleij while ((readl(d->regs + DSI_MCTL_MAIN_STS) & val) != val) { 6205fc537bfSLinus Walleij /* Sleep for a millisecond */ 6215fc537bfSLinus Walleij usleep_range(1000, 1500); 6225fc537bfSLinus Walleij if (i++ == 100) { 6235fc537bfSLinus Walleij dev_warn(d->dev, "DSI lanes did not start up\n"); 6245fc537bfSLinus Walleij return; 6255fc537bfSLinus Walleij } 6265fc537bfSLinus Walleij } 6275fc537bfSLinus Walleij 6285fc537bfSLinus Walleij /* TODO needed? */ 6295fc537bfSLinus Walleij 6305fc537bfSLinus Walleij /* Command mode, clear IF1 ID */ 6315fc537bfSLinus Walleij val = readl(d->regs + DSI_CMD_MODE_CTL); 6325fc537bfSLinus Walleij /* 6335fc537bfSLinus Walleij * If we enable low-power mode here, with 6345fc537bfSLinus Walleij * val |= DSI_CMD_MODE_CTL_IF1_LP_EN 6355fc537bfSLinus Walleij * then display updates become really slow. 6365fc537bfSLinus Walleij */ 6375fc537bfSLinus Walleij val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK; 6385fc537bfSLinus Walleij writel(val, d->regs + DSI_CMD_MODE_CTL); 6395fc537bfSLinus Walleij 6405fc537bfSLinus Walleij /* Wait for DSI PHY to initialize */ 6415fc537bfSLinus Walleij usleep_range(100, 200); 6425fc537bfSLinus Walleij dev_info(d->dev, "DSI link enabled\n"); 6435fc537bfSLinus Walleij } 6445fc537bfSLinus Walleij 6455fc537bfSLinus Walleij 6465fc537bfSLinus Walleij static void mcde_dsi_bridge_enable(struct drm_bridge *bridge) 6475fc537bfSLinus Walleij { 6485fc537bfSLinus Walleij struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 6495fc537bfSLinus Walleij 6505fc537bfSLinus Walleij dev_info(d->dev, "enable DSI master\n"); 6515fc537bfSLinus Walleij }; 6525fc537bfSLinus Walleij 6535fc537bfSLinus Walleij static void mcde_dsi_bridge_mode_set(struct drm_bridge *bridge, 6545fc537bfSLinus Walleij const struct drm_display_mode *mode, 6555fc537bfSLinus Walleij const struct drm_display_mode *adj) 6565fc537bfSLinus Walleij { 6575fc537bfSLinus Walleij struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 6585fc537bfSLinus Walleij unsigned long pixel_clock_hz = mode->clock * 1000; 6595fc537bfSLinus Walleij unsigned long hs_freq, lp_freq; 6605fc537bfSLinus Walleij u32 val; 6615fc537bfSLinus Walleij int ret; 6625fc537bfSLinus Walleij 6635fc537bfSLinus Walleij if (!d->mdsi) { 6645fc537bfSLinus Walleij dev_err(d->dev, "no DSI device attached to encoder!\n"); 6655fc537bfSLinus Walleij return; 6665fc537bfSLinus Walleij } 6675fc537bfSLinus Walleij 6685fc537bfSLinus Walleij dev_info(d->dev, "set DSI master to %dx%d %lu Hz %s mode\n", 6695fc537bfSLinus Walleij mode->hdisplay, mode->vdisplay, pixel_clock_hz, 6705fc537bfSLinus Walleij (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ? "VIDEO" : "CMD" 6715fc537bfSLinus Walleij ); 6725fc537bfSLinus Walleij 6735fc537bfSLinus Walleij /* Copy maximum clock frequencies */ 6745fc537bfSLinus Walleij if (d->mdsi->lp_rate) 6755fc537bfSLinus Walleij lp_freq = d->mdsi->lp_rate; 6765fc537bfSLinus Walleij else 6775fc537bfSLinus Walleij lp_freq = DSI_DEFAULT_LP_FREQ_HZ; 6785fc537bfSLinus Walleij if (d->mdsi->hs_rate) 6795fc537bfSLinus Walleij hs_freq = d->mdsi->hs_rate; 6805fc537bfSLinus Walleij else 6815fc537bfSLinus Walleij hs_freq = DSI_DEFAULT_HS_FREQ_HZ; 6825fc537bfSLinus Walleij 6835fc537bfSLinus Walleij /* Enable LP (Low Power, Energy Save, ES) and HS (High Speed) clocks */ 6845fc537bfSLinus Walleij d->lp_freq = clk_round_rate(d->lp_clk, lp_freq); 6855fc537bfSLinus Walleij ret = clk_set_rate(d->lp_clk, d->lp_freq); 6865fc537bfSLinus Walleij if (ret) 6875fc537bfSLinus Walleij dev_err(d->dev, "failed to set LP clock rate %lu Hz\n", 6885fc537bfSLinus Walleij d->lp_freq); 6895fc537bfSLinus Walleij 6905fc537bfSLinus Walleij d->hs_freq = clk_round_rate(d->hs_clk, hs_freq); 6915fc537bfSLinus Walleij ret = clk_set_rate(d->hs_clk, d->hs_freq); 6925fc537bfSLinus Walleij if (ret) 6935fc537bfSLinus Walleij dev_err(d->dev, "failed to set HS clock rate %lu Hz\n", 6945fc537bfSLinus Walleij d->hs_freq); 6955fc537bfSLinus Walleij 6965fc537bfSLinus Walleij /* Start clocks */ 6975fc537bfSLinus Walleij ret = clk_prepare_enable(d->lp_clk); 6985fc537bfSLinus Walleij if (ret) 6995fc537bfSLinus Walleij dev_err(d->dev, "failed to enable LP clock\n"); 7005fc537bfSLinus Walleij else 7015fc537bfSLinus Walleij dev_info(d->dev, "DSI LP clock rate %lu Hz\n", 7025fc537bfSLinus Walleij d->lp_freq); 7035fc537bfSLinus Walleij ret = clk_prepare_enable(d->hs_clk); 7045fc537bfSLinus Walleij if (ret) 7055fc537bfSLinus Walleij dev_err(d->dev, "failed to enable HS clock\n"); 7065fc537bfSLinus Walleij else 7075fc537bfSLinus Walleij dev_info(d->dev, "DSI HS clock rate %lu Hz\n", 7085fc537bfSLinus Walleij d->hs_freq); 7095fc537bfSLinus Walleij 7105fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 7115fc537bfSLinus Walleij mcde_dsi_setup_video_mode(d, mode); 7125fc537bfSLinus Walleij } else { 7135fc537bfSLinus Walleij /* Command mode, clear IF1 ID */ 7145fc537bfSLinus Walleij val = readl(d->regs + DSI_CMD_MODE_CTL); 7155fc537bfSLinus Walleij /* 7165fc537bfSLinus Walleij * If we enable low-power mode here with 7175fc537bfSLinus Walleij * val |= DSI_CMD_MODE_CTL_IF1_LP_EN 7185fc537bfSLinus Walleij * the display updates become really slow. 7195fc537bfSLinus Walleij */ 7205fc537bfSLinus Walleij val &= ~DSI_CMD_MODE_CTL_IF1_ID_MASK; 7215fc537bfSLinus Walleij writel(val, d->regs + DSI_CMD_MODE_CTL); 7225fc537bfSLinus Walleij } 7235fc537bfSLinus Walleij } 7245fc537bfSLinus Walleij 7255fc537bfSLinus Walleij static void mcde_dsi_wait_for_command_mode_stop(struct mcde_dsi *d) 7265fc537bfSLinus Walleij { 7275fc537bfSLinus Walleij u32 val; 7285fc537bfSLinus Walleij int i; 7295fc537bfSLinus Walleij 7305fc537bfSLinus Walleij /* 7315fc537bfSLinus Walleij * Wait until we get out of command mode 7325fc537bfSLinus Walleij * CSM = Command State Machine 7335fc537bfSLinus Walleij */ 7345fc537bfSLinus Walleij i = 0; 7355fc537bfSLinus Walleij val = DSI_CMD_MODE_STS_CSM_RUNNING; 7365fc537bfSLinus Walleij while ((readl(d->regs + DSI_CMD_MODE_STS) & val) == val) { 7375fc537bfSLinus Walleij /* Sleep for a millisecond */ 7385fc537bfSLinus Walleij usleep_range(1000, 2000); 7395fc537bfSLinus Walleij if (i++ == 100) { 7405fc537bfSLinus Walleij dev_warn(d->dev, 7415fc537bfSLinus Walleij "could not get out of command mode\n"); 7425fc537bfSLinus Walleij return; 7435fc537bfSLinus Walleij } 7445fc537bfSLinus Walleij } 7455fc537bfSLinus Walleij } 7465fc537bfSLinus Walleij 7475fc537bfSLinus Walleij static void mcde_dsi_wait_for_video_mode_stop(struct mcde_dsi *d) 7485fc537bfSLinus Walleij { 7495fc537bfSLinus Walleij u32 val; 7505fc537bfSLinus Walleij int i; 7515fc537bfSLinus Walleij 7525fc537bfSLinus Walleij /* Wait until we get out og video mode */ 7535fc537bfSLinus Walleij i = 0; 7545fc537bfSLinus Walleij val = DSI_VID_MODE_STS_VSG_RUNNING; 7555fc537bfSLinus Walleij while ((readl(d->regs + DSI_VID_MODE_STS) & val) == val) { 7565fc537bfSLinus Walleij /* Sleep for a millisecond */ 7575fc537bfSLinus Walleij usleep_range(1000, 2000); 7585fc537bfSLinus Walleij if (i++ == 100) { 7595fc537bfSLinus Walleij dev_warn(d->dev, 7605fc537bfSLinus Walleij "could not get out of video mode\n"); 7615fc537bfSLinus Walleij return; 7625fc537bfSLinus Walleij } 7635fc537bfSLinus Walleij } 7645fc537bfSLinus Walleij } 7655fc537bfSLinus Walleij 7665fc537bfSLinus Walleij static void mcde_dsi_bridge_disable(struct drm_bridge *bridge) 7675fc537bfSLinus Walleij { 7685fc537bfSLinus Walleij struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 7695fc537bfSLinus Walleij u32 val; 7705fc537bfSLinus Walleij 7715fc537bfSLinus Walleij /* Disable all error interrupts */ 7725fc537bfSLinus Walleij writel(0, d->regs + DSI_VID_MODE_STS_CTL); 7735fc537bfSLinus Walleij 7745fc537bfSLinus Walleij if (d->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) { 7755fc537bfSLinus Walleij /* Stop video mode */ 7765fc537bfSLinus Walleij val = readl(d->regs + DSI_MCTL_MAIN_DATA_CTL); 7775fc537bfSLinus Walleij val &= ~DSI_MCTL_MAIN_DATA_CTL_VID_EN; 7785fc537bfSLinus Walleij writel(val, d->regs + DSI_MCTL_MAIN_DATA_CTL); 7795fc537bfSLinus Walleij mcde_dsi_wait_for_video_mode_stop(d); 7805fc537bfSLinus Walleij } else { 7815fc537bfSLinus Walleij /* Stop command mode */ 7825fc537bfSLinus Walleij mcde_dsi_wait_for_command_mode_stop(d); 7835fc537bfSLinus Walleij } 7845fc537bfSLinus Walleij 7855fc537bfSLinus Walleij /* Stop clocks */ 7865fc537bfSLinus Walleij clk_disable_unprepare(d->hs_clk); 7875fc537bfSLinus Walleij clk_disable_unprepare(d->lp_clk); 7885fc537bfSLinus Walleij } 7895fc537bfSLinus Walleij 7905fc537bfSLinus Walleij /* 7915fc537bfSLinus Walleij * This connector needs no special handling, just use the default 7925fc537bfSLinus Walleij * helpers for everything. It's pretty dummy. 7935fc537bfSLinus Walleij */ 7945fc537bfSLinus Walleij static const struct drm_connector_funcs mcde_dsi_connector_funcs = { 7955fc537bfSLinus Walleij .reset = drm_atomic_helper_connector_reset, 7965fc537bfSLinus Walleij .fill_modes = drm_helper_probe_single_connector_modes, 7975fc537bfSLinus Walleij .destroy = drm_connector_cleanup, 7985fc537bfSLinus Walleij .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 7995fc537bfSLinus Walleij .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 8005fc537bfSLinus Walleij }; 8015fc537bfSLinus Walleij 8025fc537bfSLinus Walleij static int mcde_dsi_get_modes(struct drm_connector *connector) 8035fc537bfSLinus Walleij { 8045fc537bfSLinus Walleij struct mcde_dsi *d = connector_to_mcde_dsi(connector); 8055fc537bfSLinus Walleij 8065fc537bfSLinus Walleij /* Just pass the question to the panel */ 8075fc537bfSLinus Walleij if (d->panel) 8085fc537bfSLinus Walleij return drm_panel_get_modes(d->panel); 8095fc537bfSLinus Walleij 8105fc537bfSLinus Walleij /* TODO: deal with bridges */ 8115fc537bfSLinus Walleij 8125fc537bfSLinus Walleij return 0; 8135fc537bfSLinus Walleij } 8145fc537bfSLinus Walleij 8155fc537bfSLinus Walleij static const struct drm_connector_helper_funcs 8165fc537bfSLinus Walleij mcde_dsi_connector_helper_funcs = { 8175fc537bfSLinus Walleij .get_modes = mcde_dsi_get_modes, 8185fc537bfSLinus Walleij }; 8195fc537bfSLinus Walleij 8205fc537bfSLinus Walleij static int mcde_dsi_bridge_attach(struct drm_bridge *bridge) 8215fc537bfSLinus Walleij { 8225fc537bfSLinus Walleij struct mcde_dsi *d = bridge_to_mcde_dsi(bridge); 8235fc537bfSLinus Walleij struct drm_device *drm = bridge->dev; 8245fc537bfSLinus Walleij int ret; 8255fc537bfSLinus Walleij 8265fc537bfSLinus Walleij drm_connector_helper_add(&d->connector, 8275fc537bfSLinus Walleij &mcde_dsi_connector_helper_funcs); 8285fc537bfSLinus Walleij 8295fc537bfSLinus Walleij if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) { 8305fc537bfSLinus Walleij dev_err(d->dev, "we need atomic updates\n"); 8315fc537bfSLinus Walleij return -ENOTSUPP; 8325fc537bfSLinus Walleij } 8335fc537bfSLinus Walleij 8345fc537bfSLinus Walleij ret = drm_connector_init(drm, &d->connector, 8355fc537bfSLinus Walleij &mcde_dsi_connector_funcs, 8365fc537bfSLinus Walleij DRM_MODE_CONNECTOR_DSI); 8375fc537bfSLinus Walleij if (ret) { 8385fc537bfSLinus Walleij dev_err(d->dev, "failed to initialize DSI bridge connector\n"); 8395fc537bfSLinus Walleij return ret; 8405fc537bfSLinus Walleij } 8415fc537bfSLinus Walleij d->connector.polled = DRM_CONNECTOR_POLL_CONNECT; 8425fc537bfSLinus Walleij /* The encoder in the bridge attached to the DSI bridge */ 8435fc537bfSLinus Walleij drm_connector_attach_encoder(&d->connector, bridge->encoder); 8445fc537bfSLinus Walleij /* Then we attach the DSI bridge to the output (panel etc) bridge */ 8455fc537bfSLinus Walleij ret = drm_bridge_attach(bridge->encoder, d->bridge_out, bridge); 8465fc537bfSLinus Walleij if (ret) { 8475fc537bfSLinus Walleij dev_err(d->dev, "failed to attach the DSI bridge\n"); 8485fc537bfSLinus Walleij return ret; 8495fc537bfSLinus Walleij } 8505fc537bfSLinus Walleij d->connector.status = connector_status_connected; 8515fc537bfSLinus Walleij 8525fc537bfSLinus Walleij return 0; 8535fc537bfSLinus Walleij } 8545fc537bfSLinus Walleij 8555fc537bfSLinus Walleij static const struct drm_bridge_funcs mcde_dsi_bridge_funcs = { 8565fc537bfSLinus Walleij .attach = mcde_dsi_bridge_attach, 8575fc537bfSLinus Walleij .mode_set = mcde_dsi_bridge_mode_set, 8585fc537bfSLinus Walleij .disable = mcde_dsi_bridge_disable, 8595fc537bfSLinus Walleij .enable = mcde_dsi_bridge_enable, 8605fc537bfSLinus Walleij }; 8615fc537bfSLinus Walleij 8625fc537bfSLinus Walleij static int mcde_dsi_bind(struct device *dev, struct device *master, 8635fc537bfSLinus Walleij void *data) 8645fc537bfSLinus Walleij { 8655fc537bfSLinus Walleij struct drm_device *drm = data; 8665fc537bfSLinus Walleij struct mcde *mcde = drm->dev_private; 8675fc537bfSLinus Walleij struct mcde_dsi *d = dev_get_drvdata(dev); 8685fc537bfSLinus Walleij struct device_node *child; 8695fc537bfSLinus Walleij struct drm_panel *panel = NULL; 8705fc537bfSLinus Walleij struct drm_bridge *bridge = NULL; 8715fc537bfSLinus Walleij 8725fc537bfSLinus Walleij if (!of_get_available_child_count(dev->of_node)) { 8735fc537bfSLinus Walleij dev_info(dev, "unused DSI interface\n"); 8745fc537bfSLinus Walleij d->unused = true; 8755fc537bfSLinus Walleij return 0; 8765fc537bfSLinus Walleij } 8775fc537bfSLinus Walleij d->mcde = mcde; 8785fc537bfSLinus Walleij /* If the display attached before binding, set this up */ 8795fc537bfSLinus Walleij if (d->mdsi) 8805fc537bfSLinus Walleij d->mcde->mdsi = d->mdsi; 8815fc537bfSLinus Walleij 8825fc537bfSLinus Walleij /* Obtain the clocks */ 8835fc537bfSLinus Walleij d->hs_clk = devm_clk_get(dev, "hs"); 8845fc537bfSLinus Walleij if (IS_ERR(d->hs_clk)) { 8855fc537bfSLinus Walleij dev_err(dev, "unable to get HS clock\n"); 8865fc537bfSLinus Walleij return PTR_ERR(d->hs_clk); 8875fc537bfSLinus Walleij } 8885fc537bfSLinus Walleij 8895fc537bfSLinus Walleij d->lp_clk = devm_clk_get(dev, "lp"); 8905fc537bfSLinus Walleij if (IS_ERR(d->lp_clk)) { 8915fc537bfSLinus Walleij dev_err(dev, "unable to get LP clock\n"); 8925fc537bfSLinus Walleij return PTR_ERR(d->lp_clk); 8935fc537bfSLinus Walleij } 8945fc537bfSLinus Walleij 8955fc537bfSLinus Walleij /* Assert RESET through the PRCMU, active low */ 8965fc537bfSLinus Walleij /* FIXME: which DSI block? */ 8975fc537bfSLinus Walleij regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET, 8985fc537bfSLinus Walleij PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0); 8995fc537bfSLinus Walleij 9005fc537bfSLinus Walleij usleep_range(100, 200); 9015fc537bfSLinus Walleij 9025fc537bfSLinus Walleij /* De-assert RESET again */ 9035fc537bfSLinus Walleij regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET, 9045fc537bfSLinus Walleij PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 9055fc537bfSLinus Walleij PRCM_DSI_SW_RESET_DSI0_SW_RESETN); 9065fc537bfSLinus Walleij 9075fc537bfSLinus Walleij /* Start up the hardware */ 9085fc537bfSLinus Walleij mcde_dsi_start(d); 9095fc537bfSLinus Walleij 9105fc537bfSLinus Walleij /* Look for a panel as a child to this node */ 9115fc537bfSLinus Walleij for_each_available_child_of_node(dev->of_node, child) { 9125fc537bfSLinus Walleij panel = of_drm_find_panel(child); 9135fc537bfSLinus Walleij if (IS_ERR(panel)) { 9145fc537bfSLinus Walleij dev_err(dev, "failed to find panel try bridge (%lu)\n", 9155fc537bfSLinus Walleij PTR_ERR(panel)); 9165fc537bfSLinus Walleij bridge = of_drm_find_bridge(child); 9175fc537bfSLinus Walleij if (IS_ERR(bridge)) { 9185fc537bfSLinus Walleij dev_err(dev, "failed to find bridge (%lu)\n", 9195fc537bfSLinus Walleij PTR_ERR(bridge)); 9205fc537bfSLinus Walleij return PTR_ERR(bridge); 9215fc537bfSLinus Walleij } 9225fc537bfSLinus Walleij } 9235fc537bfSLinus Walleij } 9245fc537bfSLinus Walleij if (panel) { 92589958b7cSLaurent Pinchart bridge = drm_panel_bridge_add_typed(panel, 9265fc537bfSLinus Walleij DRM_MODE_CONNECTOR_DSI); 9275fc537bfSLinus Walleij if (IS_ERR(bridge)) { 9285fc537bfSLinus Walleij dev_err(dev, "error adding panel bridge\n"); 9295fc537bfSLinus Walleij return PTR_ERR(bridge); 9305fc537bfSLinus Walleij } 9315fc537bfSLinus Walleij dev_info(dev, "connected to panel\n"); 9325fc537bfSLinus Walleij d->panel = panel; 9335fc537bfSLinus Walleij } else if (bridge) { 9345fc537bfSLinus Walleij /* TODO: AV8100 HDMI encoder goes here for example */ 9355fc537bfSLinus Walleij dev_info(dev, "connected to non-panel bridge (unsupported)\n"); 9365fc537bfSLinus Walleij return -ENODEV; 9375fc537bfSLinus Walleij } else { 9385fc537bfSLinus Walleij dev_err(dev, "no panel or bridge\n"); 9395fc537bfSLinus Walleij return -ENODEV; 9405fc537bfSLinus Walleij } 9415fc537bfSLinus Walleij 9425fc537bfSLinus Walleij d->bridge_out = bridge; 9435fc537bfSLinus Walleij 9445fc537bfSLinus Walleij /* Create a bridge for this DSI channel */ 9455fc537bfSLinus Walleij d->bridge.funcs = &mcde_dsi_bridge_funcs; 9465fc537bfSLinus Walleij d->bridge.of_node = dev->of_node; 9475fc537bfSLinus Walleij drm_bridge_add(&d->bridge); 9485fc537bfSLinus Walleij 9495fc537bfSLinus Walleij /* TODO: first come first serve, use a list */ 9505fc537bfSLinus Walleij mcde->bridge = &d->bridge; 9515fc537bfSLinus Walleij 9525fc537bfSLinus Walleij dev_info(dev, "initialized MCDE DSI bridge\n"); 9535fc537bfSLinus Walleij 9545fc537bfSLinus Walleij return 0; 9555fc537bfSLinus Walleij } 9565fc537bfSLinus Walleij 9575fc537bfSLinus Walleij static void mcde_dsi_unbind(struct device *dev, struct device *master, 9585fc537bfSLinus Walleij void *data) 9595fc537bfSLinus Walleij { 9605fc537bfSLinus Walleij struct mcde_dsi *d = dev_get_drvdata(dev); 9615fc537bfSLinus Walleij 9625fc537bfSLinus Walleij if (d->panel) 9635fc537bfSLinus Walleij drm_panel_bridge_remove(d->bridge_out); 9645fc537bfSLinus Walleij regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET, 9655fc537bfSLinus Walleij PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0); 9665fc537bfSLinus Walleij } 9675fc537bfSLinus Walleij 9685fc537bfSLinus Walleij static const struct component_ops mcde_dsi_component_ops = { 9695fc537bfSLinus Walleij .bind = mcde_dsi_bind, 9705fc537bfSLinus Walleij .unbind = mcde_dsi_unbind, 9715fc537bfSLinus Walleij }; 9725fc537bfSLinus Walleij 9735fc537bfSLinus Walleij static int mcde_dsi_probe(struct platform_device *pdev) 9745fc537bfSLinus Walleij { 9755fc537bfSLinus Walleij struct device *dev = &pdev->dev; 9765fc537bfSLinus Walleij struct mcde_dsi *d; 9775fc537bfSLinus Walleij struct mipi_dsi_host *host; 9785fc537bfSLinus Walleij struct resource *res; 9795fc537bfSLinus Walleij u32 dsi_id; 9805fc537bfSLinus Walleij int ret; 9815fc537bfSLinus Walleij 9825fc537bfSLinus Walleij d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL); 9835fc537bfSLinus Walleij if (!d) 9845fc537bfSLinus Walleij return -ENOMEM; 9855fc537bfSLinus Walleij d->dev = dev; 9865fc537bfSLinus Walleij platform_set_drvdata(pdev, d); 9875fc537bfSLinus Walleij 9885fc537bfSLinus Walleij /* Get a handle on the PRCMU so we can do reset */ 9895fc537bfSLinus Walleij d->prcmu = 9905fc537bfSLinus Walleij syscon_regmap_lookup_by_compatible("stericsson,db8500-prcmu"); 9915fc537bfSLinus Walleij if (IS_ERR(d->prcmu)) { 9925fc537bfSLinus Walleij dev_err(dev, "no PRCMU regmap\n"); 9935fc537bfSLinus Walleij return PTR_ERR(d->prcmu); 9945fc537bfSLinus Walleij } 9955fc537bfSLinus Walleij 9965fc537bfSLinus Walleij res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 9975fc537bfSLinus Walleij d->regs = devm_ioremap_resource(dev, res); 9985fc537bfSLinus Walleij if (IS_ERR(d->regs)) { 9995fc537bfSLinus Walleij dev_err(dev, "no DSI regs\n"); 10005fc537bfSLinus Walleij return PTR_ERR(d->regs); 10015fc537bfSLinus Walleij } 10025fc537bfSLinus Walleij 10035fc537bfSLinus Walleij dsi_id = readl(d->regs + DSI_ID_REG); 10045fc537bfSLinus Walleij dev_info(dev, "HW revision 0x%08x\n", dsi_id); 10055fc537bfSLinus Walleij 10065fc537bfSLinus Walleij host = &d->dsi_host; 10075fc537bfSLinus Walleij host->dev = dev; 10085fc537bfSLinus Walleij host->ops = &mcde_dsi_host_ops; 10095fc537bfSLinus Walleij ret = mipi_dsi_host_register(host); 10105fc537bfSLinus Walleij if (ret < 0) { 10115fc537bfSLinus Walleij dev_err(dev, "failed to register DSI host: %d\n", ret); 10125fc537bfSLinus Walleij return ret; 10135fc537bfSLinus Walleij } 10145fc537bfSLinus Walleij dev_info(dev, "registered DSI host\n"); 10155fc537bfSLinus Walleij 10165fc537bfSLinus Walleij platform_set_drvdata(pdev, d); 10175fc537bfSLinus Walleij return component_add(dev, &mcde_dsi_component_ops); 10185fc537bfSLinus Walleij } 10195fc537bfSLinus Walleij 10205fc537bfSLinus Walleij static int mcde_dsi_remove(struct platform_device *pdev) 10215fc537bfSLinus Walleij { 10225fc537bfSLinus Walleij struct mcde_dsi *d = platform_get_drvdata(pdev); 10235fc537bfSLinus Walleij 10245fc537bfSLinus Walleij component_del(&pdev->dev, &mcde_dsi_component_ops); 10255fc537bfSLinus Walleij mipi_dsi_host_unregister(&d->dsi_host); 10265fc537bfSLinus Walleij 10275fc537bfSLinus Walleij return 0; 10285fc537bfSLinus Walleij } 10295fc537bfSLinus Walleij 10305fc537bfSLinus Walleij static const struct of_device_id mcde_dsi_of_match[] = { 10315fc537bfSLinus Walleij { 10325fc537bfSLinus Walleij .compatible = "ste,mcde-dsi", 10335fc537bfSLinus Walleij }, 10345fc537bfSLinus Walleij {}, 10355fc537bfSLinus Walleij }; 10365fc537bfSLinus Walleij 10375fc537bfSLinus Walleij struct platform_driver mcde_dsi_driver = { 10385fc537bfSLinus Walleij .driver = { 10395fc537bfSLinus Walleij .name = "mcde-dsi", 10405fc537bfSLinus Walleij .of_match_table = of_match_ptr(mcde_dsi_of_match), 10415fc537bfSLinus Walleij }, 10425fc537bfSLinus Walleij .probe = mcde_dsi_probe, 10435fc537bfSLinus Walleij .remove = mcde_dsi_remove, 10445fc537bfSLinus Walleij }; 1045