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 85fc537bfSLinus Walleij /** 95fc537bfSLinus Walleij * DOC: ST-Ericsson MCDE Driver 105fc537bfSLinus Walleij * 115fc537bfSLinus Walleij * The MCDE (short for multi-channel display engine) is a graphics 125fc537bfSLinus Walleij * controller found in the Ux500 chipsets, such as NovaThor U8500. 135fc537bfSLinus Walleij * It was initially conceptualized by ST Microelectronics for the 145fc537bfSLinus Walleij * successor of the Nomadik line, STn8500 but productified in the 155fc537bfSLinus Walleij * ST-Ericsson U8500 where is was used for mass-market deployments 165fc537bfSLinus Walleij * in Android phones from Samsung and Sony Ericsson. 175fc537bfSLinus Walleij * 185fc537bfSLinus Walleij * It can do 1080p30 on SDTV CCIR656, DPI-2, DBI-2 or DSI for 195fc537bfSLinus Walleij * panels with or without frame buffering and can convert most 205fc537bfSLinus Walleij * input formats including most variants of RGB and YUV. 215fc537bfSLinus Walleij * 225fc537bfSLinus Walleij * The hardware has four display pipes, and the layout is a little 237f97d3e8SJonathan Neuschäfer * bit like this:: 245fc537bfSLinus Walleij * 25d795fd32SLinus Walleij * Memory -> Overlay -> Channel -> FIFO -> 8 formatters -> DSI/DPI 26d795fd32SLinus Walleij * External 0..5 0..3 A,B, 6 x DSI bridge 275fc537bfSLinus Walleij * source 0..9 C0,C1 2 x DPI 285fc537bfSLinus Walleij * 295fc537bfSLinus Walleij * FIFOs A and B are for LCD and HDMI while FIFO CO/C1 are for 305fc537bfSLinus Walleij * panels with embedded buffer. 31d795fd32SLinus Walleij * 6 of the formatters are for DSI, 3 pairs for VID/CMD respectively. 325fc537bfSLinus Walleij * 2 of the formatters are for DPI. 335fc537bfSLinus Walleij * 345fc537bfSLinus Walleij * Behind the formatters are the DSI or DPI ports that route to 355fc537bfSLinus Walleij * the external pins of the chip. As there are 3 DSI ports and one 365fc537bfSLinus Walleij * DPI port, it is possible to configure up to 4 display pipelines 375fc537bfSLinus Walleij * (effectively using channels 0..3) for concurrent use. 385fc537bfSLinus Walleij * 395fc537bfSLinus Walleij * In the current DRM/KMS setup, we use one external source, one overlay, 405fc537bfSLinus Walleij * one FIFO and one formatter which we connect to the simple CMA framebuffer 415fc537bfSLinus Walleij * helpers. We then provide a bridge to the DSI port, and on the DSI port 425fc537bfSLinus Walleij * bridge we connect hang a panel bridge or other bridge. This may be subject 435fc537bfSLinus Walleij * to change as we exploit more of the hardware capabilities. 445fc537bfSLinus Walleij * 455fc537bfSLinus Walleij * TODO: 467f97d3e8SJonathan Neuschäfer * 475fc537bfSLinus Walleij * - Enabled damaged rectangles using drm_plane_enable_fb_damage_clips() 485fc537bfSLinus Walleij * so we can selectively just transmit the damaged area to a 495fc537bfSLinus Walleij * command-only display. 505fc537bfSLinus Walleij * - Enable mixing of more planes, possibly at the cost of moving away 515fc537bfSLinus Walleij * from using the simple framebuffer pipeline. 525fc537bfSLinus Walleij * - Enable output to bridges such as the AV8100 HDMI encoder from 535fc537bfSLinus Walleij * the DSI bridge. 545fc537bfSLinus Walleij */ 555fc537bfSLinus Walleij 565fc537bfSLinus Walleij #include <linux/clk.h> 575fc537bfSLinus Walleij #include <linux/component.h> 585fc537bfSLinus Walleij #include <linux/dma-buf.h> 595fc537bfSLinus Walleij #include <linux/irq.h> 605fc537bfSLinus Walleij #include <linux/io.h> 615fc537bfSLinus Walleij #include <linux/module.h> 625fc537bfSLinus Walleij #include <linux/of_platform.h> 635fc537bfSLinus Walleij #include <linux/platform_device.h> 645fc537bfSLinus Walleij #include <linux/regulator/consumer.h> 655fc537bfSLinus Walleij #include <linux/slab.h> 66c4842d4dSLinus Walleij #include <linux/delay.h> 675fc537bfSLinus Walleij 685fc537bfSLinus Walleij #include <drm/drm_atomic_helper.h> 695fc537bfSLinus Walleij #include <drm/drm_bridge.h> 705fc537bfSLinus Walleij #include <drm/drm_drv.h> 715fc537bfSLinus Walleij #include <drm/drm_fb_cma_helper.h> 725fc537bfSLinus Walleij #include <drm/drm_fb_helper.h> 735fc537bfSLinus Walleij #include <drm/drm_gem.h> 745fc537bfSLinus Walleij #include <drm/drm_gem_cma_helper.h> 755fc537bfSLinus Walleij #include <drm/drm_gem_framebuffer_helper.h> 76e2edcaaaSDaniel Vetter #include <drm/drm_managed.h> 775fc537bfSLinus Walleij #include <drm/drm_of.h> 785fc537bfSLinus Walleij #include <drm/drm_probe_helper.h> 795fc537bfSLinus Walleij #include <drm/drm_panel.h> 805fc537bfSLinus Walleij #include <drm/drm_vblank.h> 815fc537bfSLinus Walleij 825fc537bfSLinus Walleij #include "mcde_drm.h" 835fc537bfSLinus Walleij 845fc537bfSLinus Walleij #define DRIVER_DESC "DRM module for MCDE" 855fc537bfSLinus Walleij 865fc537bfSLinus Walleij #define MCDE_PID 0x000001FC 875fc537bfSLinus Walleij #define MCDE_PID_METALFIX_VERSION_SHIFT 0 885fc537bfSLinus Walleij #define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF 895fc537bfSLinus Walleij #define MCDE_PID_DEVELOPMENT_VERSION_SHIFT 8 905fc537bfSLinus Walleij #define MCDE_PID_DEVELOPMENT_VERSION_MASK 0x0000FF00 915fc537bfSLinus Walleij #define MCDE_PID_MINOR_VERSION_SHIFT 16 925fc537bfSLinus Walleij #define MCDE_PID_MINOR_VERSION_MASK 0x00FF0000 935fc537bfSLinus Walleij #define MCDE_PID_MAJOR_VERSION_SHIFT 24 945fc537bfSLinus Walleij #define MCDE_PID_MAJOR_VERSION_MASK 0xFF000000 955fc537bfSLinus Walleij 965fc537bfSLinus Walleij static const struct drm_mode_config_funcs mcde_mode_config_funcs = { 975fc537bfSLinus Walleij .fb_create = drm_gem_fb_create_with_dirty, 985fc537bfSLinus Walleij .atomic_check = drm_atomic_helper_check, 995fc537bfSLinus Walleij .atomic_commit = drm_atomic_helper_commit, 1005fc537bfSLinus Walleij }; 1015fc537bfSLinus Walleij 1025fc537bfSLinus Walleij static const struct drm_mode_config_helper_funcs mcde_mode_config_helpers = { 1035fc537bfSLinus Walleij /* 1045fc537bfSLinus Walleij * Using this function is necessary to commit atomic updates 1055fc537bfSLinus Walleij * that need the CRTC to be enabled before a commit, as is 1065fc537bfSLinus Walleij * the case with e.g. DSI displays. 1075fc537bfSLinus Walleij */ 1085fc537bfSLinus Walleij .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 1095fc537bfSLinus Walleij }; 1105fc537bfSLinus Walleij 1115fc537bfSLinus Walleij static irqreturn_t mcde_irq(int irq, void *data) 1125fc537bfSLinus Walleij { 1135fc537bfSLinus Walleij struct mcde *mcde = data; 1145fc537bfSLinus Walleij u32 val; 1155fc537bfSLinus Walleij 1165fc537bfSLinus Walleij val = readl(mcde->regs + MCDE_MISERR); 1175fc537bfSLinus Walleij 1185fc537bfSLinus Walleij mcde_display_irq(mcde); 1195fc537bfSLinus Walleij 1205fc537bfSLinus Walleij if (val) 1215fc537bfSLinus Walleij dev_info(mcde->dev, "some error IRQ\n"); 1225fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_RISERR); 1235fc537bfSLinus Walleij 1245fc537bfSLinus Walleij return IRQ_HANDLED; 1255fc537bfSLinus Walleij } 1265fc537bfSLinus Walleij 1275fc537bfSLinus Walleij static int mcde_modeset_init(struct drm_device *drm) 1285fc537bfSLinus Walleij { 1295fc537bfSLinus Walleij struct drm_mode_config *mode_config; 130fd7ee85cSDaniel Vetter struct mcde *mcde = to_mcde(drm); 1315fc537bfSLinus Walleij int ret; 1325fc537bfSLinus Walleij 133d795fd32SLinus Walleij /* 134d795fd32SLinus Walleij * If no other bridge was found, check if we have a DPI panel or 135d795fd32SLinus Walleij * any other bridge connected directly to the MCDE DPI output. 136d795fd32SLinus Walleij * If a DSI bridge is found, DSI will take precedence. 137d795fd32SLinus Walleij * 138d795fd32SLinus Walleij * TODO: more elaborate bridge selection if we have more than one 139d795fd32SLinus Walleij * thing attached to the system. 140d795fd32SLinus Walleij */ 1415fc537bfSLinus Walleij if (!mcde->bridge) { 142d795fd32SLinus Walleij struct drm_panel *panel; 143d795fd32SLinus Walleij struct drm_bridge *bridge; 144d795fd32SLinus Walleij 145d795fd32SLinus Walleij ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 146d795fd32SLinus Walleij 0, 0, &panel, &bridge); 147d795fd32SLinus Walleij if (ret) { 148d795fd32SLinus Walleij dev_err(drm->dev, 149d795fd32SLinus Walleij "Could not locate any output bridge or panel\n"); 150d795fd32SLinus Walleij return ret; 151d795fd32SLinus Walleij } 152d795fd32SLinus Walleij if (panel) { 153d795fd32SLinus Walleij bridge = drm_panel_bridge_add_typed(panel, 154d795fd32SLinus Walleij DRM_MODE_CONNECTOR_DPI); 155d795fd32SLinus Walleij if (IS_ERR(bridge)) { 156d795fd32SLinus Walleij dev_err(drm->dev, 157d795fd32SLinus Walleij "Could not connect panel bridge\n"); 158d795fd32SLinus Walleij return PTR_ERR(bridge); 159d795fd32SLinus Walleij } 160d795fd32SLinus Walleij } 161d795fd32SLinus Walleij mcde->dpi_output = true; 162d795fd32SLinus Walleij mcde->bridge = bridge; 163d795fd32SLinus Walleij mcde->flow_mode = MCDE_DPI_FORMATTER_FLOW; 1645fc537bfSLinus Walleij } 1655fc537bfSLinus Walleij 1665fc537bfSLinus Walleij mode_config = &drm->mode_config; 1675fc537bfSLinus Walleij mode_config->funcs = &mcde_mode_config_funcs; 1685fc537bfSLinus Walleij mode_config->helper_private = &mcde_mode_config_helpers; 1695fc537bfSLinus Walleij /* This hardware can do 1080p */ 1705fc537bfSLinus Walleij mode_config->min_width = 1; 1715fc537bfSLinus Walleij mode_config->max_width = 1920; 1725fc537bfSLinus Walleij mode_config->min_height = 1; 1735fc537bfSLinus Walleij mode_config->max_height = 1080; 1745fc537bfSLinus Walleij 1755fc537bfSLinus Walleij ret = drm_vblank_init(drm, 1); 1765fc537bfSLinus Walleij if (ret) { 1775fc537bfSLinus Walleij dev_err(drm->dev, "failed to init vblank\n"); 1784b055ab1SDaniel Vetter return ret; 1795fc537bfSLinus Walleij } 1805fc537bfSLinus Walleij 1815fc537bfSLinus Walleij ret = mcde_display_init(drm); 1825fc537bfSLinus Walleij if (ret) { 1835fc537bfSLinus Walleij dev_err(drm->dev, "failed to init display\n"); 1844b055ab1SDaniel Vetter return ret; 1855fc537bfSLinus Walleij } 1865fc537bfSLinus Walleij 187d795fd32SLinus Walleij /* Attach the bridge. */ 1885fc537bfSLinus Walleij ret = drm_simple_display_pipe_attach_bridge(&mcde->pipe, 1895fc537bfSLinus Walleij mcde->bridge); 1905fc537bfSLinus Walleij if (ret) { 1915fc537bfSLinus Walleij dev_err(drm->dev, "failed to attach display output bridge\n"); 1924b055ab1SDaniel Vetter return ret; 1935fc537bfSLinus Walleij } 1945fc537bfSLinus Walleij 1955fc537bfSLinus Walleij drm_mode_config_reset(drm); 1965fc537bfSLinus Walleij drm_kms_helper_poll_init(drm); 1975fc537bfSLinus Walleij 1985fc537bfSLinus Walleij return 0; 1995fc537bfSLinus Walleij } 2005fc537bfSLinus Walleij 2015fc537bfSLinus Walleij DEFINE_DRM_GEM_CMA_FOPS(drm_fops); 2025fc537bfSLinus Walleij 20370a59dd8SDaniel Vetter static const struct drm_driver mcde_drm_driver = { 2045fc537bfSLinus Walleij .driver_features = 2050424fdafSDaniel Vetter DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 2065fc537bfSLinus Walleij .lastclose = drm_fb_helper_lastclose, 2075fc537bfSLinus Walleij .ioctls = NULL, 2085fc537bfSLinus Walleij .fops = &drm_fops, 2095fc537bfSLinus Walleij .name = "mcde", 2105fc537bfSLinus Walleij .desc = DRIVER_DESC, 2115fc537bfSLinus Walleij .date = "20180529", 2125fc537bfSLinus Walleij .major = 1, 2135fc537bfSLinus Walleij .minor = 0, 2145fc537bfSLinus Walleij .patchlevel = 0, 21547d22f25SThomas Zimmermann DRM_GEM_CMA_DRIVER_OPS, 2165fc537bfSLinus Walleij }; 2175fc537bfSLinus Walleij 2185fc537bfSLinus Walleij static int mcde_drm_bind(struct device *dev) 2195fc537bfSLinus Walleij { 2205fc537bfSLinus Walleij struct drm_device *drm = dev_get_drvdata(dev); 2215fc537bfSLinus Walleij int ret; 2225fc537bfSLinus Walleij 2234b055ab1SDaniel Vetter ret = drmm_mode_config_init(drm); 2244b055ab1SDaniel Vetter if (ret) 2254b055ab1SDaniel Vetter return ret; 2265fc537bfSLinus Walleij 2275fc537bfSLinus Walleij ret = component_bind_all(drm->dev, drm); 2285fc537bfSLinus Walleij if (ret) { 2295fc537bfSLinus Walleij dev_err(dev, "can't bind component devices\n"); 2305fc537bfSLinus Walleij return ret; 2315fc537bfSLinus Walleij } 2325fc537bfSLinus Walleij 2335fc537bfSLinus Walleij ret = mcde_modeset_init(drm); 2345fc537bfSLinus Walleij if (ret) 2355fc537bfSLinus Walleij goto unbind; 2365fc537bfSLinus Walleij 2375fc537bfSLinus Walleij ret = drm_dev_register(drm, 0); 2385fc537bfSLinus Walleij if (ret < 0) 2395fc537bfSLinus Walleij goto unbind; 2405fc537bfSLinus Walleij 241b984b6d8SLinus Walleij drm_fbdev_generic_setup(drm, 32); 242b984b6d8SLinus Walleij 2435fc537bfSLinus Walleij return 0; 2445fc537bfSLinus Walleij 2455fc537bfSLinus Walleij unbind: 2465fc537bfSLinus Walleij component_unbind_all(drm->dev, drm); 2475fc537bfSLinus Walleij return ret; 2485fc537bfSLinus Walleij } 2495fc537bfSLinus Walleij 2505fc537bfSLinus Walleij static void mcde_drm_unbind(struct device *dev) 2515fc537bfSLinus Walleij { 2525fc537bfSLinus Walleij struct drm_device *drm = dev_get_drvdata(dev); 2535fc537bfSLinus Walleij 2545fc537bfSLinus Walleij drm_dev_unregister(drm); 2555fc537bfSLinus Walleij drm_atomic_helper_shutdown(drm); 2565fc537bfSLinus Walleij component_unbind_all(drm->dev, drm); 2575fc537bfSLinus Walleij } 2585fc537bfSLinus Walleij 2595fc537bfSLinus Walleij static const struct component_master_ops mcde_drm_comp_ops = { 2605fc537bfSLinus Walleij .bind = mcde_drm_bind, 2615fc537bfSLinus Walleij .unbind = mcde_drm_unbind, 2625fc537bfSLinus Walleij }; 2635fc537bfSLinus Walleij 2645fc537bfSLinus Walleij static struct platform_driver *const mcde_component_drivers[] = { 2655fc537bfSLinus Walleij &mcde_dsi_driver, 2665fc537bfSLinus Walleij }; 2675fc537bfSLinus Walleij 2685fc537bfSLinus Walleij static int mcde_probe(struct platform_device *pdev) 2695fc537bfSLinus Walleij { 2705fc537bfSLinus Walleij struct device *dev = &pdev->dev; 2715fc537bfSLinus Walleij struct drm_device *drm; 2725fc537bfSLinus Walleij struct mcde *mcde; 273ca5be902SLinus Walleij struct component_match *match = NULL; 2745fc537bfSLinus Walleij u32 pid; 2755fc537bfSLinus Walleij int irq; 2765fc537bfSLinus Walleij int ret; 2775fc537bfSLinus Walleij int i; 2785fc537bfSLinus Walleij 2796ff71edfSDaniel Vetter mcde = devm_drm_dev_alloc(dev, &mcde_drm_driver, struct mcde, drm); 2806ff71edfSDaniel Vetter if (IS_ERR(mcde)) 2816ff71edfSDaniel Vetter return PTR_ERR(mcde); 2825fc537bfSLinus Walleij drm = &mcde->drm; 2836ff71edfSDaniel Vetter mcde->dev = dev; 2845fc537bfSLinus Walleij platform_set_drvdata(pdev, drm); 2855fc537bfSLinus Walleij 2865fc537bfSLinus Walleij /* First obtain and turn on the main power */ 2875fc537bfSLinus Walleij mcde->epod = devm_regulator_get(dev, "epod"); 2885fc537bfSLinus Walleij if (IS_ERR(mcde->epod)) { 2895fc537bfSLinus Walleij ret = PTR_ERR(mcde->epod); 2905fc537bfSLinus Walleij dev_err(dev, "can't get EPOD regulator\n"); 29190c2e13bSDaniel Vetter return ret; 2925fc537bfSLinus Walleij } 2935fc537bfSLinus Walleij ret = regulator_enable(mcde->epod); 2945fc537bfSLinus Walleij if (ret) { 2955fc537bfSLinus Walleij dev_err(dev, "can't enable EPOD regulator\n"); 29690c2e13bSDaniel Vetter return ret; 2975fc537bfSLinus Walleij } 2985fc537bfSLinus Walleij mcde->vana = devm_regulator_get(dev, "vana"); 2995fc537bfSLinus Walleij if (IS_ERR(mcde->vana)) { 3005fc537bfSLinus Walleij ret = PTR_ERR(mcde->vana); 3015fc537bfSLinus Walleij dev_err(dev, "can't get VANA regulator\n"); 3025fc537bfSLinus Walleij goto regulator_epod_off; 3035fc537bfSLinus Walleij } 3045fc537bfSLinus Walleij ret = regulator_enable(mcde->vana); 3055fc537bfSLinus Walleij if (ret) { 3065fc537bfSLinus Walleij dev_err(dev, "can't enable VANA regulator\n"); 3075fc537bfSLinus Walleij goto regulator_epod_off; 3085fc537bfSLinus Walleij } 3095fc537bfSLinus Walleij /* 3105fc537bfSLinus Walleij * The vendor code uses ESRAM (onchip RAM) and need to activate 3115fc537bfSLinus Walleij * the v-esram34 regulator, but we don't use that yet 3125fc537bfSLinus Walleij */ 3135fc537bfSLinus Walleij 3145fc537bfSLinus Walleij /* Clock the silicon so we can access the registers */ 3155fc537bfSLinus Walleij mcde->mcde_clk = devm_clk_get(dev, "mcde"); 3165fc537bfSLinus Walleij if (IS_ERR(mcde->mcde_clk)) { 3175fc537bfSLinus Walleij dev_err(dev, "unable to get MCDE main clock\n"); 3185fc537bfSLinus Walleij ret = PTR_ERR(mcde->mcde_clk); 3195fc537bfSLinus Walleij goto regulator_off; 3205fc537bfSLinus Walleij } 3215fc537bfSLinus Walleij ret = clk_prepare_enable(mcde->mcde_clk); 3225fc537bfSLinus Walleij if (ret) { 3235fc537bfSLinus Walleij dev_err(dev, "failed to enable MCDE main clock\n"); 3245fc537bfSLinus Walleij goto regulator_off; 3255fc537bfSLinus Walleij } 3265fc537bfSLinus Walleij dev_info(dev, "MCDE clk rate %lu Hz\n", clk_get_rate(mcde->mcde_clk)); 3275fc537bfSLinus Walleij 3285fc537bfSLinus Walleij mcde->lcd_clk = devm_clk_get(dev, "lcd"); 3295fc537bfSLinus Walleij if (IS_ERR(mcde->lcd_clk)) { 3305fc537bfSLinus Walleij dev_err(dev, "unable to get LCD clock\n"); 3315fc537bfSLinus Walleij ret = PTR_ERR(mcde->lcd_clk); 3325fc537bfSLinus Walleij goto clk_disable; 3335fc537bfSLinus Walleij } 3345fc537bfSLinus Walleij mcde->hdmi_clk = devm_clk_get(dev, "hdmi"); 3355fc537bfSLinus Walleij if (IS_ERR(mcde->hdmi_clk)) { 3365fc537bfSLinus Walleij dev_err(dev, "unable to get HDMI clock\n"); 3375fc537bfSLinus Walleij ret = PTR_ERR(mcde->hdmi_clk); 3385fc537bfSLinus Walleij goto clk_disable; 3395fc537bfSLinus Walleij } 3405fc537bfSLinus Walleij 341c5baa944SCai Huoqing mcde->regs = devm_platform_ioremap_resource(pdev, 0); 3425fc537bfSLinus Walleij if (IS_ERR(mcde->regs)) { 3435fc537bfSLinus Walleij dev_err(dev, "no MCDE regs\n"); 3445fc537bfSLinus Walleij ret = -EINVAL; 3455fc537bfSLinus Walleij goto clk_disable; 3465fc537bfSLinus Walleij } 3475fc537bfSLinus Walleij 3485fc537bfSLinus Walleij irq = platform_get_irq(pdev, 0); 349e2dae672SKrzysztof Kozlowski if (irq < 0) { 350e2dae672SKrzysztof Kozlowski ret = irq; 3515fc537bfSLinus Walleij goto clk_disable; 3525fc537bfSLinus Walleij } 3535fc537bfSLinus Walleij 3545fc537bfSLinus Walleij ret = devm_request_irq(dev, irq, mcde_irq, 0, "mcde", mcde); 3555fc537bfSLinus Walleij if (ret) { 3565fc537bfSLinus Walleij dev_err(dev, "failed to request irq %d\n", ret); 3575fc537bfSLinus Walleij goto clk_disable; 3585fc537bfSLinus Walleij } 3595fc537bfSLinus Walleij 3605fc537bfSLinus Walleij /* 3615fc537bfSLinus Walleij * Check hardware revision, we only support U8500v2 version 3625fc537bfSLinus Walleij * as this was the only version used for mass market deployment, 3635fc537bfSLinus Walleij * but surely you can add more versions if you have them and 3645fc537bfSLinus Walleij * need them. 3655fc537bfSLinus Walleij */ 3665fc537bfSLinus Walleij pid = readl(mcde->regs + MCDE_PID); 3675fc537bfSLinus Walleij dev_info(dev, "found MCDE HW revision %d.%d (dev %d, metal fix %d)\n", 3685fc537bfSLinus Walleij (pid & MCDE_PID_MAJOR_VERSION_MASK) 3695fc537bfSLinus Walleij >> MCDE_PID_MAJOR_VERSION_SHIFT, 3705fc537bfSLinus Walleij (pid & MCDE_PID_MINOR_VERSION_MASK) 3715fc537bfSLinus Walleij >> MCDE_PID_MINOR_VERSION_SHIFT, 3725fc537bfSLinus Walleij (pid & MCDE_PID_DEVELOPMENT_VERSION_MASK) 3735fc537bfSLinus Walleij >> MCDE_PID_DEVELOPMENT_VERSION_SHIFT, 3745fc537bfSLinus Walleij (pid & MCDE_PID_METALFIX_VERSION_MASK) 3755fc537bfSLinus Walleij >> MCDE_PID_METALFIX_VERSION_SHIFT); 3765fc537bfSLinus Walleij if (pid != 0x03000800) { 3775fc537bfSLinus Walleij dev_err(dev, "unsupported hardware revision\n"); 3785fc537bfSLinus Walleij ret = -ENODEV; 3795fc537bfSLinus Walleij goto clk_disable; 3805fc537bfSLinus Walleij } 3815fc537bfSLinus Walleij 382c4842d4dSLinus Walleij /* Disable and clear any pending interrupts */ 3835fc537bfSLinus Walleij mcde_display_disable_irqs(mcde); 3845fc537bfSLinus Walleij writel(0, mcde->regs + MCDE_IMSCERR); 3855fc537bfSLinus Walleij writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR); 3865fc537bfSLinus Walleij 3875fc537bfSLinus Walleij /* Spawn child devices for the DSI ports */ 3885fc537bfSLinus Walleij devm_of_platform_populate(dev); 3895fc537bfSLinus Walleij 3905fc537bfSLinus Walleij /* Create something that will match the subdrivers when we bind */ 3915fc537bfSLinus Walleij for (i = 0; i < ARRAY_SIZE(mcde_component_drivers); i++) { 3925fc537bfSLinus Walleij struct device_driver *drv = &mcde_component_drivers[i]->driver; 3935fc537bfSLinus Walleij struct device *p = NULL, *d; 3945fc537bfSLinus Walleij 39536f3313dSSuzuki K Poulose while ((d = platform_find_device_by_driver(p, drv))) { 3965fc537bfSLinus Walleij put_device(p); 397*f8b3f5e8SYong Wu component_match_add(dev, &match, component_compare_dev, d); 3985fc537bfSLinus Walleij p = d; 3995fc537bfSLinus Walleij } 4005fc537bfSLinus Walleij put_device(p); 4015fc537bfSLinus Walleij } 402ca5be902SLinus Walleij if (!match) { 403ca5be902SLinus Walleij dev_err(dev, "no matching components\n"); 40415c665bbSChristophe JAILLET ret = -ENODEV; 40515c665bbSChristophe JAILLET goto clk_disable; 406ca5be902SLinus Walleij } 4075fc537bfSLinus Walleij if (IS_ERR(match)) { 4085fc537bfSLinus Walleij dev_err(dev, "could not create component match\n"); 4095fc537bfSLinus Walleij ret = PTR_ERR(match); 4105fc537bfSLinus Walleij goto clk_disable; 4115fc537bfSLinus Walleij } 412c4842d4dSLinus Walleij 413c4842d4dSLinus Walleij /* 414c4842d4dSLinus Walleij * Perform an invasive reset of the MCDE and all blocks by 415c4842d4dSLinus Walleij * cutting the power to the subsystem, then bring it back up 416c4842d4dSLinus Walleij * later when we enable the display as a result of 417c4842d4dSLinus Walleij * component_master_add_with_match(). 418c4842d4dSLinus Walleij */ 419c4842d4dSLinus Walleij ret = regulator_disable(mcde->epod); 420c4842d4dSLinus Walleij if (ret) { 421c4842d4dSLinus Walleij dev_err(dev, "can't disable EPOD regulator\n"); 422c4842d4dSLinus Walleij return ret; 423c4842d4dSLinus Walleij } 424c4842d4dSLinus Walleij /* Wait 50 ms so we are sure we cut the power */ 425c4842d4dSLinus Walleij usleep_range(50000, 70000); 426c4842d4dSLinus Walleij 4275fc537bfSLinus Walleij ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops, 4285fc537bfSLinus Walleij match); 4295fc537bfSLinus Walleij if (ret) { 4305fc537bfSLinus Walleij dev_err(dev, "failed to add component master\n"); 431a6c40b80SLinus Walleij /* 432a6c40b80SLinus Walleij * The EPOD regulator is already disabled at this point so some 433a6c40b80SLinus Walleij * special errorpath code is needed 434a6c40b80SLinus Walleij */ 435a6c40b80SLinus Walleij clk_disable_unprepare(mcde->mcde_clk); 436a6c40b80SLinus Walleij regulator_disable(mcde->vana); 437a6c40b80SLinus Walleij return ret; 4385fc537bfSLinus Walleij } 439c4842d4dSLinus Walleij 4405fc537bfSLinus Walleij return 0; 4415fc537bfSLinus Walleij 4425fc537bfSLinus Walleij clk_disable: 4435fc537bfSLinus Walleij clk_disable_unprepare(mcde->mcde_clk); 4445fc537bfSLinus Walleij regulator_off: 4455fc537bfSLinus Walleij regulator_disable(mcde->vana); 4465fc537bfSLinus Walleij regulator_epod_off: 4475fc537bfSLinus Walleij regulator_disable(mcde->epod); 4485fc537bfSLinus Walleij return ret; 4495fc537bfSLinus Walleij 4505fc537bfSLinus Walleij } 4515fc537bfSLinus Walleij 4525fc537bfSLinus Walleij static int mcde_remove(struct platform_device *pdev) 4535fc537bfSLinus Walleij { 4545fc537bfSLinus Walleij struct drm_device *drm = platform_get_drvdata(pdev); 455fd7ee85cSDaniel Vetter struct mcde *mcde = to_mcde(drm); 4565fc537bfSLinus Walleij 4575fc537bfSLinus Walleij component_master_del(&pdev->dev, &mcde_drm_comp_ops); 4585fc537bfSLinus Walleij clk_disable_unprepare(mcde->mcde_clk); 4595fc537bfSLinus Walleij regulator_disable(mcde->vana); 4605fc537bfSLinus Walleij regulator_disable(mcde->epod); 4615fc537bfSLinus Walleij 4625fc537bfSLinus Walleij return 0; 4635fc537bfSLinus Walleij } 4645fc537bfSLinus Walleij 4655fc537bfSLinus Walleij static const struct of_device_id mcde_of_match[] = { 4665fc537bfSLinus Walleij { 4675fc537bfSLinus Walleij .compatible = "ste,mcde", 4685fc537bfSLinus Walleij }, 4695fc537bfSLinus Walleij {}, 4705fc537bfSLinus Walleij }; 4715fc537bfSLinus Walleij 4725fc537bfSLinus Walleij static struct platform_driver mcde_driver = { 4735fc537bfSLinus Walleij .driver = { 4745fc537bfSLinus Walleij .name = "mcde", 4755fc537bfSLinus Walleij .of_match_table = of_match_ptr(mcde_of_match), 4765fc537bfSLinus Walleij }, 4775fc537bfSLinus Walleij .probe = mcde_probe, 4785fc537bfSLinus Walleij .remove = mcde_remove, 4795fc537bfSLinus Walleij }; 4805fc537bfSLinus Walleij 4815fc537bfSLinus Walleij static struct platform_driver *const component_drivers[] = { 4825fc537bfSLinus Walleij &mcde_dsi_driver, 4835fc537bfSLinus Walleij }; 4845fc537bfSLinus Walleij 4855fc537bfSLinus Walleij static int __init mcde_drm_register(void) 4865fc537bfSLinus Walleij { 4875fc537bfSLinus Walleij int ret; 4885fc537bfSLinus Walleij 4895fc537bfSLinus Walleij ret = platform_register_drivers(component_drivers, 4905fc537bfSLinus Walleij ARRAY_SIZE(component_drivers)); 4915fc537bfSLinus Walleij if (ret) 4925fc537bfSLinus Walleij return ret; 4935fc537bfSLinus Walleij 4945fc537bfSLinus Walleij return platform_driver_register(&mcde_driver); 4955fc537bfSLinus Walleij } 4965fc537bfSLinus Walleij 4975fc537bfSLinus Walleij static void __exit mcde_drm_unregister(void) 4985fc537bfSLinus Walleij { 4995fc537bfSLinus Walleij platform_unregister_drivers(component_drivers, 5005fc537bfSLinus Walleij ARRAY_SIZE(component_drivers)); 5015fc537bfSLinus Walleij platform_driver_unregister(&mcde_driver); 5025fc537bfSLinus Walleij } 5035fc537bfSLinus Walleij 5045fc537bfSLinus Walleij module_init(mcde_drm_register); 5055fc537bfSLinus Walleij module_exit(mcde_drm_unregister); 5065fc537bfSLinus Walleij 5075fc537bfSLinus Walleij MODULE_ALIAS("platform:mcde-drm"); 5085fc537bfSLinus Walleij MODULE_DESCRIPTION(DRIVER_DESC); 5095fc537bfSLinus Walleij MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 5105fc537bfSLinus Walleij MODULE_LICENSE("GPL"); 511