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 * 255fc537bfSLinus Walleij * Memory -> Overlay -> Channel -> FIFO -> 5 formatters -> DSI/DPI 265fc537bfSLinus Walleij * External 0..5 0..3 A,B, 3 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. 315fc537bfSLinus Walleij * 3 of the formatters are for DSI. 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> 665fc537bfSLinus Walleij 675fc537bfSLinus Walleij #include <drm/drm_atomic_helper.h> 685fc537bfSLinus Walleij #include <drm/drm_bridge.h> 695fc537bfSLinus Walleij #include <drm/drm_drv.h> 705fc537bfSLinus Walleij #include <drm/drm_fb_cma_helper.h> 715fc537bfSLinus Walleij #include <drm/drm_fb_helper.h> 725fc537bfSLinus Walleij #include <drm/drm_gem.h> 735fc537bfSLinus Walleij #include <drm/drm_gem_cma_helper.h> 745fc537bfSLinus Walleij #include <drm/drm_gem_framebuffer_helper.h> 75e2edcaaaSDaniel Vetter #include <drm/drm_managed.h> 765fc537bfSLinus Walleij #include <drm/drm_of.h> 775fc537bfSLinus Walleij #include <drm/drm_probe_helper.h> 785fc537bfSLinus Walleij #include <drm/drm_panel.h> 795fc537bfSLinus Walleij #include <drm/drm_vblank.h> 805fc537bfSLinus Walleij 815fc537bfSLinus Walleij #include "mcde_drm.h" 825fc537bfSLinus Walleij 835fc537bfSLinus Walleij #define DRIVER_DESC "DRM module for MCDE" 845fc537bfSLinus Walleij 855fc537bfSLinus Walleij #define MCDE_CR 0x00000000 865fc537bfSLinus Walleij #define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_SHIFT 0 875fc537bfSLinus Walleij #define MCDE_CR_IFIFOEMPTYLINECOUNT_V422_MASK 0x0000003F 885fc537bfSLinus Walleij #define MCDE_CR_IFIFOCTRLEN BIT(15) 895fc537bfSLinus Walleij #define MCDE_CR_UFRECOVERY_MODE_V422 BIT(16) 905fc537bfSLinus Walleij #define MCDE_CR_WRAP_MODE_V422_SHIFT BIT(17) 915fc537bfSLinus Walleij #define MCDE_CR_AUTOCLKG_EN BIT(30) 925fc537bfSLinus Walleij #define MCDE_CR_MCDEEN BIT(31) 935fc537bfSLinus Walleij 945fc537bfSLinus Walleij #define MCDE_CONF0 0x00000004 955fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX0 BIT(0) 965fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX1 BIT(1) 975fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX2 BIT(2) 985fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX3 BIT(3) 995fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX4 BIT(4) 1005fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX5 BIT(5) 1015fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX6 BIT(6) 1025fc537bfSLinus Walleij #define MCDE_CONF0_SYNCMUX7 BIT(7) 1035fc537bfSLinus Walleij #define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT 12 1045fc537bfSLinus Walleij #define MCDE_CONF0_IFIFOCTRLWTRMRKLVL_MASK 0x00007000 1055fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX0_SHIFT 16 1065fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX0_MASK 0x00070000 1075fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX1_SHIFT 19 1085fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX1_MASK 0x00380000 1095fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX2_SHIFT 22 1105fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX2_MASK 0x01C00000 1115fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX3_SHIFT 25 1125fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX3_MASK 0x0E000000 1135fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX4_SHIFT 28 1145fc537bfSLinus Walleij #define MCDE_CONF0_OUTMUX4_MASK 0x70000000 1155fc537bfSLinus Walleij 1165fc537bfSLinus Walleij #define MCDE_SSP 0x00000008 1175fc537bfSLinus Walleij #define MCDE_AIS 0x00000100 1185fc537bfSLinus Walleij #define MCDE_IMSCERR 0x00000110 1195fc537bfSLinus Walleij #define MCDE_RISERR 0x00000120 1205fc537bfSLinus Walleij #define MCDE_MISERR 0x00000130 1215fc537bfSLinus Walleij #define MCDE_SISERR 0x00000140 1225fc537bfSLinus Walleij 1235fc537bfSLinus Walleij #define MCDE_PID 0x000001FC 1245fc537bfSLinus Walleij #define MCDE_PID_METALFIX_VERSION_SHIFT 0 1255fc537bfSLinus Walleij #define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF 1265fc537bfSLinus Walleij #define MCDE_PID_DEVELOPMENT_VERSION_SHIFT 8 1275fc537bfSLinus Walleij #define MCDE_PID_DEVELOPMENT_VERSION_MASK 0x0000FF00 1285fc537bfSLinus Walleij #define MCDE_PID_MINOR_VERSION_SHIFT 16 1295fc537bfSLinus Walleij #define MCDE_PID_MINOR_VERSION_MASK 0x00FF0000 1305fc537bfSLinus Walleij #define MCDE_PID_MAJOR_VERSION_SHIFT 24 1315fc537bfSLinus Walleij #define MCDE_PID_MAJOR_VERSION_MASK 0xFF000000 1325fc537bfSLinus Walleij 1335fc537bfSLinus Walleij static const struct drm_mode_config_funcs mcde_mode_config_funcs = { 1345fc537bfSLinus Walleij .fb_create = drm_gem_fb_create_with_dirty, 1355fc537bfSLinus Walleij .atomic_check = drm_atomic_helper_check, 1365fc537bfSLinus Walleij .atomic_commit = drm_atomic_helper_commit, 1375fc537bfSLinus Walleij }; 1385fc537bfSLinus Walleij 1395fc537bfSLinus Walleij static const struct drm_mode_config_helper_funcs mcde_mode_config_helpers = { 1405fc537bfSLinus Walleij /* 1415fc537bfSLinus Walleij * Using this function is necessary to commit atomic updates 1425fc537bfSLinus Walleij * that need the CRTC to be enabled before a commit, as is 1435fc537bfSLinus Walleij * the case with e.g. DSI displays. 1445fc537bfSLinus Walleij */ 1455fc537bfSLinus Walleij .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 1465fc537bfSLinus Walleij }; 1475fc537bfSLinus Walleij 1485fc537bfSLinus Walleij static irqreturn_t mcde_irq(int irq, void *data) 1495fc537bfSLinus Walleij { 1505fc537bfSLinus Walleij struct mcde *mcde = data; 1515fc537bfSLinus Walleij u32 val; 1525fc537bfSLinus Walleij 1535fc537bfSLinus Walleij val = readl(mcde->regs + MCDE_MISERR); 1545fc537bfSLinus Walleij 1555fc537bfSLinus Walleij mcde_display_irq(mcde); 1565fc537bfSLinus Walleij 1575fc537bfSLinus Walleij if (val) 1585fc537bfSLinus Walleij dev_info(mcde->dev, "some error IRQ\n"); 1595fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_RISERR); 1605fc537bfSLinus Walleij 1615fc537bfSLinus Walleij return IRQ_HANDLED; 1625fc537bfSLinus Walleij } 1635fc537bfSLinus Walleij 1645fc537bfSLinus Walleij static int mcde_modeset_init(struct drm_device *drm) 1655fc537bfSLinus Walleij { 1665fc537bfSLinus Walleij struct drm_mode_config *mode_config; 1675fc537bfSLinus Walleij struct mcde *mcde = drm->dev_private; 1685fc537bfSLinus Walleij int ret; 1695fc537bfSLinus Walleij 1705fc537bfSLinus Walleij if (!mcde->bridge) { 1715fc537bfSLinus Walleij dev_err(drm->dev, "no display output bridge yet\n"); 1725fc537bfSLinus Walleij return -EPROBE_DEFER; 1735fc537bfSLinus Walleij } 1745fc537bfSLinus Walleij 1755fc537bfSLinus Walleij mode_config = &drm->mode_config; 1765fc537bfSLinus Walleij mode_config->funcs = &mcde_mode_config_funcs; 1775fc537bfSLinus Walleij mode_config->helper_private = &mcde_mode_config_helpers; 1785fc537bfSLinus Walleij /* This hardware can do 1080p */ 1795fc537bfSLinus Walleij mode_config->min_width = 1; 1805fc537bfSLinus Walleij mode_config->max_width = 1920; 1815fc537bfSLinus Walleij mode_config->min_height = 1; 1825fc537bfSLinus Walleij mode_config->max_height = 1080; 1835fc537bfSLinus Walleij 1845fc537bfSLinus Walleij ret = drm_vblank_init(drm, 1); 1855fc537bfSLinus Walleij if (ret) { 1865fc537bfSLinus Walleij dev_err(drm->dev, "failed to init vblank\n"); 187*4b055ab1SDaniel Vetter return ret; 1885fc537bfSLinus Walleij } 1895fc537bfSLinus Walleij 1905fc537bfSLinus Walleij ret = mcde_display_init(drm); 1915fc537bfSLinus Walleij if (ret) { 1925fc537bfSLinus Walleij dev_err(drm->dev, "failed to init display\n"); 193*4b055ab1SDaniel Vetter return ret; 1945fc537bfSLinus Walleij } 1955fc537bfSLinus Walleij 1965fc537bfSLinus Walleij /* 1975fc537bfSLinus Walleij * Attach the DSI bridge 1985fc537bfSLinus Walleij * 1995fc537bfSLinus Walleij * TODO: when adding support for the DPI bridge or several DSI bridges, 2005fc537bfSLinus Walleij * we selectively connect the bridge(s) here instead of this simple 2015fc537bfSLinus Walleij * attachment. 2025fc537bfSLinus Walleij */ 2035fc537bfSLinus Walleij ret = drm_simple_display_pipe_attach_bridge(&mcde->pipe, 2045fc537bfSLinus Walleij mcde->bridge); 2055fc537bfSLinus Walleij if (ret) { 2065fc537bfSLinus Walleij dev_err(drm->dev, "failed to attach display output bridge\n"); 207*4b055ab1SDaniel Vetter return ret; 2085fc537bfSLinus Walleij } 2095fc537bfSLinus Walleij 2105fc537bfSLinus Walleij drm_mode_config_reset(drm); 2115fc537bfSLinus Walleij drm_kms_helper_poll_init(drm); 2125fc537bfSLinus Walleij drm_fbdev_generic_setup(drm, 32); 2135fc537bfSLinus Walleij 2145fc537bfSLinus Walleij return 0; 2155fc537bfSLinus Walleij } 2165fc537bfSLinus Walleij 2175fc537bfSLinus Walleij DEFINE_DRM_GEM_CMA_FOPS(drm_fops); 2185fc537bfSLinus Walleij 2195fc537bfSLinus Walleij static struct drm_driver mcde_drm_driver = { 2205fc537bfSLinus Walleij .driver_features = 2210424fdafSDaniel Vetter DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 2225fc537bfSLinus Walleij .lastclose = drm_fb_helper_lastclose, 2235fc537bfSLinus Walleij .ioctls = NULL, 2245fc537bfSLinus Walleij .fops = &drm_fops, 2255fc537bfSLinus Walleij .name = "mcde", 2265fc537bfSLinus Walleij .desc = DRIVER_DESC, 2275fc537bfSLinus Walleij .date = "20180529", 2285fc537bfSLinus Walleij .major = 1, 2295fc537bfSLinus Walleij .minor = 0, 2305fc537bfSLinus Walleij .patchlevel = 0, 2315fc537bfSLinus Walleij .dumb_create = drm_gem_cma_dumb_create, 2325fc537bfSLinus Walleij .gem_free_object_unlocked = drm_gem_cma_free_object, 2335fc537bfSLinus Walleij .gem_vm_ops = &drm_gem_cma_vm_ops, 2345fc537bfSLinus Walleij 2355fc537bfSLinus Walleij .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 2365fc537bfSLinus Walleij .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 2375fc537bfSLinus Walleij .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 2385fc537bfSLinus Walleij .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 2395fc537bfSLinus Walleij .gem_prime_vmap = drm_gem_cma_prime_vmap, 2405fc537bfSLinus Walleij .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 2415fc537bfSLinus Walleij .gem_prime_mmap = drm_gem_cma_prime_mmap, 2425fc537bfSLinus Walleij }; 2435fc537bfSLinus Walleij 2445fc537bfSLinus Walleij static int mcde_drm_bind(struct device *dev) 2455fc537bfSLinus Walleij { 2465fc537bfSLinus Walleij struct drm_device *drm = dev_get_drvdata(dev); 2475fc537bfSLinus Walleij int ret; 2485fc537bfSLinus Walleij 249*4b055ab1SDaniel Vetter ret = drmm_mode_config_init(drm); 250*4b055ab1SDaniel Vetter if (ret) 251*4b055ab1SDaniel Vetter return ret; 2525fc537bfSLinus Walleij 2535fc537bfSLinus Walleij ret = component_bind_all(drm->dev, drm); 2545fc537bfSLinus Walleij if (ret) { 2555fc537bfSLinus Walleij dev_err(dev, "can't bind component devices\n"); 2565fc537bfSLinus Walleij return ret; 2575fc537bfSLinus Walleij } 2585fc537bfSLinus Walleij 2595fc537bfSLinus Walleij ret = mcde_modeset_init(drm); 2605fc537bfSLinus Walleij if (ret) 2615fc537bfSLinus Walleij goto unbind; 2625fc537bfSLinus Walleij 2635fc537bfSLinus Walleij ret = drm_dev_register(drm, 0); 2645fc537bfSLinus Walleij if (ret < 0) 2655fc537bfSLinus Walleij goto unbind; 2665fc537bfSLinus Walleij 2675fc537bfSLinus Walleij return 0; 2685fc537bfSLinus Walleij 2695fc537bfSLinus Walleij unbind: 2705fc537bfSLinus Walleij component_unbind_all(drm->dev, drm); 2715fc537bfSLinus Walleij return ret; 2725fc537bfSLinus Walleij } 2735fc537bfSLinus Walleij 2745fc537bfSLinus Walleij static void mcde_drm_unbind(struct device *dev) 2755fc537bfSLinus Walleij { 2765fc537bfSLinus Walleij struct drm_device *drm = dev_get_drvdata(dev); 2775fc537bfSLinus Walleij 2785fc537bfSLinus Walleij drm_dev_unregister(drm); 2795fc537bfSLinus Walleij drm_atomic_helper_shutdown(drm); 2805fc537bfSLinus Walleij component_unbind_all(drm->dev, drm); 2815fc537bfSLinus Walleij } 2825fc537bfSLinus Walleij 2835fc537bfSLinus Walleij static const struct component_master_ops mcde_drm_comp_ops = { 2845fc537bfSLinus Walleij .bind = mcde_drm_bind, 2855fc537bfSLinus Walleij .unbind = mcde_drm_unbind, 2865fc537bfSLinus Walleij }; 2875fc537bfSLinus Walleij 2885fc537bfSLinus Walleij static struct platform_driver *const mcde_component_drivers[] = { 2895fc537bfSLinus Walleij &mcde_dsi_driver, 2905fc537bfSLinus Walleij }; 2915fc537bfSLinus Walleij 2925fc537bfSLinus Walleij static int mcde_compare_dev(struct device *dev, void *data) 2935fc537bfSLinus Walleij { 2945fc537bfSLinus Walleij return dev == data; 2955fc537bfSLinus Walleij } 2965fc537bfSLinus Walleij 2975fc537bfSLinus Walleij static int mcde_probe(struct platform_device *pdev) 2985fc537bfSLinus Walleij { 2995fc537bfSLinus Walleij struct device *dev = &pdev->dev; 3005fc537bfSLinus Walleij struct drm_device *drm; 3015fc537bfSLinus Walleij struct mcde *mcde; 302ca5be902SLinus Walleij struct component_match *match = NULL; 3035fc537bfSLinus Walleij struct resource *res; 3045fc537bfSLinus Walleij u32 pid; 3055fc537bfSLinus Walleij u32 val; 3065fc537bfSLinus Walleij int irq; 3075fc537bfSLinus Walleij int ret; 3085fc537bfSLinus Walleij int i; 3095fc537bfSLinus Walleij 3105fc537bfSLinus Walleij mcde = kzalloc(sizeof(*mcde), GFP_KERNEL); 3115fc537bfSLinus Walleij if (!mcde) 3125fc537bfSLinus Walleij return -ENOMEM; 3135fc537bfSLinus Walleij mcde->dev = dev; 3145fc537bfSLinus Walleij 3155fc537bfSLinus Walleij ret = drm_dev_init(&mcde->drm, &mcde_drm_driver, dev); 3165fc537bfSLinus Walleij if (ret) { 3175fc537bfSLinus Walleij kfree(mcde); 3185fc537bfSLinus Walleij return ret; 3195fc537bfSLinus Walleij } 3205fc537bfSLinus Walleij drm = &mcde->drm; 3215fc537bfSLinus Walleij drm->dev_private = mcde; 322e2edcaaaSDaniel Vetter drmm_add_final_kfree(drm, mcde); 3235fc537bfSLinus Walleij platform_set_drvdata(pdev, drm); 3245fc537bfSLinus Walleij 3255fc537bfSLinus Walleij /* Enable continuous updates: this is what Linux' framebuffer expects */ 3265fc537bfSLinus Walleij mcde->oneshot_mode = false; 3275fc537bfSLinus Walleij drm->dev_private = mcde; 3285fc537bfSLinus Walleij 3295fc537bfSLinus Walleij /* First obtain and turn on the main power */ 3305fc537bfSLinus Walleij mcde->epod = devm_regulator_get(dev, "epod"); 3315fc537bfSLinus Walleij if (IS_ERR(mcde->epod)) { 3325fc537bfSLinus Walleij ret = PTR_ERR(mcde->epod); 3335fc537bfSLinus Walleij dev_err(dev, "can't get EPOD regulator\n"); 3345fc537bfSLinus Walleij goto dev_unref; 3355fc537bfSLinus Walleij } 3365fc537bfSLinus Walleij ret = regulator_enable(mcde->epod); 3375fc537bfSLinus Walleij if (ret) { 3385fc537bfSLinus Walleij dev_err(dev, "can't enable EPOD regulator\n"); 3395fc537bfSLinus Walleij goto dev_unref; 3405fc537bfSLinus Walleij } 3415fc537bfSLinus Walleij mcde->vana = devm_regulator_get(dev, "vana"); 3425fc537bfSLinus Walleij if (IS_ERR(mcde->vana)) { 3435fc537bfSLinus Walleij ret = PTR_ERR(mcde->vana); 3445fc537bfSLinus Walleij dev_err(dev, "can't get VANA regulator\n"); 3455fc537bfSLinus Walleij goto regulator_epod_off; 3465fc537bfSLinus Walleij } 3475fc537bfSLinus Walleij ret = regulator_enable(mcde->vana); 3485fc537bfSLinus Walleij if (ret) { 3495fc537bfSLinus Walleij dev_err(dev, "can't enable VANA regulator\n"); 3505fc537bfSLinus Walleij goto regulator_epod_off; 3515fc537bfSLinus Walleij } 3525fc537bfSLinus Walleij /* 3535fc537bfSLinus Walleij * The vendor code uses ESRAM (onchip RAM) and need to activate 3545fc537bfSLinus Walleij * the v-esram34 regulator, but we don't use that yet 3555fc537bfSLinus Walleij */ 3565fc537bfSLinus Walleij 3575fc537bfSLinus Walleij /* Clock the silicon so we can access the registers */ 3585fc537bfSLinus Walleij mcde->mcde_clk = devm_clk_get(dev, "mcde"); 3595fc537bfSLinus Walleij if (IS_ERR(mcde->mcde_clk)) { 3605fc537bfSLinus Walleij dev_err(dev, "unable to get MCDE main clock\n"); 3615fc537bfSLinus Walleij ret = PTR_ERR(mcde->mcde_clk); 3625fc537bfSLinus Walleij goto regulator_off; 3635fc537bfSLinus Walleij } 3645fc537bfSLinus Walleij ret = clk_prepare_enable(mcde->mcde_clk); 3655fc537bfSLinus Walleij if (ret) { 3665fc537bfSLinus Walleij dev_err(dev, "failed to enable MCDE main clock\n"); 3675fc537bfSLinus Walleij goto regulator_off; 3685fc537bfSLinus Walleij } 3695fc537bfSLinus Walleij dev_info(dev, "MCDE clk rate %lu Hz\n", clk_get_rate(mcde->mcde_clk)); 3705fc537bfSLinus Walleij 3715fc537bfSLinus Walleij mcde->lcd_clk = devm_clk_get(dev, "lcd"); 3725fc537bfSLinus Walleij if (IS_ERR(mcde->lcd_clk)) { 3735fc537bfSLinus Walleij dev_err(dev, "unable to get LCD clock\n"); 3745fc537bfSLinus Walleij ret = PTR_ERR(mcde->lcd_clk); 3755fc537bfSLinus Walleij goto clk_disable; 3765fc537bfSLinus Walleij } 3775fc537bfSLinus Walleij mcde->hdmi_clk = devm_clk_get(dev, "hdmi"); 3785fc537bfSLinus Walleij if (IS_ERR(mcde->hdmi_clk)) { 3795fc537bfSLinus Walleij dev_err(dev, "unable to get HDMI clock\n"); 3805fc537bfSLinus Walleij ret = PTR_ERR(mcde->hdmi_clk); 3815fc537bfSLinus Walleij goto clk_disable; 3825fc537bfSLinus Walleij } 3835fc537bfSLinus Walleij 3845fc537bfSLinus Walleij res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3855fc537bfSLinus Walleij mcde->regs = devm_ioremap_resource(dev, res); 3865fc537bfSLinus Walleij if (IS_ERR(mcde->regs)) { 3875fc537bfSLinus Walleij dev_err(dev, "no MCDE regs\n"); 3885fc537bfSLinus Walleij ret = -EINVAL; 3895fc537bfSLinus Walleij goto clk_disable; 3905fc537bfSLinus Walleij } 3915fc537bfSLinus Walleij 3925fc537bfSLinus Walleij irq = platform_get_irq(pdev, 0); 3935fc537bfSLinus Walleij if (!irq) { 3945fc537bfSLinus Walleij ret = -EINVAL; 3955fc537bfSLinus Walleij goto clk_disable; 3965fc537bfSLinus Walleij } 3975fc537bfSLinus Walleij 3985fc537bfSLinus Walleij ret = devm_request_irq(dev, irq, mcde_irq, 0, "mcde", mcde); 3995fc537bfSLinus Walleij if (ret) { 4005fc537bfSLinus Walleij dev_err(dev, "failed to request irq %d\n", ret); 4015fc537bfSLinus Walleij goto clk_disable; 4025fc537bfSLinus Walleij } 4035fc537bfSLinus Walleij 4045fc537bfSLinus Walleij /* 4055fc537bfSLinus Walleij * Check hardware revision, we only support U8500v2 version 4065fc537bfSLinus Walleij * as this was the only version used for mass market deployment, 4075fc537bfSLinus Walleij * but surely you can add more versions if you have them and 4085fc537bfSLinus Walleij * need them. 4095fc537bfSLinus Walleij */ 4105fc537bfSLinus Walleij pid = readl(mcde->regs + MCDE_PID); 4115fc537bfSLinus Walleij dev_info(dev, "found MCDE HW revision %d.%d (dev %d, metal fix %d)\n", 4125fc537bfSLinus Walleij (pid & MCDE_PID_MAJOR_VERSION_MASK) 4135fc537bfSLinus Walleij >> MCDE_PID_MAJOR_VERSION_SHIFT, 4145fc537bfSLinus Walleij (pid & MCDE_PID_MINOR_VERSION_MASK) 4155fc537bfSLinus Walleij >> MCDE_PID_MINOR_VERSION_SHIFT, 4165fc537bfSLinus Walleij (pid & MCDE_PID_DEVELOPMENT_VERSION_MASK) 4175fc537bfSLinus Walleij >> MCDE_PID_DEVELOPMENT_VERSION_SHIFT, 4185fc537bfSLinus Walleij (pid & MCDE_PID_METALFIX_VERSION_MASK) 4195fc537bfSLinus Walleij >> MCDE_PID_METALFIX_VERSION_SHIFT); 4205fc537bfSLinus Walleij if (pid != 0x03000800) { 4215fc537bfSLinus Walleij dev_err(dev, "unsupported hardware revision\n"); 4225fc537bfSLinus Walleij ret = -ENODEV; 4235fc537bfSLinus Walleij goto clk_disable; 4245fc537bfSLinus Walleij } 4255fc537bfSLinus Walleij 4265fc537bfSLinus Walleij /* Set up the main control, watermark level at 7 */ 4275fc537bfSLinus Walleij val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT; 4285fc537bfSLinus Walleij /* 24 bits DPI: connect LSB Ch B to D[0:7] */ 4295fc537bfSLinus Walleij val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT; 4305fc537bfSLinus Walleij /* TV out: connect LSB Ch B to D[8:15] */ 4315fc537bfSLinus Walleij val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT; 4325fc537bfSLinus Walleij /* Don't care about this muxing */ 4335fc537bfSLinus Walleij val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT; 4345fc537bfSLinus Walleij /* 24 bits DPI: connect MID Ch B to D[24:31] */ 4355fc537bfSLinus Walleij val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT; 4365fc537bfSLinus Walleij /* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */ 4375fc537bfSLinus Walleij val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT; 4385fc537bfSLinus Walleij /* Syncmux bits zero: DPI channel A and B on output pins A and B resp */ 4395fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_CONF0); 4405fc537bfSLinus Walleij 4415fc537bfSLinus Walleij /* Enable automatic clock gating */ 4425fc537bfSLinus Walleij val = readl(mcde->regs + MCDE_CR); 4435fc537bfSLinus Walleij val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN; 4445fc537bfSLinus Walleij writel(val, mcde->regs + MCDE_CR); 4455fc537bfSLinus Walleij 4465fc537bfSLinus Walleij /* Clear any pending interrupts */ 4475fc537bfSLinus Walleij mcde_display_disable_irqs(mcde); 4485fc537bfSLinus Walleij writel(0, mcde->regs + MCDE_IMSCERR); 4495fc537bfSLinus Walleij writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR); 4505fc537bfSLinus Walleij 4515fc537bfSLinus Walleij /* Spawn child devices for the DSI ports */ 4525fc537bfSLinus Walleij devm_of_platform_populate(dev); 4535fc537bfSLinus Walleij 4545fc537bfSLinus Walleij /* Create something that will match the subdrivers when we bind */ 4555fc537bfSLinus Walleij for (i = 0; i < ARRAY_SIZE(mcde_component_drivers); i++) { 4565fc537bfSLinus Walleij struct device_driver *drv = &mcde_component_drivers[i]->driver; 4575fc537bfSLinus Walleij struct device *p = NULL, *d; 4585fc537bfSLinus Walleij 45936f3313dSSuzuki K Poulose while ((d = platform_find_device_by_driver(p, drv))) { 4605fc537bfSLinus Walleij put_device(p); 4615fc537bfSLinus Walleij component_match_add(dev, &match, mcde_compare_dev, d); 4625fc537bfSLinus Walleij p = d; 4635fc537bfSLinus Walleij } 4645fc537bfSLinus Walleij put_device(p); 4655fc537bfSLinus Walleij } 466ca5be902SLinus Walleij if (!match) { 467ca5be902SLinus Walleij dev_err(dev, "no matching components\n"); 46815c665bbSChristophe JAILLET ret = -ENODEV; 46915c665bbSChristophe JAILLET goto clk_disable; 470ca5be902SLinus Walleij } 4715fc537bfSLinus Walleij if (IS_ERR(match)) { 4725fc537bfSLinus Walleij dev_err(dev, "could not create component match\n"); 4735fc537bfSLinus Walleij ret = PTR_ERR(match); 4745fc537bfSLinus Walleij goto clk_disable; 4755fc537bfSLinus Walleij } 4765fc537bfSLinus Walleij ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops, 4775fc537bfSLinus Walleij match); 4785fc537bfSLinus Walleij if (ret) { 4795fc537bfSLinus Walleij dev_err(dev, "failed to add component master\n"); 4805fc537bfSLinus Walleij goto clk_disable; 4815fc537bfSLinus Walleij } 4825fc537bfSLinus Walleij return 0; 4835fc537bfSLinus Walleij 4845fc537bfSLinus Walleij clk_disable: 4855fc537bfSLinus Walleij clk_disable_unprepare(mcde->mcde_clk); 4865fc537bfSLinus Walleij regulator_off: 4875fc537bfSLinus Walleij regulator_disable(mcde->vana); 4885fc537bfSLinus Walleij regulator_epod_off: 4895fc537bfSLinus Walleij regulator_disable(mcde->epod); 4905fc537bfSLinus Walleij dev_unref: 4915fc537bfSLinus Walleij drm_dev_put(drm); 4925fc537bfSLinus Walleij return ret; 4935fc537bfSLinus Walleij 4945fc537bfSLinus Walleij } 4955fc537bfSLinus Walleij 4965fc537bfSLinus Walleij static int mcde_remove(struct platform_device *pdev) 4975fc537bfSLinus Walleij { 4985fc537bfSLinus Walleij struct drm_device *drm = platform_get_drvdata(pdev); 4995fc537bfSLinus Walleij struct mcde *mcde = drm->dev_private; 5005fc537bfSLinus Walleij 5015fc537bfSLinus Walleij component_master_del(&pdev->dev, &mcde_drm_comp_ops); 5025fc537bfSLinus Walleij clk_disable_unprepare(mcde->mcde_clk); 5035fc537bfSLinus Walleij regulator_disable(mcde->vana); 5045fc537bfSLinus Walleij regulator_disable(mcde->epod); 5055fc537bfSLinus Walleij drm_dev_put(drm); 5065fc537bfSLinus Walleij 5075fc537bfSLinus Walleij return 0; 5085fc537bfSLinus Walleij } 5095fc537bfSLinus Walleij 5105fc537bfSLinus Walleij static const struct of_device_id mcde_of_match[] = { 5115fc537bfSLinus Walleij { 5125fc537bfSLinus Walleij .compatible = "ste,mcde", 5135fc537bfSLinus Walleij }, 5145fc537bfSLinus Walleij {}, 5155fc537bfSLinus Walleij }; 5165fc537bfSLinus Walleij 5175fc537bfSLinus Walleij static struct platform_driver mcde_driver = { 5185fc537bfSLinus Walleij .driver = { 5195fc537bfSLinus Walleij .name = "mcde", 5205fc537bfSLinus Walleij .of_match_table = of_match_ptr(mcde_of_match), 5215fc537bfSLinus Walleij }, 5225fc537bfSLinus Walleij .probe = mcde_probe, 5235fc537bfSLinus Walleij .remove = mcde_remove, 5245fc537bfSLinus Walleij }; 5255fc537bfSLinus Walleij 5265fc537bfSLinus Walleij static struct platform_driver *const component_drivers[] = { 5275fc537bfSLinus Walleij &mcde_dsi_driver, 5285fc537bfSLinus Walleij }; 5295fc537bfSLinus Walleij 5305fc537bfSLinus Walleij static int __init mcde_drm_register(void) 5315fc537bfSLinus Walleij { 5325fc537bfSLinus Walleij int ret; 5335fc537bfSLinus Walleij 5345fc537bfSLinus Walleij ret = platform_register_drivers(component_drivers, 5355fc537bfSLinus Walleij ARRAY_SIZE(component_drivers)); 5365fc537bfSLinus Walleij if (ret) 5375fc537bfSLinus Walleij return ret; 5385fc537bfSLinus Walleij 5395fc537bfSLinus Walleij return platform_driver_register(&mcde_driver); 5405fc537bfSLinus Walleij } 5415fc537bfSLinus Walleij 5425fc537bfSLinus Walleij static void __exit mcde_drm_unregister(void) 5435fc537bfSLinus Walleij { 5445fc537bfSLinus Walleij platform_unregister_drivers(component_drivers, 5455fc537bfSLinus Walleij ARRAY_SIZE(component_drivers)); 5465fc537bfSLinus Walleij platform_driver_unregister(&mcde_driver); 5475fc537bfSLinus Walleij } 5485fc537bfSLinus Walleij 5495fc537bfSLinus Walleij module_init(mcde_drm_register); 5505fc537bfSLinus Walleij module_exit(mcde_drm_unregister); 5515fc537bfSLinus Walleij 5525fc537bfSLinus Walleij MODULE_ALIAS("platform:mcde-drm"); 5535fc537bfSLinus Walleij MODULE_DESCRIPTION(DRIVER_DESC); 5545fc537bfSLinus Walleij MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 5555fc537bfSLinus Walleij MODULE_LICENSE("GPL"); 556