Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/pl111: Initial drm/kms driver for pl111

This is a modesetting driver for the pl111 CLCD display controller
found on various ARM platforms such as the Versatile Express. The
driver has only been tested on the bcm911360_entphn platform so far,
with PRIME-based buffer sharing between vc4 and clcd.

It reuses the existing devicetree binding, while not using quite as
many of its properties as the fbdev driver does (those are left for
future work).

v2: Nearly complete rewrite by anholt, cutting 2/3 of the code thanks
to DRM core's excellent new helpers.
v3: Don't match pl110 any more, don't attach if we don't have a DRM
panel, use DRM_GEM_CMA_FOPS, update MAINTAINERS, use the simple
display helper, use drm_gem_cma_dumb_create (same as our wrapper).
v4: Change the driver's .name to not clash with fbdev in sysfs, drop
platform alias, drop redundant "drm" in DRM driver name, hook up
.prepare_fb to the CMA helper so that DMA fences should work.
v5: Move register definitions inside the driver directory, fix build
in COMPILE_TEST and !AMBA mode.
v6: Drop TIM2_CLKSEL for now to be consistent with existing DT
bindings, switch back to external register definitions.

Signed-off-by: Tom Cooksey <tom.cooksey@arm.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org> (v5)
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20170413031746.12921-2-eric@anholt.net

authored by

Tom Cooksey and committed by
Eric Anholt
bed41005 73c73463

