1 /* 2 * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. 3 * 4 * Parts of this file were based on sources as follows: 5 * 6 * Copyright (c) 2006-2008 Intel Corporation 7 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 8 * Copyright (C) 2011 Texas Instruments 9 * 10 * This program is free software and is provided to you under the terms of the 11 * GNU General Public License version 2 as published by the Free Software 12 * Foundation, and any use by you of this program is subject to the terms of 13 * such GNU licence. 14 * 15 */ 16 17 /** 18 * DOC: ARM PrimeCell PL111 CLCD Driver 19 * 20 * The PL111 is a simple LCD controller that can support TFT and STN 21 * displays. This driver exposes a standard KMS interface for them. 22 * 23 * This driver uses the same Device Tree binding as the fbdev CLCD 24 * driver. While the fbdev driver supports panels that may be 25 * connected to the CLCD internally to the CLCD driver, in DRM the 26 * panels get split out to drivers/gpu/drm/panels/. This means that, 27 * in converting from using fbdev to using DRM, you also need to write 28 * a panel driver (which may be as simple as an entry in 29 * panel-simple.c). 30 * 31 * The driver currently doesn't expose the cursor. The DRM API for 32 * cursors requires support for 64x64 ARGB8888 cursor images, while 33 * the hardware can only support 64x64 monochrome with masking 34 * cursors. While one could imagine trying to hack something together 35 * to look at the ARGB8888 and program reasonable in monochrome, we 36 * just don't expose the cursor at all instead, and leave cursor 37 * support to the X11 software cursor layer. 38 * 39 * TODO: 40 * 41 * - Fix race between setting plane base address and getting IRQ for 42 * vsync firing the pageflip completion. 43 * 44 * - Expose the correct set of formats we can support based on the 45 * "arm,pl11x,tft-r0g0b0-pads" DT property. 46 * 47 * - Use the "max-memory-bandwidth" DT property to filter the 48 * supported formats. 49 * 50 * - Read back hardware state at boot to skip reprogramming the 51 * hardware when doing a no-op modeset. 52 * 53 * - Use the internal clock divisor to reduce power consumption by 54 * using HCLK (apb_pclk) when appropriate. 55 */ 56 57 #include <linux/amba/bus.h> 58 #include <linux/amba/clcd-regs.h> 59 #include <linux/version.h> 60 #include <linux/shmem_fs.h> 61 #include <linux/dma-buf.h> 62 #include <linux/module.h> 63 #include <linux/slab.h> 64 65 #include <drm/drmP.h> 66 #include <drm/drm_atomic_helper.h> 67 #include <drm/drm_crtc_helper.h> 68 #include <drm/drm_gem_cma_helper.h> 69 #include <drm/drm_fb_cma_helper.h> 70 71 #include "pl111_drm.h" 72 73 #define DRIVER_DESC "DRM module for PL111" 74 75 struct drm_mode_config_funcs mode_config_funcs = { 76 .fb_create = drm_fb_cma_create, 77 .atomic_check = drm_atomic_helper_check, 78 .atomic_commit = drm_atomic_helper_commit, 79 }; 80 81 static int pl111_modeset_init(struct drm_device *dev) 82 { 83 struct drm_mode_config *mode_config; 84 struct pl111_drm_dev_private *priv = dev->dev_private; 85 int ret = 0; 86 87 drm_mode_config_init(dev); 88 mode_config = &dev->mode_config; 89 mode_config->funcs = &mode_config_funcs; 90 mode_config->min_width = 1; 91 mode_config->max_width = 1024; 92 mode_config->min_height = 1; 93 mode_config->max_height = 768; 94 95 ret = pl111_connector_init(dev); 96 if (ret) { 97 dev_err(dev->dev, "Failed to create pl111_drm_connector\n"); 98 goto out_config; 99 } 100 101 /* Don't actually attach if we didn't find a drm_panel 102 * attached to us. This will allow a kernel to include both 103 * the fbdev pl111 driver and this one, and choose between 104 * them based on which subsystem has support for the panel. 105 */ 106 if (!priv->connector.panel) { 107 dev_info(dev->dev, 108 "Disabling due to lack of DRM panel device.\n"); 109 ret = -ENODEV; 110 goto out_config; 111 } 112 113 ret = pl111_display_init(dev); 114 if (ret != 0) { 115 dev_err(dev->dev, "Failed to init display\n"); 116 goto out_config; 117 } 118 119 ret = drm_vblank_init(dev, 1); 120 if (ret != 0) { 121 dev_err(dev->dev, "Failed to init vblank\n"); 122 goto out_config; 123 } 124 125 drm_mode_config_reset(dev); 126 127 priv->fbdev = drm_fbdev_cma_init(dev, 32, 128 dev->mode_config.num_connector); 129 130 drm_kms_helper_poll_init(dev); 131 132 goto finish; 133 134 out_config: 135 drm_mode_config_cleanup(dev); 136 finish: 137 return ret; 138 } 139 140 DEFINE_DRM_GEM_CMA_FOPS(drm_fops); 141 142 static void pl111_lastclose(struct drm_device *dev) 143 { 144 struct pl111_drm_dev_private *priv = dev->dev_private; 145 146 drm_fbdev_cma_restore_mode(priv->fbdev); 147 } 148 149 static struct drm_driver pl111_drm_driver = { 150 .driver_features = 151 DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, 152 .lastclose = pl111_lastclose, 153 .ioctls = NULL, 154 .fops = &drm_fops, 155 .name = "pl111", 156 .desc = DRIVER_DESC, 157 .date = "20170317", 158 .major = 1, 159 .minor = 0, 160 .patchlevel = 0, 161 .dumb_create = drm_gem_cma_dumb_create, 162 .dumb_destroy = drm_gem_dumb_destroy, 163 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 164 .gem_free_object = drm_gem_cma_free_object, 165 .gem_vm_ops = &drm_gem_cma_vm_ops, 166 167 .enable_vblank = pl111_enable_vblank, 168 .disable_vblank = pl111_disable_vblank, 169 170 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 171 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 172 .gem_prime_import = drm_gem_prime_import, 173 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 174 .gem_prime_export = drm_gem_prime_export, 175 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 176 }; 177 178 #ifdef CONFIG_ARM_AMBA 179 static int pl111_amba_probe(struct amba_device *amba_dev, 180 const struct amba_id *id) 181 { 182 struct device *dev = &amba_dev->dev; 183 struct pl111_drm_dev_private *priv; 184 struct drm_device *drm; 185 int ret; 186 187 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 188 if (!priv) 189 return -ENOMEM; 190 191 drm = drm_dev_alloc(&pl111_drm_driver, dev); 192 if (IS_ERR(drm)) 193 return PTR_ERR(drm); 194 amba_set_drvdata(amba_dev, drm); 195 priv->drm = drm; 196 drm->dev_private = priv; 197 198 priv->clk = devm_clk_get(dev, "clcdclk"); 199 if (IS_ERR(priv->clk)) { 200 dev_err(dev, "CLCD: unable to get clk.\n"); 201 ret = PTR_ERR(priv->clk); 202 goto dev_unref; 203 } 204 205 priv->regs = devm_ioremap_resource(dev, &amba_dev->res); 206 if (!priv->regs) { 207 dev_err(dev, "%s failed mmio\n", __func__); 208 return -EINVAL; 209 } 210 211 /* turn off interrupts before requesting the irq */ 212 writel(0, priv->regs + CLCD_PL111_IENB); 213 214 ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0, 215 "pl111", priv); 216 if (ret != 0) { 217 dev_err(dev, "%s failed irq %d\n", __func__, ret); 218 return ret; 219 } 220 221 ret = pl111_modeset_init(drm); 222 if (ret != 0) 223 goto dev_unref; 224 225 ret = drm_dev_register(drm, 0); 226 if (ret < 0) 227 goto dev_unref; 228 229 return 0; 230 231 dev_unref: 232 drm_dev_unref(drm); 233 return ret; 234 } 235 236 static int pl111_amba_remove(struct amba_device *amba_dev) 237 { 238 struct drm_device *drm = amba_get_drvdata(amba_dev); 239 struct pl111_drm_dev_private *priv = drm->dev_private; 240 241 drm_dev_unregister(drm); 242 if (priv->fbdev) 243 drm_fbdev_cma_fini(priv->fbdev); 244 drm_mode_config_cleanup(drm); 245 drm_dev_unref(drm); 246 247 return 0; 248 } 249 250 static struct amba_id pl111_id_table[] = { 251 { 252 .id = 0x00041111, 253 .mask = 0x000fffff, 254 }, 255 {0, 0}, 256 }; 257 258 static struct amba_driver pl111_amba_driver = { 259 .drv = { 260 .name = "drm-clcd-pl111", 261 }, 262 .probe = pl111_amba_probe, 263 .remove = pl111_amba_remove, 264 .id_table = pl111_id_table, 265 }; 266 267 module_amba_driver(pl111_amba_driver); 268 #endif /* CONFIG_ARM_AMBA */ 269 270 MODULE_DESCRIPTION(DRIVER_DESC); 271 MODULE_AUTHOR("ARM Ltd."); 272 MODULE_LICENSE("GPL"); 273