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