+832
+1
Documentation/gpu/index.rst
··· 12 12 drm-uapi 13 13 i915 14 14 meson 15 + pl111 15 16 tinydrm 16 17 vc4 17 18 vga-switcheroo
+6
Documentation/gpu/pl111.rst
··· 1 + ========================================== 2 + drm/pl111 ARM PrimeCell PL111 CLCD Driver 3 + ========================================== 4 + 5 + .. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c 6 + :doc: ARM PrimeCell PL111 CLCD Driver
+6
MAINTAINERS
··· 4160 4160 F: include/uapi/drm/drm* 4161 4161 F: include/linux/vga* 4162 4162 4163 + DRM DRIVER FOR ARM PL111 CLCD 4164 + M: Eric Anholt <eric@anholt.net> 4165 + T: git git://anongit.freedesktop.org/drm/drm-misc 4166 + S: Supported 4167 + F: drivers/gpu/drm/pl111/ 4168 + 4163 4169 DRM DRIVER FOR AST SERVER GRAPHICS CHIPS 4164 4170 M: Dave Airlie <airlied@redhat.com> 4165 4171 S: Odd Fixes
+2
drivers/gpu/drm/Kconfig
··· 276 276 277 277 source "drivers/gpu/drm/tinydrm/Kconfig" 278 278 279 + source "drivers/gpu/drm/pl111/Kconfig" 280 + 279 281 # Keep legacy drivers last 280 282 281 283 menuconfig DRM_LEGACY
+1
drivers/gpu/drm/Makefile
··· 97 97 obj-$(CONFIG_DRM_ZTE) += zte/ 98 98 obj-$(CONFIG_DRM_MXSFB) += mxsfb/ 99 99 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/ 100 + obj-$(CONFIG_DRM_PL111) += pl111/
+12
drivers/gpu/drm/pl111/Kconfig
··· 1 + config DRM_PL111 2 + tristate "DRM Support for PL111 CLCD Controller" 3 + depends on DRM 4 + depends on ARM || ARM64 || COMPILE_TEST 5 + select DRM_KMS_HELPER 6 + select DRM_KMS_CMA_HELPER 7 + select DRM_GEM_CMA_HELPER 8 + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE 9 + help 10 + Choose this option for DRM support for the PL111 CLCD controller. 11 + If M is selected the module will be called pl111_drm. 12 +
+5
drivers/gpu/drm/pl111/Makefile
··· 1 + pl111_drm-y += pl111_connector.o \ 2 + pl111_display.o \ 3 + pl111_drv.o 4 + 5 + obj-$(CONFIG_DRM_PL111) += pl111_drm.o
+127
drivers/gpu/drm/pl111/pl111_connector.c
··· 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 + * pl111_drm_connector.c 19 + * Implementation of the connector functions for PL111 DRM 20 + */ 21 + #include <linux/amba/clcd-regs.h> 22 + #include <linux/version.h> 23 + #include <linux/shmem_fs.h> 24 + #include <linux/dma-buf.h> 25 + 26 + #include <drm/drmP.h> 27 + #include <drm/drm_atomic_helper.h> 28 + #include <drm/drm_crtc_helper.h> 29 + #include <drm/drm_of.h> 30 + #include <drm/drm_panel.h> 31 + 32 + #include "pl111_drm.h" 33 + 34 + static void pl111_connector_destroy(struct drm_connector *connector) 35 + { 36 + struct pl111_drm_connector *pl111_connector = 37 + to_pl111_connector(connector); 38 + 39 + if (pl111_connector->panel) 40 + drm_panel_detach(pl111_connector->panel); 41 + 42 + drm_connector_unregister(connector); 43 + drm_connector_cleanup(connector); 44 + } 45 + 46 + static enum drm_connector_status pl111_connector_detect(struct drm_connector 47 + *connector, bool force) 48 + { 49 + struct pl111_drm_connector *pl111_connector = 50 + to_pl111_connector(connector); 51 + 52 + return (pl111_connector->panel ? 53 + connector_status_connected : 54 + connector_status_disconnected); 55 + } 56 + 57 + static int pl111_connector_helper_get_modes(struct drm_connector *connector) 58 + { 59 + struct pl111_drm_connector *pl111_connector = 60 + to_pl111_connector(connector); 61 + 62 + if (!pl111_connector->panel) 63 + return 0; 64 + 65 + return drm_panel_get_modes(pl111_connector->panel); 66 + } 67 + 68 + const struct drm_connector_funcs connector_funcs = { 69 + .fill_modes = drm_helper_probe_single_connector_modes, 70 + .destroy = pl111_connector_destroy, 71 + .detect = pl111_connector_detect, 72 + .dpms = drm_atomic_helper_connector_dpms, 73 + .reset = drm_atomic_helper_connector_reset, 74 + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 75 + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 76 + }; 77 + 78 + const struct drm_connector_helper_funcs connector_helper_funcs = { 79 + .get_modes = pl111_connector_helper_get_modes, 80 + }; 81 + 82 + /* Walks the OF graph to find the panel node and then asks DRM to look 83 + * up the panel. 84 + */ 85 + static struct drm_panel *pl111_get_panel(struct device *dev) 86 + { 87 + struct device_node *endpoint, *panel_node; 88 + struct device_node *np = dev->of_node; 89 + struct drm_panel *panel; 90 + 91 + endpoint = of_graph_get_next_endpoint(np, NULL); 92 + if (!endpoint) { 93 + dev_err(dev, "no endpoint to fetch panel\n"); 94 + return NULL; 95 + } 96 + 97 + /* don't proceed if we have an endpoint but no panel_node tied to it */ 98 + panel_node = of_graph_get_remote_port_parent(endpoint); 99 + of_node_put(endpoint); 100 + if (!panel_node) { 101 + dev_err(dev, "no valid panel node\n"); 102 + return NULL; 103 + } 104 + 105 + panel = of_drm_find_panel(panel_node); 106 + of_node_put(panel_node); 107 + 108 + return panel; 109 + } 110 + 111 + int pl111_connector_init(struct drm_device *dev) 112 + { 113 + struct pl111_drm_dev_private *priv = dev->dev_private; 114 + struct pl111_drm_connector *pl111_connector = &priv->connector; 115 + struct drm_connector *connector = &pl111_connector->connector; 116 + 117 + drm_connector_init(dev, connector, &connector_funcs, 118 + DRM_MODE_CONNECTOR_DPI); 119 + drm_connector_helper_add(connector, &connector_helper_funcs); 120 + 121 + pl111_connector->panel = pl111_get_panel(dev->dev); 122 + if (pl111_connector->panel) 123 + drm_panel_attach(pl111_connector->panel, connector); 124 + 125 + return 0; 126 + } 127 +
+344
drivers/gpu/drm/pl111/pl111_display.c
··· 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 + #include <linux/amba/clcd-regs.h> 18 + #include <linux/clk.h> 19 + #include <linux/version.h> 20 + #include <linux/dma-buf.h> 21 + #include <linux/of_graph.h> 22 + 23 + #include <drm/drmP.h> 24 + #include <drm/drm_panel.h> 25 + #include <drm/drm_gem_cma_helper.h> 26 + #include <drm/drm_fb_cma_helper.h> 27 + 28 + #include "pl111_drm.h" 29 + 30 + irqreturn_t pl111_irq(int irq, void *data) 31 + { 32 + struct pl111_drm_dev_private *priv = data; 33 + u32 irq_stat; 34 + irqreturn_t status = IRQ_NONE; 35 + 36 + irq_stat = readl(priv->regs + CLCD_PL111_MIS); 37 + 38 + if (!irq_stat) 39 + return IRQ_NONE; 40 + 41 + if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) { 42 + drm_crtc_handle_vblank(&priv->pipe.crtc); 43 + 44 + status = IRQ_HANDLED; 45 + } 46 + 47 + /* Clear the interrupt once done */ 48 + writel(irq_stat, priv->regs + CLCD_PL111_ICR); 49 + 50 + return status; 51 + } 52 + 53 + static u32 pl111_get_fb_offset(struct drm_plane_state *pstate) 54 + { 55 + struct drm_framebuffer *fb = pstate->fb; 56 + struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0); 57 + 58 + return (obj->paddr + 59 + fb->offsets[0] + 60 + fb->format->cpp[0] * pstate->src_x + 61 + fb->pitches[0] * pstate->src_y); 62 + } 63 + 64 + static int pl111_display_check(struct drm_simple_display_pipe *pipe, 65 + struct drm_plane_state *pstate, 66 + struct drm_crtc_state *cstate) 67 + { 68 + const struct drm_display_mode *mode = &cstate->mode; 69 + struct drm_framebuffer *old_fb = pipe->plane.state->fb; 70 + struct drm_framebuffer *fb = pstate->fb; 71 + 72 + if (mode->hdisplay % 16) 73 + return -EINVAL; 74 + 75 + if (fb) { 76 + u32 offset = pl111_get_fb_offset(pstate); 77 + 78 + /* FB base address must be dword aligned. */ 79 + if (offset & 3) 80 + return -EINVAL; 81 + 82 + /* There's no pitch register -- the mode's hdisplay 83 + * controls it. 84 + */ 85 + if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) 86 + return -EINVAL; 87 + 88 + /* We can't change the FB format in a flicker-free 89 + * manner (and only update it during CRTC enable). 90 + */ 91 + if (old_fb && old_fb->format != fb->format) 92 + cstate->mode_changed = true; 93 + } 94 + 95 + return 0; 96 + } 97 + 98 + static void pl111_display_enable(struct drm_simple_display_pipe *pipe, 99 + struct drm_crtc_state *cstate) 100 + { 101 + struct drm_crtc *crtc = &pipe->crtc; 102 + struct drm_plane *plane = &pipe->plane; 103 + struct drm_device *drm = crtc->dev; 104 + struct pl111_drm_dev_private *priv = drm->dev_private; 105 + const struct drm_display_mode *mode = &cstate->mode; 106 + struct drm_framebuffer *fb = plane->state->fb; 107 + struct drm_connector *connector = &priv->connector.connector; 108 + u32 cntl; 109 + u32 ppl, hsw, hfp, hbp; 110 + u32 lpp, vsw, vfp, vbp; 111 + u32 cpl; 112 + int ret; 113 + 114 + ret = clk_set_rate(priv->clk, mode->clock * 1000); 115 + if (ret) { 116 + dev_err(drm->dev, 117 + "Failed to set pixel clock rate to %d: %d\n", 118 + mode->clock * 1000, ret); 119 + } 120 + 121 + clk_prepare_enable(priv->clk); 122 + 123 + ppl = (mode->hdisplay / 16) - 1; 124 + hsw = mode->hsync_end - mode->hsync_start - 1; 125 + hfp = mode->hsync_start - mode->hdisplay - 1; 126 + hbp = mode->htotal - mode->hsync_end - 1; 127 + 128 + lpp = mode->vdisplay - 1; 129 + vsw = mode->vsync_end - mode->vsync_start - 1; 130 + vfp = mode->vsync_start - mode->vdisplay; 131 + vbp = mode->vtotal - mode->vsync_end; 132 + 133 + cpl = mode->hdisplay - 1; 134 + 135 + writel((ppl << 2) | 136 + (hsw << 8) | 137 + (hfp << 16) | 138 + (hbp << 24), 139 + priv->regs + CLCD_TIM0); 140 + writel(lpp | 141 + (vsw << 10) | 142 + (vfp << 16) | 143 + (vbp << 24), 144 + priv->regs + CLCD_TIM1); 145 + /* XXX: We currently always use CLCDCLK with no divisor. We 146 + * could probably reduce power consumption by using HCLK 147 + * (apb_pclk) with a divisor when it gets us near our target 148 + * pixel clock. 149 + */ 150 + writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) | 151 + ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) | 152 + ((connector->display_info.bus_flags & 153 + DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) | 154 + ((connector->display_info.bus_flags & 155 + DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) | 156 + TIM2_BCD | 157 + (cpl << 16), 158 + priv->regs + CLCD_TIM2); 159 + writel(0, priv->regs + CLCD_TIM3); 160 + 161 + drm_panel_prepare(priv->connector.panel); 162 + 163 + /* Enable and Power Up */ 164 + cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1); 165 + 166 + /* Note that the the hardware's format reader takes 'r' from 167 + * the low bit, while DRM formats list channels from high bit 168 + * to low bit as you read left to right. 169 + */ 170 + switch (fb->format->format) { 171 + case DRM_FORMAT_ABGR8888: 172 + case DRM_FORMAT_XBGR8888: 173 + cntl |= CNTL_LCDBPP24; 174 + break; 175 + case DRM_FORMAT_ARGB8888: 176 + case DRM_FORMAT_XRGB8888: 177 + cntl |= CNTL_LCDBPP24 | CNTL_BGR; 178 + break; 179 + case DRM_FORMAT_BGR565: 180 + cntl |= CNTL_LCDBPP16_565; 181 + break; 182 + case DRM_FORMAT_RGB565: 183 + cntl |= CNTL_LCDBPP16_565 | CNTL_BGR; 184 + break; 185 + case DRM_FORMAT_ABGR1555: 186 + case DRM_FORMAT_XBGR1555: 187 + cntl |= CNTL_LCDBPP16; 188 + break; 189 + case DRM_FORMAT_ARGB1555: 190 + case DRM_FORMAT_XRGB1555: 191 + cntl |= CNTL_LCDBPP16 | CNTL_BGR; 192 + break; 193 + case DRM_FORMAT_ABGR4444: 194 + case DRM_FORMAT_XBGR4444: 195 + cntl |= CNTL_LCDBPP16_444; 196 + break; 197 + case DRM_FORMAT_ARGB4444: 198 + case DRM_FORMAT_XRGB4444: 199 + cntl |= CNTL_LCDBPP16_444 | CNTL_BGR; 200 + break; 201 + default: 202 + WARN_ONCE(true, "Unknown FB format 0x%08x\n", 203 + fb->format->format); 204 + break; 205 + } 206 + 207 + writel(cntl, priv->regs + CLCD_PL111_CNTL); 208 + 209 + drm_panel_enable(priv->connector.panel); 210 + 211 + drm_crtc_vblank_on(crtc); 212 + } 213 + 214 + void pl111_display_disable(struct drm_simple_display_pipe *pipe) 215 + { 216 + struct drm_crtc *crtc = &pipe->crtc; 217 + struct drm_device *drm = crtc->dev; 218 + struct pl111_drm_dev_private *priv = drm->dev_private; 219 + 220 + drm_crtc_vblank_off(crtc); 221 + 222 + drm_panel_disable(priv->connector.panel); 223 + 224 + /* Disable and Power Down */ 225 + writel(0, priv->regs + CLCD_PL111_CNTL); 226 + 227 + drm_panel_unprepare(priv->connector.panel); 228 + 229 + clk_disable_unprepare(priv->clk); 230 + } 231 + 232 + static void pl111_display_update(struct drm_simple_display_pipe *pipe, 233 + struct drm_plane_state *old_pstate) 234 + { 235 + struct drm_crtc *crtc = &pipe->crtc; 236 + struct drm_device *drm = crtc->dev; 237 + struct pl111_drm_dev_private *priv = drm->dev_private; 238 + struct drm_pending_vblank_event *event = crtc->state->event; 239 + struct drm_plane *plane = &pipe->plane; 240 + struct drm_plane_state *pstate = plane->state; 241 + struct drm_framebuffer *fb = pstate->fb; 242 + 243 + if (fb) { 244 + u32 addr = pl111_get_fb_offset(pstate); 245 + 246 + writel(addr, priv->regs + CLCD_UBAS); 247 + } 248 + 249 + if (event) { 250 + crtc->state->event = NULL; 251 + 252 + spin_lock_irq(&crtc->dev->event_lock); 253 + if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) 254 + drm_crtc_arm_vblank_event(crtc, event); 255 + else 256 + drm_crtc_send_vblank_event(crtc, event); 257 + spin_unlock_irq(&crtc->dev->event_lock); 258 + } 259 + } 260 + 261 + int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc) 262 + { 263 + struct pl111_drm_dev_private *priv = drm->dev_private; 264 + 265 + writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB); 266 + 267 + return 0; 268 + } 269 + 270 + void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc) 271 + { 272 + struct pl111_drm_dev_private *priv = drm->dev_private; 273 + 274 + writel(0, priv->regs + CLCD_PL111_IENB); 275 + } 276 + 277 + static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe, 278 + struct drm_plane_state *plane_state) 279 + { 280 + return drm_fb_cma_prepare_fb(&pipe->plane, plane_state); 281 + } 282 + 283 + const struct drm_simple_display_pipe_funcs pl111_display_funcs = { 284 + .check = pl111_display_check, 285 + .enable = pl111_display_enable, 286 + .disable = pl111_display_disable, 287 + .update = pl111_display_update, 288 + .prepare_fb = pl111_display_prepare_fb, 289 + }; 290 + 291 + int pl111_display_init(struct drm_device *drm) 292 + { 293 + struct pl111_drm_dev_private *priv = drm->dev_private; 294 + struct device *dev = drm->dev; 295 + struct device_node *endpoint; 296 + u32 tft_r0b0g0[3]; 297 + int ret; 298 + static const u32 formats[] = { 299 + DRM_FORMAT_ABGR8888, 300 + DRM_FORMAT_XBGR8888, 301 + DRM_FORMAT_ARGB8888, 302 + DRM_FORMAT_XRGB8888, 303 + DRM_FORMAT_BGR565, 304 + DRM_FORMAT_RGB565, 305 + DRM_FORMAT_ABGR1555, 306 + DRM_FORMAT_XBGR1555, 307 + DRM_FORMAT_ARGB1555, 308 + DRM_FORMAT_XRGB1555, 309 + DRM_FORMAT_ABGR4444, 310 + DRM_FORMAT_XBGR4444, 311 + DRM_FORMAT_ARGB4444, 312 + DRM_FORMAT_XRGB4444, 313 + }; 314 + 315 + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); 316 + if (!endpoint) 317 + return -ENODEV; 318 + 319 + if (of_property_read_u32_array(endpoint, 320 + "arm,pl11x,tft-r0g0b0-pads", 321 + tft_r0b0g0, 322 + ARRAY_SIZE(tft_r0b0g0)) != 0) { 323 + dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n"); 324 + of_node_put(endpoint); 325 + return -ENOENT; 326 + } 327 + of_node_put(endpoint); 328 + 329 + if (tft_r0b0g0[0] != 0 || 330 + tft_r0b0g0[1] != 8 || 331 + tft_r0b0g0[2] != 16) { 332 + dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n"); 333 + return -EINVAL; 334 + } 335 + 336 + ret = drm_simple_display_pipe_init(drm, &priv->pipe, 337 + &pl111_display_funcs, 338 + formats, ARRAY_SIZE(formats), 339 + &priv->connector.connector); 340 + if (ret) 341 + return ret; 342 + 343 + return 0; 344 + }
+56
drivers/gpu/drm/pl111/pl111_drm.h
··· 1 + /* 2 + * 3 + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. 4 + * 5 + * 6 + * Parts of this file were based on sources as follows: 7 + * 8 + * Copyright (c) 2006-2008 Intel Corporation 9 + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 10 + * Copyright (C) 2011 Texas Instruments 11 + * 12 + * This program is free software and is provided to you under the terms of the 13 + * GNU General Public License version 2 as published by the Free Software 14 + * Foundation, and any use by you of this program is subject to the terms of 15 + * such GNU licence. 16 + * 17 + */ 18 + 19 + #ifndef _PL111_DRM_H_ 20 + #define _PL111_DRM_H_ 21 + 22 + #include <drm/drm_gem.h> 23 + #include <drm/drm_simple_kms_helper.h> 24 + 25 + #define CLCD_IRQ_NEXTBASE_UPDATE BIT(2) 26 + 27 + struct pl111_drm_connector { 28 + struct drm_connector connector; 29 + struct drm_panel *panel; 30 + }; 31 + 32 + struct pl111_drm_dev_private { 33 + struct drm_device *drm; 34 + 35 + struct pl111_drm_connector connector; 36 + struct drm_simple_display_pipe pipe; 37 + struct drm_fbdev_cma *fbdev; 38 + 39 + void *regs; 40 + struct clk *clk; 41 + }; 42 + 43 + #define to_pl111_connector(x) \ 44 + container_of(x, struct pl111_drm_connector, connector) 45 + 46 + int pl111_display_init(struct drm_device *dev); 47 + int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc); 48 + void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc); 49 + irqreturn_t pl111_irq(int irq, void *data); 50 + int pl111_connector_init(struct drm_device *dev); 51 + int pl111_encoder_init(struct drm_device *dev); 52 + int pl111_dumb_create(struct drm_file *file_priv, 53 + struct drm_device *dev, 54 + struct drm_mode_create_dumb *args); 55 + 56 + #endif /* _PL111_DRM_H_ */
+272
drivers/gpu/drm/pl111/pl111_drv.c
··· 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");