diff options
| author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-08-15 08:59:49 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-10-12 05:13:40 -0400 |
| commit | 96f60e37dc66091bde8d5de136ff6fda09f2d799 (patch) | |
| tree | 2c2cc30a5ac7339730430369e27d33d4a8dd21ef | |
| parent | 15c03dd4859ab16f9212238f29dd315654aa94f6 (diff) | |
DRM: Armada: Add Armada DRM driver
This patch adds support for the pair of LCD controllers on the Marvell
Armada 510 SoCs. This driver supports:
- multiple contiguous scanout buffers for video and graphics
- shm backed cacheable buffer objects for X pixmaps for Vivante GPU
acceleration
- dual lcd0 and lcd1 crt operation
- video overlay on each LCD crt via DRM planes
- page flipping of the main scanout buffers
- DRM prime for buffer export/import
This driver is trivial to extend to other Armada SoCs.
Included in this commit is the core driver with no output support; output
support is platform and encoder driver dependent.
Tested-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Reviewed-by: Rob Clark <robdclark@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
24 files changed, 4015 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 955555d6ec88..3b5176dd1c86 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig | |||
| @@ -225,6 +225,8 @@ source "drivers/gpu/drm/mgag200/Kconfig" | |||
| 225 | 225 | ||
| 226 | source "drivers/gpu/drm/cirrus/Kconfig" | 226 | source "drivers/gpu/drm/cirrus/Kconfig" |
| 227 | 227 | ||
| 228 | source "drivers/gpu/drm/armada/Kconfig" | ||
| 229 | |||
| 228 | source "drivers/gpu/drm/rcar-du/Kconfig" | 230 | source "drivers/gpu/drm/rcar-du/Kconfig" |
| 229 | 231 | ||
| 230 | source "drivers/gpu/drm/shmobile/Kconfig" | 232 | source "drivers/gpu/drm/shmobile/Kconfig" |
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f089adfe70ee..385f460459d4 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
| @@ -49,6 +49,7 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ | |||
| 49 | obj-$(CONFIG_DRM_GMA500) += gma500/ | 49 | obj-$(CONFIG_DRM_GMA500) += gma500/ |
| 50 | obj-$(CONFIG_DRM_UDL) += udl/ | 50 | obj-$(CONFIG_DRM_UDL) += udl/ |
| 51 | obj-$(CONFIG_DRM_AST) += ast/ | 51 | obj-$(CONFIG_DRM_AST) += ast/ |
| 52 | obj-$(CONFIG_DRM_ARMADA) += armada/ | ||
| 52 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ | 53 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ |
| 53 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ | 54 | obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ |
| 54 | obj-$(CONFIG_DRM_OMAP) += omapdrm/ | 55 | obj-$(CONFIG_DRM_OMAP) += omapdrm/ |
diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig new file mode 100644 index 000000000000..c7a0a944acfe --- /dev/null +++ b/drivers/gpu/drm/armada/Kconfig | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | config DRM_ARMADA | ||
| 2 | tristate "DRM support for Marvell Armada SoCs" | ||
| 3 | depends on DRM && HAVE_CLK | ||
| 4 | select FB_CFB_FILLRECT | ||
| 5 | select FB_CFB_COPYAREA | ||
| 6 | select FB_CFB_IMAGEBLIT | ||
| 7 | select DRM_KMS_HELPER | ||
| 8 | help | ||
| 9 | Support the "LCD" controllers found on the Marvell Armada 510 | ||
| 10 | devices. There are two controllers on the device, each controller | ||
| 11 | supports graphics and video overlays. | ||
| 12 | |||
| 13 | This driver provides no built-in acceleration; acceleration is | ||
| 14 | performed by other IP found on the SoC. This driver provides | ||
| 15 | kernel mode setting and buffer management to userspace. | ||
diff --git a/drivers/gpu/drm/armada/Makefile b/drivers/gpu/drm/armada/Makefile new file mode 100644 index 000000000000..d6f43e06150a --- /dev/null +++ b/drivers/gpu/drm/armada/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \ | ||
| 2 | armada_gem.o armada_output.o armada_overlay.o \ | ||
| 3 | armada_slave.o | ||
| 4 | armada-y += armada_510.o | ||
| 5 | armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o | ||
| 6 | |||
| 7 | obj-$(CONFIG_DRM_ARMADA) := armada.o | ||
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c new file mode 100644 index 000000000000..a016888acd29 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_510.c | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * Armada 510 (aka Dove) variant support | ||
| 9 | */ | ||
| 10 | #include <linux/clk.h> | ||
| 11 | #include <linux/io.h> | ||
| 12 | #include <drm/drmP.h> | ||
| 13 | #include <drm/drm_crtc_helper.h> | ||
| 14 | #include "armada_crtc.h" | ||
| 15 | #include "armada_drm.h" | ||
| 16 | #include "armada_hw.h" | ||
| 17 | |||
| 18 | static int armada510_init(struct armada_private *priv, struct device *dev) | ||
| 19 | { | ||
| 20 | priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1"); | ||
| 21 | |||
| 22 | if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT) | ||
| 23 | priv->extclk[0] = ERR_PTR(-EPROBE_DEFER); | ||
| 24 | |||
| 25 | return PTR_RET(priv->extclk[0]); | ||
| 26 | } | ||
| 27 | |||
| 28 | static int armada510_crtc_init(struct armada_crtc *dcrtc) | ||
| 29 | { | ||
| 30 | /* Lower the watermark so to eliminate jitter at higher bandwidths */ | ||
| 31 | armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); | ||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Armada510 specific SCLK register selection. | ||
| 37 | * This gets called with sclk = NULL to test whether the mode is | ||
| 38 | * supportable, and again with sclk != NULL to set the clocks up for | ||
| 39 | * that. The former can return an error, but the latter is expected | ||
| 40 | * not to. | ||
| 41 | * | ||
| 42 | * We currently are pretty rudimentary here, always selecting | ||
| 43 | * EXT_REF_CLK_1 for LCD0 and erroring LCD1. This needs improvement! | ||
| 44 | */ | ||
| 45 | static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, | ||
| 46 | const struct drm_display_mode *mode, uint32_t *sclk) | ||
| 47 | { | ||
| 48 | struct armada_private *priv = dcrtc->crtc.dev->dev_private; | ||
| 49 | struct clk *clk = priv->extclk[0]; | ||
| 50 | int ret; | ||
| 51 | |||
| 52 | if (dcrtc->num == 1) | ||
| 53 | return -EINVAL; | ||
| 54 | |||
| 55 | if (IS_ERR(clk)) | ||
| 56 | return PTR_ERR(clk); | ||
| 57 | |||
| 58 | if (dcrtc->clk != clk) { | ||
| 59 | ret = clk_prepare_enable(clk); | ||
| 60 | if (ret) | ||
| 61 | return ret; | ||
| 62 | dcrtc->clk = clk; | ||
| 63 | } | ||
| 64 | |||
| 65 | if (sclk) { | ||
| 66 | uint32_t rate, ref, div; | ||
| 67 | |||
| 68 | rate = mode->clock * 1000; | ||
| 69 | ref = clk_round_rate(clk, rate); | ||
| 70 | div = DIV_ROUND_UP(ref, rate); | ||
| 71 | if (div < 1) | ||
| 72 | div = 1; | ||
| 73 | |||
| 74 | clk_set_rate(clk, ref); | ||
| 75 | *sclk = div | SCLK_510_EXTCLK1; | ||
| 76 | } | ||
| 77 | |||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | |||
| 81 | const struct armada_variant armada510_ops = { | ||
| 82 | .has_spu_adv_reg = true, | ||
| 83 | .init = armada510_init, | ||
| 84 | .crtc_init = armada510_crtc_init, | ||
| 85 | .crtc_compute_clock = armada510_crtc_compute_clock, | ||
| 86 | }; | ||
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c new file mode 100644 index 000000000000..7b379fdb4e8c --- /dev/null +++ b/drivers/gpu/drm/armada/armada_crtc.c | |||
| @@ -0,0 +1,861 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * Rewritten from the dovefb driver, and Armada510 manuals. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #include <linux/clk.h> | ||
| 10 | #include <drm/drmP.h> | ||
| 11 | #include <drm/drm_crtc_helper.h> | ||
| 12 | #include "armada_crtc.h" | ||
| 13 | #include "armada_drm.h" | ||
| 14 | #include "armada_fb.h" | ||
| 15 | #include "armada_gem.h" | ||
| 16 | #include "armada_hw.h" | ||
| 17 | |||
| 18 | struct armada_frame_work { | ||
| 19 | struct drm_pending_vblank_event *event; | ||
| 20 | struct armada_regs regs[4]; | ||
| 21 | struct drm_framebuffer *old_fb; | ||
| 22 | }; | ||
| 23 | |||
| 24 | enum csc_mode { | ||
| 25 | CSC_AUTO = 0, | ||
| 26 | CSC_YUV_CCIR601 = 1, | ||
| 27 | CSC_YUV_CCIR709 = 2, | ||
| 28 | CSC_RGB_COMPUTER = 1, | ||
| 29 | CSC_RGB_STUDIO = 2, | ||
| 30 | }; | ||
| 31 | |||
| 32 | /* | ||
| 33 | * A note about interlacing. Let's consider HDMI 1920x1080i. | ||
| 34 | * The timing parameters we have from X are: | ||
| 35 | * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot | ||
| 36 | * 1920 2448 2492 2640 1080 1084 1094 1125 | ||
| 37 | * Which get translated to: | ||
| 38 | * Hact HsyA HsyI Htot Vact VsyA VsyI Vtot | ||
| 39 | * 1920 2448 2492 2640 540 542 547 562 | ||
| 40 | * | ||
| 41 | * This is how it is defined by CEA-861-D - line and pixel numbers are | ||
| 42 | * referenced to the rising edge of VSYNC and HSYNC. Total clocks per | ||
| 43 | * line: 2640. The odd frame, the first active line is at line 21, and | ||
| 44 | * the even frame, the first active line is 584. | ||
| 45 | * | ||
| 46 | * LN: 560 561 562 563 567 568 569 | ||
| 47 | * DE: ~~~|____________________________//__________________________ | ||
| 48 | * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____ | ||
| 49 | * VSYNC: _________________________|~~~~~~//~~~~~~~~~~~~~~~|__________ | ||
| 50 | * 22 blanking lines. VSYNC at 1320 (referenced to the HSYNC rising edge). | ||
| 51 | * | ||
| 52 | * LN: 1123 1124 1125 1 5 6 7 | ||
| 53 | * DE: ~~~|____________________________//__________________________ | ||
| 54 | * HSYNC: ____|~|_____|~|_____|~|_____|~|_//__|~|_____|~|_____|~|_____ | ||
| 55 | * VSYNC: ____________________|~~~~~~~~~~~//~~~~~~~~~~|_______________ | ||
| 56 | * 23 blanking lines | ||
| 57 | * | ||
| 58 | * The Armada LCD Controller line and pixel numbers are, like X timings, | ||
| 59 | * referenced to the top left of the active frame. | ||
| 60 | * | ||
| 61 | * So, translating these to our LCD controller: | ||
| 62 | * Odd frame, 563 total lines, VSYNC at line 543-548, pixel 1128. | ||
| 63 | * Even frame, 562 total lines, VSYNC at line 542-547, pixel 2448. | ||
| 64 | * Note: Vsync front porch remains constant! | ||
| 65 | * | ||
| 66 | * if (odd_frame) { | ||
| 67 | * vtotal = mode->crtc_vtotal + 1; | ||
| 68 | * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay + 1; | ||
| 69 | * vhorizpos = mode->crtc_hsync_start - mode->crtc_htotal / 2 | ||
| 70 | * } else { | ||
| 71 | * vtotal = mode->crtc_vtotal; | ||
| 72 | * vbackporch = mode->crtc_vsync_start - mode->crtc_vdisplay; | ||
| 73 | * vhorizpos = mode->crtc_hsync_start; | ||
| 74 | * } | ||
| 75 | * vfrontporch = mode->crtc_vtotal - mode->crtc_vsync_end; | ||
| 76 | * | ||
| 77 | * So, we need to reprogram these registers on each vsync event: | ||
| 78 | * LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL | ||
| 79 | * | ||
| 80 | * Note: we do not use the frame done interrupts because these appear | ||
| 81 | * to happen too early, and lead to jitter on the display (presumably | ||
| 82 | * they occur at the end of the last active line, before the vsync back | ||
| 83 | * porch, which we're reprogramming.) | ||
| 84 | */ | ||
| 85 | |||
| 86 | void | ||
| 87 | armada_drm_crtc_update_regs(struct armada_crtc *dcrtc, struct armada_regs *regs) | ||
| 88 | { | ||
| 89 | while (regs->offset != ~0) { | ||
| 90 | void __iomem *reg = dcrtc->base + regs->offset; | ||
| 91 | uint32_t val; | ||
| 92 | |||
| 93 | val = regs->mask; | ||
| 94 | if (val != 0) | ||
| 95 | val &= readl_relaxed(reg); | ||
| 96 | writel_relaxed(val | regs->val, reg); | ||
| 97 | ++regs; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | #define dpms_blanked(dpms) ((dpms) != DRM_MODE_DPMS_ON) | ||
| 102 | |||
| 103 | static void armada_drm_crtc_update(struct armada_crtc *dcrtc) | ||
| 104 | { | ||
| 105 | uint32_t dumb_ctrl; | ||
| 106 | |||
| 107 | dumb_ctrl = dcrtc->cfg_dumb_ctrl; | ||
| 108 | |||
| 109 | if (!dpms_blanked(dcrtc->dpms)) | ||
| 110 | dumb_ctrl |= CFG_DUMB_ENA; | ||
| 111 | |||
| 112 | /* | ||
| 113 | * When the dumb interface isn't in DUMB24_RGB888_0 mode, it might | ||
| 114 | * be using SPI or GPIO. If we set this to DUMB_BLANK, we will | ||
| 115 | * force LCD_D[23:0] to output blank color, overriding the GPIO or | ||
| 116 | * SPI usage. So leave it as-is unless in DUMB24_RGB888_0 mode. | ||
| 117 | */ | ||
| 118 | if (dpms_blanked(dcrtc->dpms) && | ||
| 119 | (dumb_ctrl & DUMB_MASK) == DUMB24_RGB888_0) { | ||
| 120 | dumb_ctrl &= ~DUMB_MASK; | ||
| 121 | dumb_ctrl |= DUMB_BLANK; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* | ||
| 125 | * The documentation doesn't indicate what the normal state of | ||
| 126 | * the sync signals are. Sebastian Hesselbart kindly probed | ||
| 127 | * these signals on his board to determine their state. | ||
| 128 | * | ||
| 129 | * The non-inverted state of the sync signals is active high. | ||
| 130 | * Setting these bits makes the appropriate signal active low. | ||
| 131 | */ | ||
| 132 | if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NCSYNC) | ||
| 133 | dumb_ctrl |= CFG_INV_CSYNC; | ||
| 134 | if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NHSYNC) | ||
| 135 | dumb_ctrl |= CFG_INV_HSYNC; | ||
| 136 | if (dcrtc->crtc.mode.flags & DRM_MODE_FLAG_NVSYNC) | ||
| 137 | dumb_ctrl |= CFG_INV_VSYNC; | ||
| 138 | |||
| 139 | if (dcrtc->dumb_ctrl != dumb_ctrl) { | ||
| 140 | dcrtc->dumb_ctrl = dumb_ctrl; | ||
| 141 | writel_relaxed(dumb_ctrl, dcrtc->base + LCD_SPU_DUMB_CTRL); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | static unsigned armada_drm_crtc_calc_fb(struct drm_framebuffer *fb, | ||
| 146 | int x, int y, struct armada_regs *regs, bool interlaced) | ||
| 147 | { | ||
| 148 | struct armada_gem_object *obj = drm_fb_obj(fb); | ||
| 149 | unsigned pitch = fb->pitches[0]; | ||
| 150 | unsigned offset = y * pitch + x * fb->bits_per_pixel / 8; | ||
| 151 | uint32_t addr_odd, addr_even; | ||
| 152 | unsigned i = 0; | ||
| 153 | |||
| 154 | DRM_DEBUG_DRIVER("pitch %u x %d y %d bpp %d\n", | ||
| 155 | pitch, x, y, fb->bits_per_pixel); | ||
| 156 | |||
| 157 | addr_odd = addr_even = obj->dev_addr + offset; | ||
| 158 | |||
| 159 | if (interlaced) { | ||
| 160 | addr_even += pitch; | ||
| 161 | pitch *= 2; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* write offset, base, and pitch */ | ||
| 165 | armada_reg_queue_set(regs, i, addr_odd, LCD_CFG_GRA_START_ADDR0); | ||
| 166 | armada_reg_queue_set(regs, i, addr_even, LCD_CFG_GRA_START_ADDR1); | ||
| 167 | armada_reg_queue_mod(regs, i, pitch, 0xffff, LCD_CFG_GRA_PITCH); | ||
| 168 | |||
| 169 | return i; | ||
| 170 | } | ||
| 171 | |||
| 172 | static int armada_drm_crtc_queue_frame_work(struct armada_crtc *dcrtc, | ||
| 173 | struct armada_frame_work *work) | ||
| 174 | { | ||
| 175 | struct drm_device *dev = dcrtc->crtc.dev; | ||
| 176 | unsigned long flags; | ||
| 177 | int ret; | ||
| 178 | |||
| 179 | ret = drm_vblank_get(dev, dcrtc->num); | ||
| 180 | if (ret) { | ||
| 181 | DRM_ERROR("failed to acquire vblank counter\n"); | ||
| 182 | return ret; | ||
| 183 | } | ||
| 184 | |||
| 185 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 186 | if (!dcrtc->frame_work) | ||
| 187 | dcrtc->frame_work = work; | ||
| 188 | else | ||
| 189 | ret = -EBUSY; | ||
| 190 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 191 | |||
| 192 | if (ret) | ||
| 193 | drm_vblank_put(dev, dcrtc->num); | ||
| 194 | |||
| 195 | return ret; | ||
| 196 | } | ||
| 197 | |||
| 198 | static void armada_drm_crtc_complete_frame_work(struct armada_crtc *dcrtc) | ||
| 199 | { | ||
| 200 | struct drm_device *dev = dcrtc->crtc.dev; | ||
| 201 | struct armada_frame_work *work = dcrtc->frame_work; | ||
| 202 | |||
| 203 | dcrtc->frame_work = NULL; | ||
| 204 | |||
| 205 | armada_drm_crtc_update_regs(dcrtc, work->regs); | ||
| 206 | |||
| 207 | if (work->event) | ||
| 208 | drm_send_vblank_event(dev, dcrtc->num, work->event); | ||
| 209 | |||
| 210 | drm_vblank_put(dev, dcrtc->num); | ||
| 211 | |||
| 212 | /* Finally, queue the process-half of the cleanup. */ | ||
| 213 | __armada_drm_queue_unref_work(dcrtc->crtc.dev, work->old_fb); | ||
| 214 | kfree(work); | ||
| 215 | } | ||
| 216 | |||
| 217 | static void armada_drm_crtc_finish_fb(struct armada_crtc *dcrtc, | ||
| 218 | struct drm_framebuffer *fb, bool force) | ||
| 219 | { | ||
| 220 | struct armada_frame_work *work; | ||
| 221 | |||
| 222 | if (!fb) | ||
| 223 | return; | ||
| 224 | |||
| 225 | if (force) { | ||
| 226 | /* Display is disabled, so just drop the old fb */ | ||
| 227 | drm_framebuffer_unreference(fb); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | |||
| 231 | work = kmalloc(sizeof(*work), GFP_KERNEL); | ||
| 232 | if (work) { | ||
| 233 | int i = 0; | ||
| 234 | work->event = NULL; | ||
| 235 | work->old_fb = fb; | ||
| 236 | armada_reg_queue_end(work->regs, i); | ||
| 237 | |||
| 238 | if (armada_drm_crtc_queue_frame_work(dcrtc, work) == 0) | ||
| 239 | return; | ||
| 240 | |||
| 241 | kfree(work); | ||
| 242 | } | ||
| 243 | |||
| 244 | /* | ||
| 245 | * Oops - just drop the reference immediately and hope for | ||
| 246 | * the best. The worst that will happen is the buffer gets | ||
| 247 | * reused before it has finished being displayed. | ||
| 248 | */ | ||
| 249 | drm_framebuffer_unreference(fb); | ||
| 250 | } | ||
| 251 | |||
| 252 | static void armada_drm_vblank_off(struct armada_crtc *dcrtc) | ||
| 253 | { | ||
| 254 | struct drm_device *dev = dcrtc->crtc.dev; | ||
| 255 | |||
| 256 | /* | ||
| 257 | * Tell the DRM core that vblank IRQs aren't going to happen for | ||
| 258 | * a while. This cleans up any pending vblank events for us. | ||
| 259 | */ | ||
| 260 | drm_vblank_off(dev, dcrtc->num); | ||
| 261 | |||
| 262 | /* Handle any pending flip event. */ | ||
| 263 | spin_lock_irq(&dev->event_lock); | ||
| 264 | if (dcrtc->frame_work) | ||
| 265 | armada_drm_crtc_complete_frame_work(dcrtc); | ||
| 266 | spin_unlock_irq(&dev->event_lock); | ||
| 267 | } | ||
| 268 | |||
| 269 | void armada_drm_crtc_gamma_set(struct drm_crtc *crtc, u16 r, u16 g, u16 b, | ||
| 270 | int idx) | ||
| 271 | { | ||
| 272 | } | ||
| 273 | |||
| 274 | void armada_drm_crtc_gamma_get(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, | ||
| 275 | int idx) | ||
| 276 | { | ||
| 277 | } | ||
| 278 | |||
| 279 | /* The mode_config.mutex will be held for this call */ | ||
| 280 | static void armada_drm_crtc_dpms(struct drm_crtc *crtc, int dpms) | ||
| 281 | { | ||
| 282 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 283 | |||
| 284 | if (dcrtc->dpms != dpms) { | ||
| 285 | dcrtc->dpms = dpms; | ||
| 286 | armada_drm_crtc_update(dcrtc); | ||
| 287 | if (dpms_blanked(dpms)) | ||
| 288 | armada_drm_vblank_off(dcrtc); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | /* | ||
| 293 | * Prepare for a mode set. Turn off overlay to ensure that we don't end | ||
| 294 | * up with the overlay size being bigger than the active screen size. | ||
| 295 | * We rely upon X refreshing this state after the mode set has completed. | ||
| 296 | * | ||
| 297 | * The mode_config.mutex will be held for this call | ||
| 298 | */ | ||
| 299 | static void armada_drm_crtc_prepare(struct drm_crtc *crtc) | ||
| 300 | { | ||
| 301 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 302 | struct drm_plane *plane; | ||
| 303 | |||
| 304 | /* | ||
| 305 | * If we have an overlay plane associated with this CRTC, disable | ||
| 306 | * it before the modeset to avoid its coordinates being outside | ||
| 307 | * the new mode parameters. DRM doesn't provide help with this. | ||
| 308 | */ | ||
| 309 | plane = dcrtc->plane; | ||
| 310 | if (plane) { | ||
| 311 | struct drm_framebuffer *fb = plane->fb; | ||
| 312 | |||
| 313 | plane->funcs->disable_plane(plane); | ||
| 314 | plane->fb = NULL; | ||
| 315 | plane->crtc = NULL; | ||
| 316 | drm_framebuffer_unreference(fb); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | /* The mode_config.mutex will be held for this call */ | ||
| 321 | static void armada_drm_crtc_commit(struct drm_crtc *crtc) | ||
| 322 | { | ||
| 323 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 324 | |||
| 325 | if (dcrtc->dpms != DRM_MODE_DPMS_ON) { | ||
| 326 | dcrtc->dpms = DRM_MODE_DPMS_ON; | ||
| 327 | armada_drm_crtc_update(dcrtc); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | /* The mode_config.mutex will be held for this call */ | ||
| 332 | static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, | ||
| 333 | const struct drm_display_mode *mode, struct drm_display_mode *adj) | ||
| 334 | { | ||
| 335 | struct armada_private *priv = crtc->dev->dev_private; | ||
| 336 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 337 | int ret; | ||
| 338 | |||
| 339 | /* We can't do interlaced modes if we don't have the SPU_ADV_REG */ | ||
| 340 | if (!priv->variant->has_spu_adv_reg && | ||
| 341 | adj->flags & DRM_MODE_FLAG_INTERLACE) | ||
| 342 | return false; | ||
| 343 | |||
| 344 | /* Check whether the display mode is possible */ | ||
| 345 | ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL); | ||
| 346 | if (ret) | ||
| 347 | return false; | ||
| 348 | |||
| 349 | return true; | ||
| 350 | } | ||
| 351 | |||
| 352 | void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) | ||
| 353 | { | ||
| 354 | struct armada_vbl_event *e, *n; | ||
| 355 | void __iomem *base = dcrtc->base; | ||
| 356 | |||
| 357 | if (stat & DMA_FF_UNDERFLOW) | ||
| 358 | DRM_ERROR("video underflow on crtc %u\n", dcrtc->num); | ||
| 359 | if (stat & GRA_FF_UNDERFLOW) | ||
| 360 | DRM_ERROR("graphics underflow on crtc %u\n", dcrtc->num); | ||
| 361 | |||
| 362 | if (stat & VSYNC_IRQ) | ||
| 363 | drm_handle_vblank(dcrtc->crtc.dev, dcrtc->num); | ||
| 364 | |||
| 365 | spin_lock(&dcrtc->irq_lock); | ||
| 366 | |||
| 367 | list_for_each_entry_safe(e, n, &dcrtc->vbl_list, node) { | ||
| 368 | list_del_init(&e->node); | ||
| 369 | drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); | ||
| 370 | e->fn(dcrtc, e->data); | ||
| 371 | } | ||
| 372 | |||
| 373 | if (stat & GRA_FRAME_IRQ && dcrtc->interlaced) { | ||
| 374 | int i = stat & GRA_FRAME_IRQ0 ? 0 : 1; | ||
| 375 | uint32_t val; | ||
| 376 | |||
| 377 | writel_relaxed(dcrtc->v[i].spu_v_porch, base + LCD_SPU_V_PORCH); | ||
| 378 | writel_relaxed(dcrtc->v[i].spu_v_h_total, | ||
| 379 | base + LCD_SPUT_V_H_TOTAL); | ||
| 380 | |||
| 381 | val = readl_relaxed(base + LCD_SPU_ADV_REG); | ||
| 382 | val &= ~(ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN); | ||
| 383 | val |= dcrtc->v[i].spu_adv_reg; | ||
| 384 | writel_relaxed(val, dcrtc->base + LCD_SPU_ADV_REG); | ||
| 385 | } | ||
| 386 | spin_unlock(&dcrtc->irq_lock); | ||
| 387 | |||
| 388 | if (stat & GRA_FRAME_IRQ) { | ||
| 389 | struct drm_device *dev = dcrtc->crtc.dev; | ||
| 390 | |||
| 391 | spin_lock(&dev->event_lock); | ||
| 392 | if (dcrtc->frame_work) | ||
| 393 | armada_drm_crtc_complete_frame_work(dcrtc); | ||
| 394 | spin_unlock(&dev->event_lock); | ||
| 395 | |||
| 396 | wake_up(&dcrtc->frame_wait); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | /* These are locked by dev->vbl_lock */ | ||
| 401 | void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) | ||
| 402 | { | ||
| 403 | if (dcrtc->irq_ena & mask) { | ||
| 404 | dcrtc->irq_ena &= ~mask; | ||
| 405 | writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | void armada_drm_crtc_enable_irq(struct armada_crtc *dcrtc, u32 mask) | ||
| 410 | { | ||
| 411 | if ((dcrtc->irq_ena & mask) != mask) { | ||
| 412 | dcrtc->irq_ena |= mask; | ||
| 413 | writel(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); | ||
| 414 | if (readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR) & mask) | ||
| 415 | writel(0, dcrtc->base + LCD_SPU_IRQ_ISR); | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | static uint32_t armada_drm_crtc_calculate_csc(struct armada_crtc *dcrtc) | ||
| 420 | { | ||
| 421 | struct drm_display_mode *adj = &dcrtc->crtc.mode; | ||
| 422 | uint32_t val = 0; | ||
| 423 | |||
| 424 | if (dcrtc->csc_yuv_mode == CSC_YUV_CCIR709) | ||
| 425 | val |= CFG_CSC_YUV_CCIR709; | ||
| 426 | if (dcrtc->csc_rgb_mode == CSC_RGB_STUDIO) | ||
| 427 | val |= CFG_CSC_RGB_STUDIO; | ||
| 428 | |||
| 429 | /* | ||
| 430 | * In auto mode, set the colorimetry, based upon the HDMI spec. | ||
| 431 | * 1280x720p, 1920x1080p and 1920x1080i use ITU709, others use | ||
| 432 | * ITU601. It may be more appropriate to set this depending on | ||
| 433 | * the source - but what if the graphic frame is YUV and the | ||
| 434 | * video frame is RGB? | ||
| 435 | */ | ||
| 436 | if ((adj->hdisplay == 1280 && adj->vdisplay == 720 && | ||
| 437 | !(adj->flags & DRM_MODE_FLAG_INTERLACE)) || | ||
| 438 | (adj->hdisplay == 1920 && adj->vdisplay == 1080)) { | ||
| 439 | if (dcrtc->csc_yuv_mode == CSC_AUTO) | ||
| 440 | val |= CFG_CSC_YUV_CCIR709; | ||
| 441 | } | ||
| 442 | |||
| 443 | /* | ||
| 444 | * We assume we're connected to a TV-like device, so the YUV->RGB | ||
| 445 | * conversion should produce a limited range. We should set this | ||
| 446 | * depending on the connectors attached to this CRTC, and what | ||
| 447 | * kind of device they report being connected. | ||
| 448 | */ | ||
| 449 | if (dcrtc->csc_rgb_mode == CSC_AUTO) | ||
| 450 | val |= CFG_CSC_RGB_STUDIO; | ||
| 451 | |||
| 452 | return val; | ||
| 453 | } | ||
| 454 | |||
| 455 | /* The mode_config.mutex will be held for this call */ | ||
| 456 | static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, | ||
| 457 | struct drm_display_mode *mode, struct drm_display_mode *adj, | ||
| 458 | int x, int y, struct drm_framebuffer *old_fb) | ||
| 459 | { | ||
| 460 | struct armada_private *priv = crtc->dev->dev_private; | ||
| 461 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 462 | struct armada_regs regs[17]; | ||
| 463 | uint32_t lm, rm, tm, bm, val, sclk; | ||
| 464 | unsigned long flags; | ||
| 465 | unsigned i; | ||
| 466 | bool interlaced; | ||
| 467 | |||
| 468 | drm_framebuffer_reference(crtc->fb); | ||
| 469 | |||
| 470 | interlaced = !!(adj->flags & DRM_MODE_FLAG_INTERLACE); | ||
| 471 | |||
| 472 | i = armada_drm_crtc_calc_fb(dcrtc->crtc.fb, x, y, regs, interlaced); | ||
| 473 | |||
| 474 | rm = adj->crtc_hsync_start - adj->crtc_hdisplay; | ||
| 475 | lm = adj->crtc_htotal - adj->crtc_hsync_end; | ||
| 476 | bm = adj->crtc_vsync_start - adj->crtc_vdisplay; | ||
| 477 | tm = adj->crtc_vtotal - adj->crtc_vsync_end; | ||
| 478 | |||
| 479 | DRM_DEBUG_DRIVER("H: %d %d %d %d lm %d rm %d\n", | ||
| 480 | adj->crtc_hdisplay, | ||
| 481 | adj->crtc_hsync_start, | ||
| 482 | adj->crtc_hsync_end, | ||
| 483 | adj->crtc_htotal, lm, rm); | ||
| 484 | DRM_DEBUG_DRIVER("V: %d %d %d %d tm %d bm %d\n", | ||
| 485 | adj->crtc_vdisplay, | ||
| 486 | adj->crtc_vsync_start, | ||
| 487 | adj->crtc_vsync_end, | ||
| 488 | adj->crtc_vtotal, tm, bm); | ||
| 489 | |||
| 490 | /* Wait for pending flips to complete */ | ||
| 491 | wait_event(dcrtc->frame_wait, !dcrtc->frame_work); | ||
| 492 | |||
| 493 | drm_vblank_pre_modeset(crtc->dev, dcrtc->num); | ||
| 494 | |||
| 495 | crtc->mode = *adj; | ||
| 496 | |||
| 497 | val = dcrtc->dumb_ctrl & ~CFG_DUMB_ENA; | ||
| 498 | if (val != dcrtc->dumb_ctrl) { | ||
| 499 | dcrtc->dumb_ctrl = val; | ||
| 500 | writel_relaxed(val, dcrtc->base + LCD_SPU_DUMB_CTRL); | ||
| 501 | } | ||
| 502 | |||
| 503 | /* Now compute the divider for real */ | ||
| 504 | priv->variant->crtc_compute_clock(dcrtc, adj, &sclk); | ||
| 505 | |||
| 506 | /* Ensure graphic fifo is enabled */ | ||
| 507 | armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); | ||
| 508 | armada_reg_queue_set(regs, i, sclk, LCD_CFG_SCLK_DIV); | ||
| 509 | |||
| 510 | if (interlaced ^ dcrtc->interlaced) { | ||
| 511 | if (adj->flags & DRM_MODE_FLAG_INTERLACE) | ||
| 512 | drm_vblank_get(dcrtc->crtc.dev, dcrtc->num); | ||
| 513 | else | ||
| 514 | drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); | ||
| 515 | dcrtc->interlaced = interlaced; | ||
| 516 | } | ||
| 517 | |||
| 518 | spin_lock_irqsave(&dcrtc->irq_lock, flags); | ||
| 519 | |||
| 520 | /* Even interlaced/progressive frame */ | ||
| 521 | dcrtc->v[1].spu_v_h_total = adj->crtc_vtotal << 16 | | ||
| 522 | adj->crtc_htotal; | ||
| 523 | dcrtc->v[1].spu_v_porch = tm << 16 | bm; | ||
| 524 | val = adj->crtc_hsync_start; | ||
| 525 | dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; | ||
| 526 | |||
| 527 | if (interlaced) { | ||
| 528 | /* Odd interlaced frame */ | ||
| 529 | dcrtc->v[0].spu_v_h_total = dcrtc->v[1].spu_v_h_total + | ||
| 530 | (1 << 16); | ||
| 531 | dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; | ||
| 532 | val = adj->crtc_hsync_start - adj->crtc_htotal / 2; | ||
| 533 | dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN; | ||
| 534 | } else { | ||
| 535 | dcrtc->v[0] = dcrtc->v[1]; | ||
| 536 | } | ||
| 537 | |||
| 538 | val = adj->crtc_vdisplay << 16 | adj->crtc_hdisplay; | ||
| 539 | |||
| 540 | armada_reg_queue_set(regs, i, val, LCD_SPU_V_H_ACTIVE); | ||
| 541 | armada_reg_queue_set(regs, i, val, LCD_SPU_GRA_HPXL_VLN); | ||
| 542 | armada_reg_queue_set(regs, i, val, LCD_SPU_GZM_HPXL_VLN); | ||
| 543 | armada_reg_queue_set(regs, i, (lm << 16) | rm, LCD_SPU_H_PORCH); | ||
| 544 | armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_porch, LCD_SPU_V_PORCH); | ||
| 545 | armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, | ||
| 546 | LCD_SPUT_V_H_TOTAL); | ||
| 547 | |||
| 548 | if (priv->variant->has_spu_adv_reg) | ||
| 549 | armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, | ||
| 550 | ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | | ||
| 551 | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); | ||
| 552 | |||
| 553 | val = CFG_GRA_ENA | CFG_GRA_HSMOOTH; | ||
| 554 | val |= CFG_GRA_FMT(drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt); | ||
| 555 | val |= CFG_GRA_MOD(drm_fb_to_armada_fb(dcrtc->crtc.fb)->mod); | ||
| 556 | |||
| 557 | if (drm_fb_to_armada_fb(dcrtc->crtc.fb)->fmt > CFG_420) | ||
| 558 | val |= CFG_PALETTE_ENA; | ||
| 559 | |||
| 560 | if (interlaced) | ||
| 561 | val |= CFG_GRA_FTOGGLE; | ||
| 562 | |||
| 563 | armada_reg_queue_mod(regs, i, val, CFG_GRAFORMAT | | ||
| 564 | CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV | | ||
| 565 | CFG_SWAPYU | CFG_YUV2RGB) | | ||
| 566 | CFG_PALETTE_ENA | CFG_GRA_FTOGGLE, | ||
| 567 | LCD_SPU_DMA_CTRL0); | ||
| 568 | |||
| 569 | val = adj->flags & DRM_MODE_FLAG_NVSYNC ? CFG_VSYNC_INV : 0; | ||
| 570 | armada_reg_queue_mod(regs, i, val, CFG_VSYNC_INV, LCD_SPU_DMA_CTRL1); | ||
| 571 | |||
| 572 | val = dcrtc->spu_iopad_ctrl | armada_drm_crtc_calculate_csc(dcrtc); | ||
| 573 | armada_reg_queue_set(regs, i, val, LCD_SPU_IOPAD_CONTROL); | ||
| 574 | armada_reg_queue_end(regs, i); | ||
| 575 | |||
| 576 | armada_drm_crtc_update_regs(dcrtc, regs); | ||
| 577 | spin_unlock_irqrestore(&dcrtc->irq_lock, flags); | ||
| 578 | |||
| 579 | armada_drm_crtc_update(dcrtc); | ||
| 580 | |||
| 581 | drm_vblank_post_modeset(crtc->dev, dcrtc->num); | ||
| 582 | armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms)); | ||
| 583 | |||
| 584 | return 0; | ||
| 585 | } | ||
| 586 | |||
| 587 | /* The mode_config.mutex will be held for this call */ | ||
| 588 | static int armada_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
| 589 | struct drm_framebuffer *old_fb) | ||
| 590 | { | ||
| 591 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 592 | struct armada_regs regs[4]; | ||
| 593 | unsigned i; | ||
| 594 | |||
| 595 | i = armada_drm_crtc_calc_fb(crtc->fb, crtc->x, crtc->y, regs, | ||
| 596 | dcrtc->interlaced); | ||
| 597 | armada_reg_queue_end(regs, i); | ||
| 598 | |||
| 599 | /* Wait for pending flips to complete */ | ||
| 600 | wait_event(dcrtc->frame_wait, !dcrtc->frame_work); | ||
| 601 | |||
| 602 | /* Take a reference to the new fb as we're using it */ | ||
| 603 | drm_framebuffer_reference(crtc->fb); | ||
| 604 | |||
| 605 | /* Update the base in the CRTC */ | ||
| 606 | armada_drm_crtc_update_regs(dcrtc, regs); | ||
| 607 | |||
| 608 | /* Drop our previously held reference */ | ||
| 609 | armada_drm_crtc_finish_fb(dcrtc, old_fb, dpms_blanked(dcrtc->dpms)); | ||
| 610 | |||
| 611 | return 0; | ||
| 612 | } | ||
| 613 | |||
| 614 | static void armada_drm_crtc_load_lut(struct drm_crtc *crtc) | ||
| 615 | { | ||
| 616 | } | ||
| 617 | |||
| 618 | /* The mode_config.mutex will be held for this call */ | ||
| 619 | static void armada_drm_crtc_disable(struct drm_crtc *crtc) | ||
| 620 | { | ||
| 621 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 622 | |||
| 623 | armada_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | ||
| 624 | armada_drm_crtc_finish_fb(dcrtc, crtc->fb, true); | ||
| 625 | |||
| 626 | /* Power down most RAMs and FIFOs */ | ||
| 627 | writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | | ||
| 628 | CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 | | ||
| 629 | CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); | ||
| 630 | } | ||
| 631 | |||
| 632 | static const struct drm_crtc_helper_funcs armada_crtc_helper_funcs = { | ||
| 633 | .dpms = armada_drm_crtc_dpms, | ||
| 634 | .prepare = armada_drm_crtc_prepare, | ||
| 635 | .commit = armada_drm_crtc_commit, | ||
| 636 | .mode_fixup = armada_drm_crtc_mode_fixup, | ||
| 637 | .mode_set = armada_drm_crtc_mode_set, | ||
| 638 | .mode_set_base = armada_drm_crtc_mode_set_base, | ||
| 639 | .load_lut = armada_drm_crtc_load_lut, | ||
| 640 | .disable = armada_drm_crtc_disable, | ||
| 641 | }; | ||
| 642 | |||
| 643 | static void armada_drm_crtc_destroy(struct drm_crtc *crtc) | ||
| 644 | { | ||
| 645 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 646 | struct armada_private *priv = crtc->dev->dev_private; | ||
| 647 | |||
| 648 | priv->dcrtc[dcrtc->num] = NULL; | ||
| 649 | drm_crtc_cleanup(&dcrtc->crtc); | ||
| 650 | |||
| 651 | if (!IS_ERR(dcrtc->clk)) | ||
| 652 | clk_disable_unprepare(dcrtc->clk); | ||
| 653 | |||
| 654 | kfree(dcrtc); | ||
| 655 | } | ||
| 656 | |||
| 657 | /* | ||
| 658 | * The mode_config lock is held here, to prevent races between this | ||
| 659 | * and a mode_set. | ||
| 660 | */ | ||
| 661 | static int armada_drm_crtc_page_flip(struct drm_crtc *crtc, | ||
| 662 | struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) | ||
| 663 | { | ||
| 664 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 665 | struct armada_frame_work *work; | ||
| 666 | struct drm_device *dev = crtc->dev; | ||
| 667 | unsigned long flags; | ||
| 668 | unsigned i; | ||
| 669 | int ret; | ||
| 670 | |||
| 671 | /* We don't support changing the pixel format */ | ||
| 672 | if (fb->pixel_format != crtc->fb->pixel_format) | ||
| 673 | return -EINVAL; | ||
| 674 | |||
| 675 | work = kmalloc(sizeof(*work), GFP_KERNEL); | ||
| 676 | if (!work) | ||
| 677 | return -ENOMEM; | ||
| 678 | |||
| 679 | work->event = event; | ||
| 680 | work->old_fb = dcrtc->crtc.fb; | ||
| 681 | |||
| 682 | i = armada_drm_crtc_calc_fb(fb, crtc->x, crtc->y, work->regs, | ||
| 683 | dcrtc->interlaced); | ||
| 684 | armada_reg_queue_end(work->regs, i); | ||
| 685 | |||
| 686 | /* | ||
| 687 | * Hold the old framebuffer for the work - DRM appears to drop our | ||
| 688 | * reference to the old framebuffer in drm_mode_page_flip_ioctl(). | ||
| 689 | */ | ||
| 690 | drm_framebuffer_reference(work->old_fb); | ||
| 691 | |||
| 692 | ret = armada_drm_crtc_queue_frame_work(dcrtc, work); | ||
| 693 | if (ret) { | ||
| 694 | /* | ||
| 695 | * Undo our reference above; DRM does not drop the reference | ||
| 696 | * to this object on error, so that's okay. | ||
| 697 | */ | ||
| 698 | drm_framebuffer_unreference(work->old_fb); | ||
| 699 | kfree(work); | ||
| 700 | return ret; | ||
| 701 | } | ||
| 702 | |||
| 703 | /* | ||
| 704 | * Don't take a reference on the new framebuffer; | ||
| 705 | * drm_mode_page_flip_ioctl() has already grabbed a reference and | ||
| 706 | * will _not_ drop that reference on successful return from this | ||
| 707 | * function. Simply mark this new framebuffer as the current one. | ||
| 708 | */ | ||
| 709 | dcrtc->crtc.fb = fb; | ||
| 710 | |||
| 711 | /* | ||
| 712 | * Finally, if the display is blanked, we won't receive an | ||
| 713 | * interrupt, so complete it now. | ||
| 714 | */ | ||
| 715 | if (dpms_blanked(dcrtc->dpms)) { | ||
| 716 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 717 | if (dcrtc->frame_work) | ||
| 718 | armada_drm_crtc_complete_frame_work(dcrtc); | ||
| 719 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 720 | } | ||
| 721 | |||
| 722 | return 0; | ||
| 723 | } | ||
| 724 | |||
| 725 | static int | ||
| 726 | armada_drm_crtc_set_property(struct drm_crtc *crtc, | ||
| 727 | struct drm_property *property, uint64_t val) | ||
| 728 | { | ||
| 729 | struct armada_private *priv = crtc->dev->dev_private; | ||
| 730 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 731 | bool update_csc = false; | ||
| 732 | |||
| 733 | if (property == priv->csc_yuv_prop) { | ||
| 734 | dcrtc->csc_yuv_mode = val; | ||
| 735 | update_csc = true; | ||
| 736 | } else if (property == priv->csc_rgb_prop) { | ||
| 737 | dcrtc->csc_rgb_mode = val; | ||
| 738 | update_csc = true; | ||
| 739 | } | ||
| 740 | |||
| 741 | if (update_csc) { | ||
| 742 | uint32_t val; | ||
| 743 | |||
| 744 | val = dcrtc->spu_iopad_ctrl | | ||
| 745 | armada_drm_crtc_calculate_csc(dcrtc); | ||
| 746 | writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL); | ||
| 747 | } | ||
| 748 | |||
| 749 | return 0; | ||
| 750 | } | ||
| 751 | |||
| 752 | static struct drm_crtc_funcs armada_crtc_funcs = { | ||
| 753 | .destroy = armada_drm_crtc_destroy, | ||
| 754 | .set_config = drm_crtc_helper_set_config, | ||
| 755 | .page_flip = armada_drm_crtc_page_flip, | ||
| 756 | .set_property = armada_drm_crtc_set_property, | ||
| 757 | }; | ||
| 758 | |||
| 759 | static struct drm_prop_enum_list armada_drm_csc_yuv_enum_list[] = { | ||
| 760 | { CSC_AUTO, "Auto" }, | ||
| 761 | { CSC_YUV_CCIR601, "CCIR601" }, | ||
| 762 | { CSC_YUV_CCIR709, "CCIR709" }, | ||
| 763 | }; | ||
| 764 | |||
| 765 | static struct drm_prop_enum_list armada_drm_csc_rgb_enum_list[] = { | ||
| 766 | { CSC_AUTO, "Auto" }, | ||
| 767 | { CSC_RGB_COMPUTER, "Computer system" }, | ||
| 768 | { CSC_RGB_STUDIO, "Studio" }, | ||
| 769 | }; | ||
| 770 | |||
| 771 | static int armada_drm_crtc_create_properties(struct drm_device *dev) | ||
| 772 | { | ||
| 773 | struct armada_private *priv = dev->dev_private; | ||
| 774 | |||
| 775 | if (priv->csc_yuv_prop) | ||
| 776 | return 0; | ||
| 777 | |||
| 778 | priv->csc_yuv_prop = drm_property_create_enum(dev, 0, | ||
| 779 | "CSC_YUV", armada_drm_csc_yuv_enum_list, | ||
| 780 | ARRAY_SIZE(armada_drm_csc_yuv_enum_list)); | ||
| 781 | priv->csc_rgb_prop = drm_property_create_enum(dev, 0, | ||
| 782 | "CSC_RGB", armada_drm_csc_rgb_enum_list, | ||
| 783 | ARRAY_SIZE(armada_drm_csc_rgb_enum_list)); | ||
| 784 | |||
| 785 | if (!priv->csc_yuv_prop || !priv->csc_rgb_prop) | ||
| 786 | return -ENOMEM; | ||
| 787 | |||
| 788 | return 0; | ||
| 789 | } | ||
| 790 | |||
| 791 | int armada_drm_crtc_create(struct drm_device *dev, unsigned num, | ||
| 792 | struct resource *res) | ||
| 793 | { | ||
| 794 | struct armada_private *priv = dev->dev_private; | ||
| 795 | struct armada_crtc *dcrtc; | ||
| 796 | void __iomem *base; | ||
| 797 | int ret; | ||
| 798 | |||
| 799 | ret = armada_drm_crtc_create_properties(dev); | ||
| 800 | if (ret) | ||
| 801 | return ret; | ||
| 802 | |||
| 803 | base = devm_request_and_ioremap(dev->dev, res); | ||
| 804 | if (!base) { | ||
| 805 | DRM_ERROR("failed to ioremap register\n"); | ||
| 806 | return -ENOMEM; | ||
| 807 | } | ||
| 808 | |||
| 809 | dcrtc = kzalloc(sizeof(*dcrtc), GFP_KERNEL); | ||
| 810 | if (!dcrtc) { | ||
| 811 | DRM_ERROR("failed to allocate Armada crtc\n"); | ||
| 812 | return -ENOMEM; | ||
| 813 | } | ||
| 814 | |||
| 815 | dcrtc->base = base; | ||
| 816 | dcrtc->num = num; | ||
| 817 | dcrtc->clk = ERR_PTR(-EINVAL); | ||
| 818 | dcrtc->csc_yuv_mode = CSC_AUTO; | ||
| 819 | dcrtc->csc_rgb_mode = CSC_AUTO; | ||
| 820 | dcrtc->cfg_dumb_ctrl = DUMB24_RGB888_0; | ||
| 821 | dcrtc->spu_iopad_ctrl = CFG_VSCALE_LN_EN | CFG_IOPAD_DUMB24; | ||
| 822 | spin_lock_init(&dcrtc->irq_lock); | ||
| 823 | dcrtc->irq_ena = CLEAN_SPU_IRQ_ISR; | ||
| 824 | INIT_LIST_HEAD(&dcrtc->vbl_list); | ||
| 825 | init_waitqueue_head(&dcrtc->frame_wait); | ||
| 826 | |||
| 827 | /* Initialize some registers which we don't otherwise set */ | ||
| 828 | writel_relaxed(0x00000001, dcrtc->base + LCD_CFG_SCLK_DIV); | ||
| 829 | writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_BLANKCOLOR); | ||
| 830 | writel_relaxed(dcrtc->spu_iopad_ctrl, | ||
| 831 | dcrtc->base + LCD_SPU_IOPAD_CONTROL); | ||
| 832 | writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_SRAM_PARA0); | ||
| 833 | writel_relaxed(CFG_PDWN256x32 | CFG_PDWN256x24 | CFG_PDWN256x8 | | ||
| 834 | CFG_PDWN32x32 | CFG_PDWN16x66 | CFG_PDWN32x66 | | ||
| 835 | CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); | ||
| 836 | writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); | ||
| 837 | writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN); | ||
| 838 | |||
| 839 | if (priv->variant->crtc_init) { | ||
| 840 | ret = priv->variant->crtc_init(dcrtc); | ||
| 841 | if (ret) { | ||
| 842 | kfree(dcrtc); | ||
| 843 | return ret; | ||
| 844 | } | ||
| 845 | } | ||
| 846 | |||
| 847 | /* Ensure AXI pipeline is enabled */ | ||
| 848 | armada_updatel(CFG_ARBFAST_ENA, 0, dcrtc->base + LCD_SPU_DMA_CTRL0); | ||
| 849 | |||
| 850 | priv->dcrtc[dcrtc->num] = dcrtc; | ||
| 851 | |||
| 852 | drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs); | ||
| 853 | drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); | ||
| 854 | |||
| 855 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, | ||
| 856 | dcrtc->csc_yuv_mode); | ||
| 857 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, | ||
| 858 | dcrtc->csc_rgb_mode); | ||
| 859 | |||
| 860 | return armada_overlay_plane_create(dev, 1 << dcrtc->num); | ||
| 861 | } | ||
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h new file mode 100644 index 000000000000..972da535921d --- /dev/null +++ b/drivers/gpu/drm/armada/armada_crtc.h | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #ifndef ARMADA_CRTC_H | ||
| 9 | #define ARMADA_CRTC_H | ||
| 10 | |||
| 11 | struct armada_gem_object; | ||
| 12 | |||
| 13 | struct armada_regs { | ||
| 14 | uint32_t offset; | ||
| 15 | uint32_t mask; | ||
| 16 | uint32_t val; | ||
| 17 | }; | ||
| 18 | |||
| 19 | #define armada_reg_queue_mod(_r, _i, _v, _m, _o) \ | ||
| 20 | do { \ | ||
| 21 | struct armada_regs *__reg = _r; \ | ||
| 22 | __reg[_i].offset = _o; \ | ||
| 23 | __reg[_i].mask = ~(_m); \ | ||
| 24 | __reg[_i].val = _v; \ | ||
| 25 | _i++; \ | ||
| 26 | } while (0) | ||
| 27 | |||
| 28 | #define armada_reg_queue_set(_r, _i, _v, _o) \ | ||
| 29 | armada_reg_queue_mod(_r, _i, _v, ~0, _o) | ||
| 30 | |||
| 31 | #define armada_reg_queue_end(_r, _i) \ | ||
| 32 | armada_reg_queue_mod(_r, _i, 0, 0, ~0) | ||
| 33 | |||
| 34 | struct armada_frame_work; | ||
| 35 | |||
| 36 | struct armada_crtc { | ||
| 37 | struct drm_crtc crtc; | ||
| 38 | unsigned num; | ||
| 39 | void __iomem *base; | ||
| 40 | struct clk *clk; | ||
| 41 | struct { | ||
| 42 | uint32_t spu_v_h_total; | ||
| 43 | uint32_t spu_v_porch; | ||
| 44 | uint32_t spu_adv_reg; | ||
| 45 | } v[2]; | ||
| 46 | bool interlaced; | ||
| 47 | uint8_t csc_yuv_mode; | ||
| 48 | uint8_t csc_rgb_mode; | ||
| 49 | |||
| 50 | struct drm_plane *plane; | ||
| 51 | |||
| 52 | int dpms; | ||
| 53 | uint32_t cfg_dumb_ctrl; | ||
| 54 | uint32_t dumb_ctrl; | ||
| 55 | uint32_t spu_iopad_ctrl; | ||
| 56 | |||
| 57 | wait_queue_head_t frame_wait; | ||
| 58 | struct armada_frame_work *frame_work; | ||
| 59 | |||
| 60 | spinlock_t irq_lock; | ||
| 61 | uint32_t irq_ena; | ||
| 62 | struct list_head vbl_list; | ||
| 63 | }; | ||
| 64 | #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) | ||
| 65 | |||
| 66 | int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *); | ||
| 67 | void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); | ||
| 68 | void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); | ||
| 69 | void armada_drm_crtc_irq(struct armada_crtc *, u32); | ||
| 70 | void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); | ||
| 71 | void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); | ||
| 72 | void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); | ||
| 73 | |||
| 74 | #endif | ||
diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c new file mode 100644 index 000000000000..612f3753cd92 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_debugfs.c | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * Rewritten from the dovefb driver, and Armada510 manuals. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #include <linux/ctype.h> | ||
| 10 | #include <linux/debugfs.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/seq_file.h> | ||
| 13 | #include <drm/drmP.h> | ||
| 14 | #include "armada_crtc.h" | ||
| 15 | #include "armada_drm.h" | ||
| 16 | |||
| 17 | static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data) | ||
| 18 | { | ||
| 19 | struct drm_info_node *node = m->private; | ||
| 20 | struct drm_device *dev = node->minor->dev; | ||
| 21 | struct armada_private *priv = dev->dev_private; | ||
| 22 | int ret; | ||
| 23 | |||
| 24 | mutex_lock(&dev->struct_mutex); | ||
| 25 | ret = drm_mm_dump_table(m, &priv->linear); | ||
| 26 | mutex_unlock(&dev->struct_mutex); | ||
| 27 | |||
| 28 | return ret; | ||
| 29 | } | ||
| 30 | |||
| 31 | static int armada_debugfs_reg_show(struct seq_file *m, void *data) | ||
| 32 | { | ||
| 33 | struct drm_device *dev = m->private; | ||
| 34 | struct armada_private *priv = dev->dev_private; | ||
| 35 | int n, i; | ||
| 36 | |||
| 37 | if (priv) { | ||
| 38 | for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { | ||
| 39 | struct armada_crtc *dcrtc = priv->dcrtc[n]; | ||
| 40 | if (!dcrtc) | ||
| 41 | continue; | ||
| 42 | |||
| 43 | for (i = 0x84; i <= 0x1c4; i += 4) { | ||
| 44 | uint32_t v = readl_relaxed(dcrtc->base + i); | ||
| 45 | seq_printf(m, "%u: 0x%04x: 0x%08x\n", n, i, v); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | return 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | static int armada_debugfs_reg_r_open(struct inode *inode, struct file *file) | ||
| 54 | { | ||
| 55 | return single_open(file, armada_debugfs_reg_show, inode->i_private); | ||
| 56 | } | ||
| 57 | |||
| 58 | static const struct file_operations fops_reg_r = { | ||
| 59 | .owner = THIS_MODULE, | ||
| 60 | .open = armada_debugfs_reg_r_open, | ||
| 61 | .read = seq_read, | ||
| 62 | .llseek = seq_lseek, | ||
| 63 | .release = single_release, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static int armada_debugfs_write(struct file *file, const char __user *ptr, | ||
| 67 | size_t len, loff_t *off) | ||
| 68 | { | ||
| 69 | struct drm_device *dev = file->private_data; | ||
| 70 | struct armada_private *priv = dev->dev_private; | ||
| 71 | struct armada_crtc *dcrtc = priv->dcrtc[0]; | ||
| 72 | char buf[32], *p; | ||
| 73 | uint32_t reg, val; | ||
| 74 | int ret; | ||
| 75 | |||
| 76 | if (*off != 0) | ||
| 77 | return 0; | ||
| 78 | |||
| 79 | if (len > sizeof(buf) - 1) | ||
| 80 | len = sizeof(buf) - 1; | ||
| 81 | |||
| 82 | ret = strncpy_from_user(buf, ptr, len); | ||
| 83 | if (ret < 0) | ||
| 84 | return ret; | ||
| 85 | buf[len] = '\0'; | ||
| 86 | |||
| 87 | reg = simple_strtoul(buf, &p, 16); | ||
| 88 | if (!isspace(*p)) | ||
| 89 | return -EINVAL; | ||
| 90 | val = simple_strtoul(p + 1, NULL, 16); | ||
| 91 | |||
| 92 | if (reg >= 0x84 && reg <= 0x1c4) | ||
| 93 | writel(val, dcrtc->base + reg); | ||
| 94 | |||
| 95 | return len; | ||
| 96 | } | ||
| 97 | |||
| 98 | static int armada_debugfs_reg_w_open(struct inode *inode, struct file *file) | ||
| 99 | { | ||
| 100 | file->private_data = inode->i_private; | ||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | static const struct file_operations fops_reg_w = { | ||
| 105 | .owner = THIS_MODULE, | ||
| 106 | .open = armada_debugfs_reg_w_open, | ||
| 107 | .write = armada_debugfs_write, | ||
| 108 | .llseek = noop_llseek, | ||
| 109 | }; | ||
| 110 | |||
| 111 | static struct drm_info_list armada_debugfs_list[] = { | ||
| 112 | { "gem_linear", armada_debugfs_gem_linear_show, 0 }, | ||
| 113 | }; | ||
| 114 | #define ARMADA_DEBUGFS_ENTRIES ARRAY_SIZE(armada_debugfs_list) | ||
| 115 | |||
| 116 | static int drm_add_fake_info_node(struct drm_minor *minor, struct dentry *ent, | ||
| 117 | const void *key) | ||
| 118 | { | ||
| 119 | struct drm_info_node *node; | ||
| 120 | |||
| 121 | node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); | ||
| 122 | if (node == NULL) { | ||
| 123 | debugfs_remove(ent); | ||
| 124 | return -ENOMEM; | ||
| 125 | } | ||
| 126 | |||
| 127 | node->minor = minor; | ||
| 128 | node->dent = ent; | ||
| 129 | node->info_ent = (void *) key; | ||
| 130 | |||
| 131 | mutex_lock(&minor->debugfs_lock); | ||
| 132 | list_add(&node->list, &minor->debugfs_list); | ||
| 133 | mutex_unlock(&minor->debugfs_lock); | ||
| 134 | |||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int armada_debugfs_create(struct dentry *root, struct drm_minor *minor, | ||
| 139 | const char *name, umode_t mode, const struct file_operations *fops) | ||
| 140 | { | ||
| 141 | struct dentry *de; | ||
| 142 | |||
| 143 | de = debugfs_create_file(name, mode, root, minor->dev, fops); | ||
| 144 | |||
| 145 | return drm_add_fake_info_node(minor, de, fops); | ||
| 146 | } | ||
| 147 | |||
| 148 | int armada_drm_debugfs_init(struct drm_minor *minor) | ||
| 149 | { | ||
| 150 | int ret; | ||
| 151 | |||
| 152 | ret = drm_debugfs_create_files(armada_debugfs_list, | ||
| 153 | ARMADA_DEBUGFS_ENTRIES, | ||
| 154 | minor->debugfs_root, minor); | ||
| 155 | if (ret) | ||
| 156 | return ret; | ||
| 157 | |||
| 158 | ret = armada_debugfs_create(minor->debugfs_root, minor, | ||
| 159 | "reg", S_IFREG | S_IRUSR, &fops_reg_r); | ||
| 160 | if (ret) | ||
| 161 | goto err_1; | ||
| 162 | |||
| 163 | ret = armada_debugfs_create(minor->debugfs_root, minor, | ||
| 164 | "reg_wr", S_IFREG | S_IWUSR, &fops_reg_w); | ||
| 165 | if (ret) | ||
| 166 | goto err_2; | ||
| 167 | return ret; | ||
| 168 | |||
| 169 | err_2: | ||
| 170 | drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor); | ||
| 171 | err_1: | ||
| 172 | drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES, | ||
| 173 | minor); | ||
| 174 | return ret; | ||
| 175 | } | ||
| 176 | |||
| 177 | void armada_drm_debugfs_cleanup(struct drm_minor *minor) | ||
| 178 | { | ||
| 179 | drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_w, 1, minor); | ||
| 180 | drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor); | ||
| 181 | drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES, | ||
| 182 | minor); | ||
| 183 | } | ||
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h new file mode 100644 index 000000000000..e8c4f80c61d4 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_drm.h | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #ifndef ARMADA_DRM_H | ||
| 9 | #define ARMADA_DRM_H | ||
| 10 | |||
| 11 | #include <linux/kfifo.h> | ||
| 12 | #include <linux/io.h> | ||
| 13 | #include <linux/workqueue.h> | ||
| 14 | #include <drm/drmP.h> | ||
| 15 | |||
| 16 | struct armada_crtc; | ||
| 17 | struct armada_gem_object; | ||
| 18 | struct clk; | ||
| 19 | struct drm_fb_helper; | ||
| 20 | |||
| 21 | static inline void | ||
| 22 | armada_updatel(uint32_t val, uint32_t mask, void __iomem *ptr) | ||
| 23 | { | ||
| 24 | uint32_t ov, v; | ||
| 25 | |||
| 26 | ov = v = readl_relaxed(ptr); | ||
| 27 | v = (v & ~mask) | val; | ||
| 28 | if (ov != v) | ||
| 29 | writel_relaxed(v, ptr); | ||
| 30 | } | ||
| 31 | |||
| 32 | static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp) | ||
| 33 | { | ||
| 34 | uint32_t pitch = bpp != 4 ? width * ((bpp + 7) / 8) : width / 2; | ||
| 35 | |||
| 36 | /* 88AP510 spec recommends pitch be a multiple of 128 */ | ||
| 37 | return ALIGN(pitch, 128); | ||
| 38 | } | ||
| 39 | |||
| 40 | struct armada_vbl_event { | ||
| 41 | struct list_head node; | ||
| 42 | void *data; | ||
| 43 | void (*fn)(struct armada_crtc *, void *); | ||
| 44 | }; | ||
| 45 | void armada_drm_vbl_event_add(struct armada_crtc *, | ||
| 46 | struct armada_vbl_event *); | ||
| 47 | void armada_drm_vbl_event_remove(struct armada_crtc *, | ||
| 48 | struct armada_vbl_event *); | ||
| 49 | void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *, | ||
| 50 | struct armada_vbl_event *); | ||
| 51 | #define armada_drm_vbl_event_init(_e, _f, _d) do { \ | ||
| 52 | struct armada_vbl_event *__e = _e; \ | ||
| 53 | INIT_LIST_HEAD(&__e->node); \ | ||
| 54 | __e->data = _d; \ | ||
| 55 | __e->fn = _f; \ | ||
| 56 | } while (0) | ||
| 57 | |||
| 58 | |||
| 59 | struct armada_private; | ||
| 60 | |||
| 61 | struct armada_variant { | ||
| 62 | bool has_spu_adv_reg; | ||
| 63 | int (*init)(struct armada_private *, struct device *); | ||
| 64 | int (*crtc_init)(struct armada_crtc *); | ||
| 65 | int (*crtc_compute_clock)(struct armada_crtc *, | ||
| 66 | const struct drm_display_mode *, | ||
| 67 | uint32_t *); | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* Variant ops */ | ||
| 71 | extern const struct armada_variant armada510_ops; | ||
| 72 | |||
| 73 | struct armada_private { | ||
| 74 | const struct armada_variant *variant; | ||
| 75 | struct work_struct fb_unref_work; | ||
| 76 | DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); | ||
| 77 | struct drm_fb_helper *fbdev; | ||
| 78 | struct armada_crtc *dcrtc[2]; | ||
| 79 | struct drm_mm linear; | ||
| 80 | struct clk *extclk[2]; | ||
| 81 | struct drm_property *csc_yuv_prop; | ||
| 82 | struct drm_property *csc_rgb_prop; | ||
| 83 | struct drm_property *colorkey_prop; | ||
| 84 | struct drm_property *colorkey_min_prop; | ||
| 85 | struct drm_property *colorkey_max_prop; | ||
| 86 | struct drm_property *colorkey_val_prop; | ||
| 87 | struct drm_property *colorkey_alpha_prop; | ||
| 88 | struct drm_property *colorkey_mode_prop; | ||
| 89 | struct drm_property *brightness_prop; | ||
| 90 | struct drm_property *contrast_prop; | ||
| 91 | struct drm_property *saturation_prop; | ||
| 92 | #ifdef CONFIG_DEBUG_FS | ||
| 93 | struct dentry *de; | ||
| 94 | #endif | ||
| 95 | }; | ||
| 96 | |||
| 97 | void __armada_drm_queue_unref_work(struct drm_device *, | ||
| 98 | struct drm_framebuffer *); | ||
| 99 | void armada_drm_queue_unref_work(struct drm_device *, | ||
| 100 | struct drm_framebuffer *); | ||
| 101 | |||
| 102 | extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs; | ||
| 103 | |||
| 104 | int armada_fbdev_init(struct drm_device *); | ||
| 105 | void armada_fbdev_fini(struct drm_device *); | ||
| 106 | |||
| 107 | int armada_overlay_plane_create(struct drm_device *, unsigned long); | ||
| 108 | |||
| 109 | int armada_drm_debugfs_init(struct drm_minor *); | ||
| 110 | void armada_drm_debugfs_cleanup(struct drm_minor *); | ||
| 111 | |||
| 112 | #endif | ||
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c new file mode 100644 index 000000000000..7bfab9a286ae --- /dev/null +++ b/drivers/gpu/drm/armada/armada_drv.c | |||
| @@ -0,0 +1,380 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #include <linux/clk.h> | ||
| 9 | #include <linux/module.h> | ||
| 10 | #include <drm/drmP.h> | ||
| 11 | #include <drm/drm_crtc_helper.h> | ||
| 12 | #include "armada_crtc.h" | ||
| 13 | #include "armada_drm.h" | ||
| 14 | #include "armada_gem.h" | ||
| 15 | #include "armada_hw.h" | ||
| 16 | #include <drm/armada_drm.h> | ||
| 17 | #include "armada_ioctlP.h" | ||
| 18 | |||
| 19 | static void armada_drm_unref_work(struct work_struct *work) | ||
| 20 | { | ||
| 21 | struct armada_private *priv = | ||
| 22 | container_of(work, struct armada_private, fb_unref_work); | ||
| 23 | struct drm_framebuffer *fb; | ||
| 24 | |||
| 25 | while (kfifo_get(&priv->fb_unref, &fb)) | ||
| 26 | drm_framebuffer_unreference(fb); | ||
| 27 | } | ||
| 28 | |||
| 29 | /* Must be called with dev->event_lock held */ | ||
| 30 | void __armada_drm_queue_unref_work(struct drm_device *dev, | ||
| 31 | struct drm_framebuffer *fb) | ||
| 32 | { | ||
| 33 | struct armada_private *priv = dev->dev_private; | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Yes, we really must jump through these hoops just to store a | ||
| 37 | * _pointer_ to something into the kfifo. This is utterly insane | ||
| 38 | * and idiotic, because it kfifo requires the _data_ pointed to by | ||
| 39 | * the pointer const, not the pointer itself. Not only that, but | ||
| 40 | * you have to pass a pointer _to_ the pointer you want stored. | ||
| 41 | */ | ||
| 42 | const struct drm_framebuffer *silly_api_alert = fb; | ||
| 43 | WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert)); | ||
| 44 | schedule_work(&priv->fb_unref_work); | ||
| 45 | } | ||
| 46 | |||
| 47 | void armada_drm_queue_unref_work(struct drm_device *dev, | ||
| 48 | struct drm_framebuffer *fb) | ||
| 49 | { | ||
| 50 | unsigned long flags; | ||
| 51 | |||
| 52 | spin_lock_irqsave(&dev->event_lock, flags); | ||
| 53 | __armada_drm_queue_unref_work(dev, fb); | ||
| 54 | spin_unlock_irqrestore(&dev->event_lock, flags); | ||
| 55 | } | ||
| 56 | |||
| 57 | static int armada_drm_load(struct drm_device *dev, unsigned long flags) | ||
| 58 | { | ||
| 59 | const struct platform_device_id *id; | ||
| 60 | struct armada_private *priv; | ||
| 61 | struct resource *res[ARRAY_SIZE(priv->dcrtc)]; | ||
| 62 | struct resource *mem = NULL; | ||
| 63 | int ret, n, i; | ||
| 64 | |||
| 65 | memset(res, 0, sizeof(res)); | ||
| 66 | |||
| 67 | for (n = i = 0; ; n++) { | ||
| 68 | struct resource *r = platform_get_resource(dev->platformdev, | ||
| 69 | IORESOURCE_MEM, n); | ||
| 70 | if (!r) | ||
| 71 | break; | ||
| 72 | |||
| 73 | /* Resources above 64K are graphics memory */ | ||
| 74 | if (resource_size(r) > SZ_64K) | ||
| 75 | mem = r; | ||
| 76 | else if (i < ARRAY_SIZE(priv->dcrtc)) | ||
| 77 | res[i++] = r; | ||
| 78 | else | ||
| 79 | return -EINVAL; | ||
| 80 | } | ||
| 81 | |||
| 82 | if (!res[0] || !mem) | ||
| 83 | return -ENXIO; | ||
| 84 | |||
| 85 | if (!devm_request_mem_region(dev->dev, mem->start, | ||
| 86 | resource_size(mem), "armada-drm")) | ||
| 87 | return -EBUSY; | ||
| 88 | |||
| 89 | priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); | ||
| 90 | if (!priv) { | ||
| 91 | DRM_ERROR("failed to allocate private\n"); | ||
| 92 | return -ENOMEM; | ||
| 93 | } | ||
| 94 | |||
| 95 | dev->dev_private = priv; | ||
| 96 | |||
| 97 | /* Get the implementation specific driver data. */ | ||
| 98 | id = platform_get_device_id(dev->platformdev); | ||
| 99 | if (!id) | ||
| 100 | return -ENXIO; | ||
| 101 | |||
| 102 | priv->variant = (struct armada_variant *)id->driver_data; | ||
| 103 | |||
| 104 | ret = priv->variant->init(priv, dev->dev); | ||
| 105 | if (ret) | ||
| 106 | return ret; | ||
| 107 | |||
| 108 | INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); | ||
| 109 | INIT_KFIFO(priv->fb_unref); | ||
| 110 | |||
| 111 | /* Mode setting support */ | ||
| 112 | drm_mode_config_init(dev); | ||
| 113 | dev->mode_config.min_width = 320; | ||
| 114 | dev->mode_config.min_height = 200; | ||
| 115 | |||
| 116 | /* | ||
| 117 | * With vscale enabled, the maximum width is 1920 due to the | ||
| 118 | * 1920 by 3 lines RAM | ||
| 119 | */ | ||
| 120 | dev->mode_config.max_width = 1920; | ||
| 121 | dev->mode_config.max_height = 2048; | ||
| 122 | |||
| 123 | dev->mode_config.preferred_depth = 24; | ||
| 124 | dev->mode_config.funcs = &armada_drm_mode_config_funcs; | ||
| 125 | drm_mm_init(&priv->linear, mem->start, resource_size(mem)); | ||
| 126 | |||
| 127 | /* Create all LCD controllers */ | ||
| 128 | for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { | ||
| 129 | if (!res[n]) | ||
| 130 | break; | ||
| 131 | |||
| 132 | ret = armada_drm_crtc_create(dev, n, res[n]); | ||
| 133 | if (ret) | ||
| 134 | goto err_kms; | ||
| 135 | } | ||
| 136 | |||
| 137 | ret = drm_vblank_init(dev, n); | ||
| 138 | if (ret) | ||
| 139 | goto err_kms; | ||
| 140 | |||
| 141 | ret = drm_irq_install(dev); | ||
| 142 | if (ret) | ||
| 143 | goto err_kms; | ||
| 144 | |||
| 145 | dev->vblank_disable_allowed = 1; | ||
| 146 | |||
| 147 | ret = armada_fbdev_init(dev); | ||
| 148 | if (ret) | ||
| 149 | goto err_irq; | ||
| 150 | |||
| 151 | drm_kms_helper_poll_init(dev); | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | |||
| 155 | err_irq: | ||
| 156 | drm_irq_uninstall(dev); | ||
| 157 | err_kms: | ||
| 158 | drm_mode_config_cleanup(dev); | ||
| 159 | drm_mm_takedown(&priv->linear); | ||
| 160 | flush_work(&priv->fb_unref_work); | ||
| 161 | |||
| 162 | return ret; | ||
| 163 | } | ||
| 164 | |||
| 165 | static int armada_drm_unload(struct drm_device *dev) | ||
| 166 | { | ||
| 167 | struct armada_private *priv = dev->dev_private; | ||
| 168 | |||
| 169 | drm_kms_helper_poll_fini(dev); | ||
| 170 | armada_fbdev_fini(dev); | ||
| 171 | drm_irq_uninstall(dev); | ||
| 172 | drm_mode_config_cleanup(dev); | ||
| 173 | drm_mm_takedown(&priv->linear); | ||
| 174 | flush_work(&priv->fb_unref_work); | ||
| 175 | dev->dev_private = NULL; | ||
| 176 | |||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | void armada_drm_vbl_event_add(struct armada_crtc *dcrtc, | ||
| 181 | struct armada_vbl_event *evt) | ||
| 182 | { | ||
| 183 | unsigned long flags; | ||
| 184 | |||
| 185 | spin_lock_irqsave(&dcrtc->irq_lock, flags); | ||
| 186 | if (list_empty(&evt->node)) { | ||
| 187 | list_add_tail(&evt->node, &dcrtc->vbl_list); | ||
| 188 | |||
| 189 | drm_vblank_get(dcrtc->crtc.dev, dcrtc->num); | ||
| 190 | } | ||
| 191 | spin_unlock_irqrestore(&dcrtc->irq_lock, flags); | ||
| 192 | } | ||
| 193 | |||
| 194 | void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc, | ||
| 195 | struct armada_vbl_event *evt) | ||
| 196 | { | ||
| 197 | if (!list_empty(&evt->node)) { | ||
| 198 | list_del_init(&evt->node); | ||
| 199 | drm_vblank_put(dcrtc->crtc.dev, dcrtc->num); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *dcrtc, | ||
| 204 | struct armada_vbl_event *evt) | ||
| 205 | { | ||
| 206 | unsigned long flags; | ||
| 207 | |||
| 208 | spin_lock_irqsave(&dcrtc->irq_lock, flags); | ||
| 209 | armada_drm_vbl_event_remove(dcrtc, evt); | ||
| 210 | spin_unlock_irqrestore(&dcrtc->irq_lock, flags); | ||
| 211 | } | ||
| 212 | |||
| 213 | /* These are called under the vbl_lock. */ | ||
| 214 | static int armada_drm_enable_vblank(struct drm_device *dev, int crtc) | ||
| 215 | { | ||
| 216 | struct armada_private *priv = dev->dev_private; | ||
| 217 | armada_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA); | ||
| 218 | return 0; | ||
| 219 | } | ||
| 220 | |||
| 221 | static void armada_drm_disable_vblank(struct drm_device *dev, int crtc) | ||
| 222 | { | ||
| 223 | struct armada_private *priv = dev->dev_private; | ||
| 224 | armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA); | ||
| 225 | } | ||
| 226 | |||
| 227 | static irqreturn_t armada_drm_irq_handler(int irq, void *arg) | ||
| 228 | { | ||
| 229 | struct drm_device *dev = arg; | ||
| 230 | struct armada_private *priv = dev->dev_private; | ||
| 231 | struct armada_crtc *dcrtc = priv->dcrtc[0]; | ||
| 232 | uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); | ||
| 233 | irqreturn_t handled = IRQ_NONE; | ||
| 234 | |||
| 235 | /* | ||
| 236 | * This is rediculous - rather than writing bits to clear, we | ||
| 237 | * have to set the actual status register value. This is racy. | ||
| 238 | */ | ||
| 239 | writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); | ||
| 240 | |||
| 241 | /* Mask out those interrupts we haven't enabled */ | ||
| 242 | v = stat & dcrtc->irq_ena; | ||
| 243 | |||
| 244 | if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { | ||
| 245 | armada_drm_crtc_irq(dcrtc, stat); | ||
| 246 | handled = IRQ_HANDLED; | ||
| 247 | } | ||
| 248 | |||
| 249 | return handled; | ||
| 250 | } | ||
| 251 | |||
| 252 | static int armada_drm_irq_postinstall(struct drm_device *dev) | ||
| 253 | { | ||
| 254 | struct armada_private *priv = dev->dev_private; | ||
| 255 | struct armada_crtc *dcrtc = priv->dcrtc[0]; | ||
| 256 | |||
| 257 | spin_lock_irq(&dev->vbl_lock); | ||
| 258 | writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); | ||
| 259 | writel(0, dcrtc->base + LCD_SPU_IRQ_ISR); | ||
| 260 | spin_unlock_irq(&dev->vbl_lock); | ||
| 261 | |||
| 262 | return 0; | ||
| 263 | } | ||
| 264 | |||
| 265 | static void armada_drm_irq_uninstall(struct drm_device *dev) | ||
| 266 | { | ||
| 267 | struct armada_private *priv = dev->dev_private; | ||
| 268 | struct armada_crtc *dcrtc = priv->dcrtc[0]; | ||
| 269 | |||
| 270 | writel(0, dcrtc->base + LCD_SPU_IRQ_ENA); | ||
| 271 | } | ||
| 272 | |||
| 273 | static struct drm_ioctl_desc armada_ioctls[] = { | ||
| 274 | DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl, | ||
| 275 | DRM_UNLOCKED), | ||
| 276 | DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl, | ||
| 277 | DRM_UNLOCKED), | ||
| 278 | DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl, | ||
| 279 | DRM_UNLOCKED), | ||
| 280 | }; | ||
| 281 | |||
| 282 | static const struct file_operations armada_drm_fops = { | ||
| 283 | .owner = THIS_MODULE, | ||
| 284 | .llseek = no_llseek, | ||
| 285 | .read = drm_read, | ||
| 286 | .poll = drm_poll, | ||
| 287 | .unlocked_ioctl = drm_ioctl, | ||
| 288 | .mmap = drm_gem_mmap, | ||
| 289 | .open = drm_open, | ||
| 290 | .release = drm_release, | ||
| 291 | }; | ||
| 292 | |||
| 293 | static struct drm_driver armada_drm_driver = { | ||
| 294 | .load = armada_drm_load, | ||
| 295 | .open = NULL, | ||
| 296 | .preclose = NULL, | ||
| 297 | .postclose = NULL, | ||
| 298 | .lastclose = NULL, | ||
| 299 | .unload = armada_drm_unload, | ||
| 300 | .get_vblank_counter = drm_vblank_count, | ||
| 301 | .enable_vblank = armada_drm_enable_vblank, | ||
| 302 | .disable_vblank = armada_drm_disable_vblank, | ||
| 303 | .irq_handler = armada_drm_irq_handler, | ||
| 304 | .irq_postinstall = armada_drm_irq_postinstall, | ||
| 305 | .irq_uninstall = armada_drm_irq_uninstall, | ||
| 306 | #ifdef CONFIG_DEBUG_FS | ||
| 307 | .debugfs_init = armada_drm_debugfs_init, | ||
| 308 | .debugfs_cleanup = armada_drm_debugfs_cleanup, | ||
| 309 | #endif | ||
| 310 | .gem_init_object = NULL, | ||
| 311 | .gem_free_object = armada_gem_free_object, | ||
| 312 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
| 313 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
| 314 | .gem_prime_export = armada_gem_prime_export, | ||
| 315 | .gem_prime_import = armada_gem_prime_import, | ||
| 316 | .dumb_create = armada_gem_dumb_create, | ||
| 317 | .dumb_map_offset = armada_gem_dumb_map_offset, | ||
| 318 | .dumb_destroy = armada_gem_dumb_destroy, | ||
| 319 | .gem_vm_ops = &armada_gem_vm_ops, | ||
| 320 | .major = 1, | ||
| 321 | .minor = 0, | ||
| 322 | .name = "armada-drm", | ||
| 323 | .desc = "Armada SoC DRM", | ||
| 324 | .date = "20120730", | ||
| 325 | .driver_features = DRIVER_GEM | DRIVER_MODESET | | ||
| 326 | DRIVER_HAVE_IRQ | DRIVER_PRIME, | ||
| 327 | .ioctls = armada_ioctls, | ||
| 328 | .fops = &armada_drm_fops, | ||
| 329 | }; | ||
| 330 | |||
| 331 | static int armada_drm_probe(struct platform_device *pdev) | ||
| 332 | { | ||
| 333 | return drm_platform_init(&armada_drm_driver, pdev); | ||
| 334 | } | ||
| 335 | |||
| 336 | static int armada_drm_remove(struct platform_device *pdev) | ||
| 337 | { | ||
| 338 | drm_platform_exit(&armada_drm_driver, pdev); | ||
| 339 | return 0; | ||
| 340 | } | ||
| 341 | |||
| 342 | static const struct platform_device_id armada_drm_platform_ids[] = { | ||
| 343 | { | ||
| 344 | .name = "armada-drm", | ||
| 345 | .driver_data = (unsigned long)&armada510_ops, | ||
| 346 | }, { | ||
| 347 | .name = "armada-510-drm", | ||
| 348 | .driver_data = (unsigned long)&armada510_ops, | ||
| 349 | }, | ||
| 350 | { }, | ||
| 351 | }; | ||
| 352 | MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids); | ||
| 353 | |||
| 354 | static struct platform_driver armada_drm_platform_driver = { | ||
| 355 | .probe = armada_drm_probe, | ||
| 356 | .remove = armada_drm_remove, | ||
| 357 | .driver = { | ||
| 358 | .name = "armada-drm", | ||
| 359 | .owner = THIS_MODULE, | ||
| 360 | }, | ||
| 361 | .id_table = armada_drm_platform_ids, | ||
| 362 | }; | ||
| 363 | |||
| 364 | static int __init armada_drm_init(void) | ||
| 365 | { | ||
| 366 | armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls); | ||
| 367 | return platform_driver_register(&armada_drm_platform_driver); | ||
| 368 | } | ||
| 369 | module_init(armada_drm_init); | ||
| 370 | |||
| 371 | static void __exit armada_drm_exit(void) | ||
| 372 | { | ||
| 373 | platform_driver_unregister(&armada_drm_platform_driver); | ||
| 374 | } | ||
| 375 | module_exit(armada_drm_exit); | ||
| 376 | |||
| 377 | MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>"); | ||
| 378 | MODULE_DESCRIPTION("Armada DRM Driver"); | ||
| 379 | MODULE_LICENSE("GPL"); | ||
| 380 | MODULE_ALIAS("platform:armada-drm"); | ||
diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c new file mode 100644 index 000000000000..1c90969def3e --- /dev/null +++ b/drivers/gpu/drm/armada/armada_fb.c | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #include <drm/drmP.h> | ||
| 9 | #include <drm/drm_crtc_helper.h> | ||
| 10 | #include <drm/drm_fb_helper.h> | ||
| 11 | #include "armada_drm.h" | ||
| 12 | #include "armada_fb.h" | ||
| 13 | #include "armada_gem.h" | ||
| 14 | #include "armada_hw.h" | ||
| 15 | |||
| 16 | static void armada_fb_destroy(struct drm_framebuffer *fb) | ||
| 17 | { | ||
| 18 | struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); | ||
| 19 | |||
| 20 | drm_framebuffer_cleanup(&dfb->fb); | ||
| 21 | drm_gem_object_unreference_unlocked(&dfb->obj->obj); | ||
| 22 | kfree(dfb); | ||
| 23 | } | ||
| 24 | |||
| 25 | static int armada_fb_create_handle(struct drm_framebuffer *fb, | ||
| 26 | struct drm_file *dfile, unsigned int *handle) | ||
| 27 | { | ||
| 28 | struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); | ||
| 29 | return drm_gem_handle_create(dfile, &dfb->obj->obj, handle); | ||
| 30 | } | ||
| 31 | |||
| 32 | static const struct drm_framebuffer_funcs armada_fb_funcs = { | ||
| 33 | .destroy = armada_fb_destroy, | ||
| 34 | .create_handle = armada_fb_create_handle, | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, | ||
| 38 | struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj) | ||
| 39 | { | ||
| 40 | struct armada_framebuffer *dfb; | ||
| 41 | uint8_t format, config; | ||
| 42 | int ret; | ||
| 43 | |||
| 44 | switch (mode->pixel_format) { | ||
| 45 | #define FMT(drm, fmt, mod) \ | ||
| 46 | case DRM_FORMAT_##drm: \ | ||
| 47 | format = CFG_##fmt; \ | ||
| 48 | config = mod; \ | ||
| 49 | break | ||
| 50 | FMT(RGB565, 565, CFG_SWAPRB); | ||
| 51 | FMT(BGR565, 565, 0); | ||
| 52 | FMT(ARGB1555, 1555, CFG_SWAPRB); | ||
| 53 | FMT(ABGR1555, 1555, 0); | ||
| 54 | FMT(RGB888, 888PACK, CFG_SWAPRB); | ||
| 55 | FMT(BGR888, 888PACK, 0); | ||
| 56 | FMT(XRGB8888, X888, CFG_SWAPRB); | ||
| 57 | FMT(XBGR8888, X888, 0); | ||
| 58 | FMT(ARGB8888, 8888, CFG_SWAPRB); | ||
| 59 | FMT(ABGR8888, 8888, 0); | ||
| 60 | FMT(YUYV, 422PACK, CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV); | ||
| 61 | FMT(UYVY, 422PACK, CFG_YUV2RGB); | ||
| 62 | FMT(VYUY, 422PACK, CFG_YUV2RGB | CFG_SWAPUV); | ||
| 63 | FMT(YVYU, 422PACK, CFG_YUV2RGB | CFG_SWAPYU); | ||
| 64 | FMT(YUV422, 422, CFG_YUV2RGB); | ||
| 65 | FMT(YVU422, 422, CFG_YUV2RGB | CFG_SWAPUV); | ||
| 66 | FMT(YUV420, 420, CFG_YUV2RGB); | ||
| 67 | FMT(YVU420, 420, CFG_YUV2RGB | CFG_SWAPUV); | ||
| 68 | FMT(C8, PSEUDO8, 0); | ||
| 69 | #undef FMT | ||
| 70 | default: | ||
| 71 | return ERR_PTR(-EINVAL); | ||
| 72 | } | ||
| 73 | |||
| 74 | dfb = kzalloc(sizeof(*dfb), GFP_KERNEL); | ||
| 75 | if (!dfb) { | ||
| 76 | DRM_ERROR("failed to allocate Armada fb object\n"); | ||
| 77 | return ERR_PTR(-ENOMEM); | ||
| 78 | } | ||
| 79 | |||
| 80 | dfb->fmt = format; | ||
| 81 | dfb->mod = config; | ||
| 82 | dfb->obj = obj; | ||
| 83 | |||
| 84 | drm_helper_mode_fill_fb_struct(&dfb->fb, mode); | ||
| 85 | |||
| 86 | ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs); | ||
| 87 | if (ret) { | ||
| 88 | kfree(dfb); | ||
| 89 | return ERR_PTR(ret); | ||
| 90 | } | ||
| 91 | |||
| 92 | /* | ||
| 93 | * Take a reference on our object as we're successful - the | ||
| 94 | * caller already holds a reference, which keeps us safe for | ||
| 95 | * the above call, but the caller will drop their reference | ||
| 96 | * to it. Hence we need to take our own reference. | ||
| 97 | */ | ||
| 98 | drm_gem_object_reference(&obj->obj); | ||
| 99 | |||
| 100 | return dfb; | ||
| 101 | } | ||
| 102 | |||
| 103 | static struct drm_framebuffer *armada_fb_create(struct drm_device *dev, | ||
| 104 | struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode) | ||
| 105 | { | ||
| 106 | struct armada_gem_object *obj; | ||
| 107 | struct armada_framebuffer *dfb; | ||
| 108 | int ret; | ||
| 109 | |||
| 110 | DRM_DEBUG_DRIVER("w%u h%u pf%08x f%u p%u,%u,%u\n", | ||
| 111 | mode->width, mode->height, mode->pixel_format, | ||
| 112 | mode->flags, mode->pitches[0], mode->pitches[1], | ||
| 113 | mode->pitches[2]); | ||
| 114 | |||
| 115 | /* We can only handle a single plane at the moment */ | ||
| 116 | if (drm_format_num_planes(mode->pixel_format) > 1 && | ||
| 117 | (mode->handles[0] != mode->handles[1] || | ||
| 118 | mode->handles[0] != mode->handles[2])) { | ||
| 119 | ret = -EINVAL; | ||
| 120 | goto err; | ||
| 121 | } | ||
| 122 | |||
| 123 | obj = armada_gem_object_lookup(dev, dfile, mode->handles[0]); | ||
| 124 | if (!obj) { | ||
| 125 | ret = -ENOENT; | ||
| 126 | goto err; | ||
| 127 | } | ||
| 128 | |||
| 129 | if (obj->obj.import_attach && !obj->sgt) { | ||
| 130 | ret = armada_gem_map_import(obj); | ||
| 131 | if (ret) | ||
| 132 | goto err_unref; | ||
| 133 | } | ||
| 134 | |||
| 135 | /* Framebuffer objects must have a valid device address for scanout */ | ||
| 136 | if (obj->dev_addr == DMA_ERROR_CODE) { | ||
| 137 | ret = -EINVAL; | ||
| 138 | goto err_unref; | ||
| 139 | } | ||
| 140 | |||
| 141 | dfb = armada_framebuffer_create(dev, mode, obj); | ||
| 142 | if (IS_ERR(dfb)) { | ||
| 143 | ret = PTR_ERR(dfb); | ||
| 144 | goto err; | ||
| 145 | } | ||
| 146 | |||
| 147 | drm_gem_object_unreference_unlocked(&obj->obj); | ||
| 148 | |||
| 149 | return &dfb->fb; | ||
| 150 | |||
| 151 | err_unref: | ||
| 152 | drm_gem_object_unreference_unlocked(&obj->obj); | ||
| 153 | err: | ||
| 154 | DRM_ERROR("failed to initialize framebuffer: %d\n", ret); | ||
| 155 | return ERR_PTR(ret); | ||
| 156 | } | ||
| 157 | |||
| 158 | static void armada_output_poll_changed(struct drm_device *dev) | ||
| 159 | { | ||
| 160 | struct armada_private *priv = dev->dev_private; | ||
| 161 | struct drm_fb_helper *fbh = priv->fbdev; | ||
| 162 | |||
| 163 | if (fbh) | ||
| 164 | drm_fb_helper_hotplug_event(fbh); | ||
| 165 | } | ||
| 166 | |||
| 167 | const struct drm_mode_config_funcs armada_drm_mode_config_funcs = { | ||
| 168 | .fb_create = armada_fb_create, | ||
| 169 | .output_poll_changed = armada_output_poll_changed, | ||
| 170 | }; | ||
diff --git a/drivers/gpu/drm/armada/armada_fb.h b/drivers/gpu/drm/armada/armada_fb.h new file mode 100644 index 000000000000..ce3f12ebfc53 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_fb.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #ifndef ARMADA_FB_H | ||
| 9 | #define ARMADA_FB_H | ||
| 10 | |||
| 11 | struct armada_framebuffer { | ||
| 12 | struct drm_framebuffer fb; | ||
| 13 | struct armada_gem_object *obj; | ||
| 14 | uint8_t fmt; | ||
| 15 | uint8_t mod; | ||
| 16 | }; | ||
| 17 | #define drm_fb_to_armada_fb(dfb) \ | ||
| 18 | container_of(dfb, struct armada_framebuffer, fb) | ||
| 19 | #define drm_fb_obj(fb) drm_fb_to_armada_fb(fb)->obj | ||
| 20 | |||
| 21 | struct armada_framebuffer *armada_framebuffer_create(struct drm_device *, | ||
| 22 | struct drm_mode_fb_cmd2 *, struct armada_gem_object *); | ||
| 23 | |||
| 24 | #endif | ||
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c new file mode 100644 index 000000000000..dd5ea77dac96 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_fbdev.c | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * Written from the i915 driver. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #include <linux/errno.h> | ||
| 10 | #include <linux/fb.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | |||
| 14 | #include <drm/drmP.h> | ||
| 15 | #include <drm/drm_fb_helper.h> | ||
| 16 | #include "armada_crtc.h" | ||
| 17 | #include "armada_drm.h" | ||
| 18 | #include "armada_fb.h" | ||
| 19 | #include "armada_gem.h" | ||
| 20 | |||
| 21 | static /*const*/ struct fb_ops armada_fb_ops = { | ||
| 22 | .owner = THIS_MODULE, | ||
| 23 | .fb_check_var = drm_fb_helper_check_var, | ||
| 24 | .fb_set_par = drm_fb_helper_set_par, | ||
| 25 | .fb_fillrect = cfb_fillrect, | ||
| 26 | .fb_copyarea = cfb_copyarea, | ||
| 27 | .fb_imageblit = cfb_imageblit, | ||
| 28 | .fb_pan_display = drm_fb_helper_pan_display, | ||
| 29 | .fb_blank = drm_fb_helper_blank, | ||
| 30 | .fb_setcmap = drm_fb_helper_setcmap, | ||
| 31 | .fb_debug_enter = drm_fb_helper_debug_enter, | ||
| 32 | .fb_debug_leave = drm_fb_helper_debug_leave, | ||
| 33 | }; | ||
| 34 | |||
| 35 | static int armada_fb_create(struct drm_fb_helper *fbh, | ||
| 36 | struct drm_fb_helper_surface_size *sizes) | ||
| 37 | { | ||
| 38 | struct drm_device *dev = fbh->dev; | ||
| 39 | struct drm_mode_fb_cmd2 mode; | ||
| 40 | struct armada_framebuffer *dfb; | ||
| 41 | struct armada_gem_object *obj; | ||
| 42 | struct fb_info *info; | ||
| 43 | int size, ret; | ||
| 44 | void *ptr; | ||
| 45 | |||
| 46 | memset(&mode, 0, sizeof(mode)); | ||
| 47 | mode.width = sizes->surface_width; | ||
| 48 | mode.height = sizes->surface_height; | ||
| 49 | mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp); | ||
| 50 | mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | ||
| 51 | sizes->surface_depth); | ||
| 52 | |||
| 53 | size = mode.pitches[0] * mode.height; | ||
| 54 | obj = armada_gem_alloc_private_object(dev, size); | ||
| 55 | if (!obj) { | ||
| 56 | DRM_ERROR("failed to allocate fb memory\n"); | ||
| 57 | return -ENOMEM; | ||
| 58 | } | ||
| 59 | |||
| 60 | ret = armada_gem_linear_back(dev, obj); | ||
| 61 | if (ret) { | ||
| 62 | drm_gem_object_unreference_unlocked(&obj->obj); | ||
| 63 | return ret; | ||
| 64 | } | ||
| 65 | |||
| 66 | ptr = armada_gem_map_object(dev, obj); | ||
| 67 | if (!ptr) { | ||
| 68 | drm_gem_object_unreference_unlocked(&obj->obj); | ||
| 69 | return -ENOMEM; | ||
| 70 | } | ||
| 71 | |||
| 72 | dfb = armada_framebuffer_create(dev, &mode, obj); | ||
| 73 | |||
| 74 | /* | ||
| 75 | * A reference is now held by the framebuffer object if | ||
| 76 | * successful, otherwise this drops the ref for the error path. | ||
| 77 | */ | ||
| 78 | drm_gem_object_unreference_unlocked(&obj->obj); | ||
| 79 | |||
| 80 | if (IS_ERR(dfb)) | ||
| 81 | return PTR_ERR(dfb); | ||
| 82 | |||
| 83 | info = framebuffer_alloc(0, dev->dev); | ||
| 84 | if (!info) { | ||
| 85 | ret = -ENOMEM; | ||
| 86 | goto err_fballoc; | ||
| 87 | } | ||
| 88 | |||
| 89 | ret = fb_alloc_cmap(&info->cmap, 256, 0); | ||
| 90 | if (ret) { | ||
| 91 | ret = -ENOMEM; | ||
| 92 | goto err_fbcmap; | ||
| 93 | } | ||
| 94 | |||
| 95 | strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id)); | ||
| 96 | info->par = fbh; | ||
| 97 | info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; | ||
| 98 | info->fbops = &armada_fb_ops; | ||
| 99 | info->fix.smem_start = obj->phys_addr; | ||
| 100 | info->fix.smem_len = obj->obj.size; | ||
| 101 | info->screen_size = obj->obj.size; | ||
| 102 | info->screen_base = ptr; | ||
| 103 | fbh->fb = &dfb->fb; | ||
| 104 | fbh->fbdev = info; | ||
| 105 | drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth); | ||
| 106 | drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height); | ||
| 107 | |||
| 108 | DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08x\n", | ||
| 109 | dfb->fb.width, dfb->fb.height, | ||
| 110 | dfb->fb.bits_per_pixel, obj->phys_addr); | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | |||
| 114 | err_fbcmap: | ||
| 115 | framebuffer_release(info); | ||
| 116 | err_fballoc: | ||
| 117 | dfb->fb.funcs->destroy(&dfb->fb); | ||
| 118 | return ret; | ||
| 119 | } | ||
| 120 | |||
| 121 | static int armada_fb_probe(struct drm_fb_helper *fbh, | ||
| 122 | struct drm_fb_helper_surface_size *sizes) | ||
| 123 | { | ||
| 124 | int ret = 0; | ||
| 125 | |||
| 126 | if (!fbh->fb) { | ||
| 127 | ret = armada_fb_create(fbh, sizes); | ||
| 128 | if (ret == 0) | ||
| 129 | ret = 1; | ||
| 130 | } | ||
| 131 | return ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | static struct drm_fb_helper_funcs armada_fb_helper_funcs = { | ||
| 135 | .gamma_set = armada_drm_crtc_gamma_set, | ||
| 136 | .gamma_get = armada_drm_crtc_gamma_get, | ||
| 137 | .fb_probe = armada_fb_probe, | ||
| 138 | }; | ||
| 139 | |||
| 140 | int armada_fbdev_init(struct drm_device *dev) | ||
| 141 | { | ||
| 142 | struct armada_private *priv = dev->dev_private; | ||
| 143 | struct drm_fb_helper *fbh; | ||
| 144 | int ret; | ||
| 145 | |||
| 146 | fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL); | ||
| 147 | if (!fbh) | ||
| 148 | return -ENOMEM; | ||
| 149 | |||
| 150 | priv->fbdev = fbh; | ||
| 151 | |||
| 152 | fbh->funcs = &armada_fb_helper_funcs; | ||
| 153 | |||
| 154 | ret = drm_fb_helper_init(dev, fbh, 1, 1); | ||
| 155 | if (ret) { | ||
| 156 | DRM_ERROR("failed to initialize drm fb helper\n"); | ||
| 157 | goto err_fb_helper; | ||
| 158 | } | ||
| 159 | |||
| 160 | ret = drm_fb_helper_single_add_all_connectors(fbh); | ||
| 161 | if (ret) { | ||
| 162 | DRM_ERROR("failed to add fb connectors\n"); | ||
| 163 | goto err_fb_setup; | ||
| 164 | } | ||
| 165 | |||
| 166 | ret = drm_fb_helper_initial_config(fbh, 32); | ||
| 167 | if (ret) { | ||
| 168 | DRM_ERROR("failed to set initial config\n"); | ||
| 169 | goto err_fb_setup; | ||
| 170 | } | ||
| 171 | |||
| 172 | return 0; | ||
| 173 | err_fb_setup: | ||
| 174 | drm_fb_helper_fini(fbh); | ||
| 175 | err_fb_helper: | ||
| 176 | priv->fbdev = NULL; | ||
| 177 | return ret; | ||
| 178 | } | ||
| 179 | |||
| 180 | void armada_fbdev_fini(struct drm_device *dev) | ||
| 181 | { | ||
| 182 | struct armada_private *priv = dev->dev_private; | ||
| 183 | struct drm_fb_helper *fbh = priv->fbdev; | ||
| 184 | |||
| 185 | if (fbh) { | ||
| 186 | struct fb_info *info = fbh->fbdev; | ||
| 187 | |||
| 188 | if (info) { | ||
| 189 | unregister_framebuffer(info); | ||
| 190 | if (info->cmap.len) | ||
| 191 | fb_dealloc_cmap(&info->cmap); | ||
| 192 | framebuffer_release(info); | ||
| 193 | } | ||
| 194 | |||
| 195 | if (fbh->fb) | ||
| 196 | fbh->fb->funcs->destroy(fbh->fb); | ||
| 197 | |||
| 198 | drm_fb_helper_fini(fbh); | ||
| 199 | |||
| 200 | priv->fbdev = NULL; | ||
| 201 | } | ||
| 202 | } | ||
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c new file mode 100644 index 000000000000..9f2356bae7fd --- /dev/null +++ b/drivers/gpu/drm/armada/armada_gem.c | |||
| @@ -0,0 +1,611 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #include <linux/dma-buf.h> | ||
| 9 | #include <linux/dma-mapping.h> | ||
| 10 | #include <linux/shmem_fs.h> | ||
| 11 | #include <drm/drmP.h> | ||
| 12 | #include "armada_drm.h" | ||
| 13 | #include "armada_gem.h" | ||
| 14 | #include <drm/armada_drm.h> | ||
| 15 | #include "armada_ioctlP.h" | ||
| 16 | |||
| 17 | static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
| 18 | { | ||
| 19 | struct armada_gem_object *obj = drm_to_armada_gem(vma->vm_private_data); | ||
| 20 | unsigned long addr = (unsigned long)vmf->virtual_address; | ||
| 21 | unsigned long pfn = obj->phys_addr >> PAGE_SHIFT; | ||
| 22 | int ret; | ||
| 23 | |||
| 24 | pfn += (addr - vma->vm_start) >> PAGE_SHIFT; | ||
| 25 | ret = vm_insert_pfn(vma, addr, pfn); | ||
| 26 | |||
| 27 | switch (ret) { | ||
| 28 | case 0: | ||
| 29 | case -EBUSY: | ||
| 30 | return VM_FAULT_NOPAGE; | ||
| 31 | case -ENOMEM: | ||
| 32 | return VM_FAULT_OOM; | ||
| 33 | default: | ||
| 34 | return VM_FAULT_SIGBUS; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | const struct vm_operations_struct armada_gem_vm_ops = { | ||
| 39 | .fault = armada_gem_vm_fault, | ||
| 40 | .open = drm_gem_vm_open, | ||
| 41 | .close = drm_gem_vm_close, | ||
| 42 | }; | ||
| 43 | |||
| 44 | static size_t roundup_gem_size(size_t size) | ||
| 45 | { | ||
| 46 | return roundup(size, PAGE_SIZE); | ||
| 47 | } | ||
| 48 | |||
| 49 | /* dev->struct_mutex is held here */ | ||
| 50 | void armada_gem_free_object(struct drm_gem_object *obj) | ||
| 51 | { | ||
| 52 | struct armada_gem_object *dobj = drm_to_armada_gem(obj); | ||
| 53 | |||
| 54 | DRM_DEBUG_DRIVER("release obj %p\n", dobj); | ||
| 55 | |||
| 56 | drm_gem_free_mmap_offset(&dobj->obj); | ||
| 57 | |||
| 58 | if (dobj->page) { | ||
| 59 | /* page backed memory */ | ||
| 60 | unsigned int order = get_order(dobj->obj.size); | ||
| 61 | __free_pages(dobj->page, order); | ||
| 62 | } else if (dobj->linear) { | ||
| 63 | /* linear backed memory */ | ||
| 64 | drm_mm_remove_node(dobj->linear); | ||
| 65 | kfree(dobj->linear); | ||
| 66 | if (dobj->addr) | ||
| 67 | iounmap(dobj->addr); | ||
| 68 | } | ||
| 69 | |||
| 70 | if (dobj->obj.import_attach) { | ||
| 71 | /* We only ever display imported data */ | ||
| 72 | dma_buf_unmap_attachment(dobj->obj.import_attach, dobj->sgt, | ||
| 73 | DMA_TO_DEVICE); | ||
| 74 | drm_prime_gem_destroy(&dobj->obj, NULL); | ||
| 75 | } | ||
| 76 | |||
| 77 | drm_gem_object_release(&dobj->obj); | ||
| 78 | |||
| 79 | kfree(dobj); | ||
| 80 | } | ||
| 81 | |||
| 82 | int | ||
| 83 | armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj) | ||
| 84 | { | ||
| 85 | struct armada_private *priv = dev->dev_private; | ||
| 86 | size_t size = obj->obj.size; | ||
| 87 | |||
| 88 | if (obj->page || obj->linear) | ||
| 89 | return 0; | ||
| 90 | |||
| 91 | /* | ||
| 92 | * If it is a small allocation (typically cursor, which will | ||
| 93 | * be 32x64 or 64x32 ARGB pixels) try to get it from the system. | ||
| 94 | * Framebuffers will never be this small (our minimum size for | ||
| 95 | * framebuffers is larger than this anyway.) Such objects are | ||
| 96 | * only accessed by the CPU so we don't need any special handing | ||
| 97 | * here. | ||
| 98 | */ | ||
| 99 | if (size <= 8192) { | ||
| 100 | unsigned int order = get_order(size); | ||
| 101 | struct page *p = alloc_pages(GFP_KERNEL, order); | ||
| 102 | |||
| 103 | if (p) { | ||
| 104 | obj->addr = page_address(p); | ||
| 105 | obj->phys_addr = page_to_phys(p); | ||
| 106 | obj->page = p; | ||
| 107 | |||
| 108 | memset(obj->addr, 0, PAGE_ALIGN(size)); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /* | ||
| 113 | * We could grab something from CMA if it's enabled, but that | ||
| 114 | * involves building in a problem: | ||
| 115 | * | ||
| 116 | * CMA's interface uses dma_alloc_coherent(), which provides us | ||
| 117 | * with an CPU virtual address and a device address. | ||
| 118 | * | ||
| 119 | * The CPU virtual address may be either an address in the kernel | ||
| 120 | * direct mapped region (for example, as it would be on x86) or | ||
| 121 | * it may be remapped into another part of kernel memory space | ||
| 122 | * (eg, as it would be on ARM.) This means virt_to_phys() on the | ||
| 123 | * returned virtual address is invalid depending on the architecture | ||
| 124 | * implementation. | ||
| 125 | * | ||
| 126 | * The device address may also not be a physical address; it may | ||
| 127 | * be that there is some kind of remapping between the device and | ||
| 128 | * system RAM, which makes the use of the device address also | ||
| 129 | * unsafe to re-use as a physical address. | ||
| 130 | * | ||
| 131 | * This makes DRM usage of dma_alloc_coherent() in a generic way | ||
| 132 | * at best very questionable and unsafe. | ||
| 133 | */ | ||
| 134 | |||
| 135 | /* Otherwise, grab it from our linear allocation */ | ||
| 136 | if (!obj->page) { | ||
| 137 | struct drm_mm_node *node; | ||
| 138 | unsigned align = min_t(unsigned, size, SZ_2M); | ||
| 139 | void __iomem *ptr; | ||
| 140 | int ret; | ||
| 141 | |||
| 142 | node = kzalloc(sizeof(*node), GFP_KERNEL); | ||
| 143 | if (!node) | ||
| 144 | return -ENOSPC; | ||
| 145 | |||
| 146 | mutex_lock(&dev->struct_mutex); | ||
| 147 | ret = drm_mm_insert_node(&priv->linear, node, size, align, | ||
| 148 | DRM_MM_SEARCH_DEFAULT); | ||
| 149 | mutex_unlock(&dev->struct_mutex); | ||
| 150 | if (ret) { | ||
| 151 | kfree(node); | ||
| 152 | return ret; | ||
| 153 | } | ||
| 154 | |||
| 155 | obj->linear = node; | ||
| 156 | |||
| 157 | /* Ensure that the memory we're returning is cleared. */ | ||
| 158 | ptr = ioremap_wc(obj->linear->start, size); | ||
| 159 | if (!ptr) { | ||
| 160 | mutex_lock(&dev->struct_mutex); | ||
| 161 | drm_mm_remove_node(obj->linear); | ||
| 162 | mutex_unlock(&dev->struct_mutex); | ||
| 163 | kfree(obj->linear); | ||
| 164 | obj->linear = NULL; | ||
| 165 | return -ENOMEM; | ||
| 166 | } | ||
| 167 | |||
| 168 | memset_io(ptr, 0, size); | ||
| 169 | iounmap(ptr); | ||
| 170 | |||
| 171 | obj->phys_addr = obj->linear->start; | ||
| 172 | obj->dev_addr = obj->linear->start; | ||
| 173 | } | ||
| 174 | |||
| 175 | DRM_DEBUG_DRIVER("obj %p phys %#x dev %#x\n", | ||
| 176 | obj, obj->phys_addr, obj->dev_addr); | ||
| 177 | |||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | void * | ||
| 182 | armada_gem_map_object(struct drm_device *dev, struct armada_gem_object *dobj) | ||
| 183 | { | ||
| 184 | /* only linear objects need to be ioremap'd */ | ||
| 185 | if (!dobj->addr && dobj->linear) | ||
| 186 | dobj->addr = ioremap_wc(dobj->phys_addr, dobj->obj.size); | ||
| 187 | return dobj->addr; | ||
| 188 | } | ||
| 189 | |||
| 190 | struct armada_gem_object * | ||
| 191 | armada_gem_alloc_private_object(struct drm_device *dev, size_t size) | ||
| 192 | { | ||
| 193 | struct armada_gem_object *obj; | ||
| 194 | |||
| 195 | size = roundup_gem_size(size); | ||
| 196 | |||
| 197 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); | ||
| 198 | if (!obj) | ||
| 199 | return NULL; | ||
| 200 | |||
| 201 | drm_gem_private_object_init(dev, &obj->obj, size); | ||
| 202 | obj->dev_addr = DMA_ERROR_CODE; | ||
| 203 | |||
| 204 | DRM_DEBUG_DRIVER("alloc private obj %p size %zu\n", obj, size); | ||
| 205 | |||
| 206 | return obj; | ||
| 207 | } | ||
| 208 | |||
| 209 | struct armada_gem_object *armada_gem_alloc_object(struct drm_device *dev, | ||
| 210 | size_t size) | ||
| 211 | { | ||
| 212 | struct armada_gem_object *obj; | ||
| 213 | struct address_space *mapping; | ||
| 214 | |||
| 215 | size = roundup_gem_size(size); | ||
| 216 | |||
| 217 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); | ||
| 218 | if (!obj) | ||
| 219 | return NULL; | ||
| 220 | |||
| 221 | if (drm_gem_object_init(dev, &obj->obj, size)) { | ||
| 222 | kfree(obj); | ||
| 223 | return NULL; | ||
| 224 | } | ||
| 225 | |||
| 226 | obj->dev_addr = DMA_ERROR_CODE; | ||
| 227 | |||
| 228 | mapping = obj->obj.filp->f_path.dentry->d_inode->i_mapping; | ||
| 229 | mapping_set_gfp_mask(mapping, GFP_HIGHUSER | __GFP_RECLAIMABLE); | ||
| 230 | |||
| 231 | DRM_DEBUG_DRIVER("alloc obj %p size %zu\n", obj, size); | ||
| 232 | |||
| 233 | return obj; | ||
| 234 | } | ||
| 235 | |||
| 236 | /* Dumb alloc support */ | ||
| 237 | int armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev, | ||
| 238 | struct drm_mode_create_dumb *args) | ||
| 239 | { | ||
| 240 | struct armada_gem_object *dobj; | ||
| 241 | u32 handle; | ||
| 242 | size_t size; | ||
| 243 | int ret; | ||
| 244 | |||
| 245 | args->pitch = armada_pitch(args->width, args->bpp); | ||
| 246 | args->size = size = args->pitch * args->height; | ||
| 247 | |||
| 248 | dobj = armada_gem_alloc_private_object(dev, size); | ||
| 249 | if (dobj == NULL) | ||
| 250 | return -ENOMEM; | ||
| 251 | |||
| 252 | ret = armada_gem_linear_back(dev, dobj); | ||
| 253 | if (ret) | ||
| 254 | goto err; | ||
| 255 | |||
| 256 | ret = drm_gem_handle_create(file, &dobj->obj, &handle); | ||
| 257 | if (ret) | ||
| 258 | goto err; | ||
| 259 | |||
| 260 | args->handle = handle; | ||
| 261 | |||
| 262 | /* drop reference from allocate - handle holds it now */ | ||
| 263 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); | ||
| 264 | err: | ||
| 265 | drm_gem_object_unreference_unlocked(&dobj->obj); | ||
| 266 | return ret; | ||
| 267 | } | ||
| 268 | |||
| 269 | int armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, | ||
| 270 | uint32_t handle, uint64_t *offset) | ||
| 271 | { | ||
| 272 | struct armada_gem_object *obj; | ||
| 273 | int ret = 0; | ||
| 274 | |||
| 275 | mutex_lock(&dev->struct_mutex); | ||
| 276 | obj = armada_gem_object_lookup(dev, file, handle); | ||
| 277 | if (!obj) { | ||
| 278 | DRM_ERROR("failed to lookup gem object\n"); | ||
| 279 | ret = -EINVAL; | ||
| 280 | goto err_unlock; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* Don't allow imported objects to be mapped */ | ||
| 284 | if (obj->obj.import_attach) { | ||
| 285 | ret = -EINVAL; | ||
| 286 | goto err_unlock; | ||
| 287 | } | ||
| 288 | |||
| 289 | ret = drm_gem_create_mmap_offset(&obj->obj); | ||
| 290 | if (ret == 0) { | ||
| 291 | *offset = drm_vma_node_offset_addr(&obj->obj.vma_node); | ||
| 292 | DRM_DEBUG_DRIVER("handle %#x offset %llx\n", handle, *offset); | ||
| 293 | } | ||
| 294 | |||
| 295 | drm_gem_object_unreference(&obj->obj); | ||
| 296 | err_unlock: | ||
| 297 | mutex_unlock(&dev->struct_mutex); | ||
| 298 | |||
| 299 | return ret; | ||
| 300 | } | ||
| 301 | |||
| 302 | int armada_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, | ||
| 303 | uint32_t handle) | ||
| 304 | { | ||
| 305 | return drm_gem_handle_delete(file, handle); | ||
| 306 | } | ||
| 307 | |||
| 308 | /* Private driver gem ioctls */ | ||
| 309 | int armada_gem_create_ioctl(struct drm_device *dev, void *data, | ||
| 310 | struct drm_file *file) | ||
| 311 | { | ||
| 312 | struct drm_armada_gem_create *args = data; | ||
| 313 | struct armada_gem_object *dobj; | ||
| 314 | size_t size; | ||
| 315 | u32 handle; | ||
| 316 | int ret; | ||
| 317 | |||
| 318 | if (args->size == 0) | ||
| 319 | return -ENOMEM; | ||
| 320 | |||
| 321 | size = args->size; | ||
| 322 | |||
| 323 | dobj = armada_gem_alloc_object(dev, size); | ||
| 324 | if (dobj == NULL) | ||
| 325 | return -ENOMEM; | ||
| 326 | |||
| 327 | ret = drm_gem_handle_create(file, &dobj->obj, &handle); | ||
| 328 | if (ret) | ||
| 329 | goto err; | ||
| 330 | |||
| 331 | args->handle = handle; | ||
| 332 | |||
| 333 | /* drop reference from allocate - handle holds it now */ | ||
| 334 | DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle); | ||
| 335 | err: | ||
| 336 | drm_gem_object_unreference_unlocked(&dobj->obj); | ||
| 337 | return ret; | ||
| 338 | } | ||
| 339 | |||
| 340 | /* Map a shmem-backed object into process memory space */ | ||
| 341 | int armada_gem_mmap_ioctl(struct drm_device *dev, void *data, | ||
| 342 | struct drm_file *file) | ||
| 343 | { | ||
| 344 | struct drm_armada_gem_mmap *args = data; | ||
| 345 | struct armada_gem_object *dobj; | ||
| 346 | unsigned long addr; | ||
| 347 | |||
| 348 | dobj = armada_gem_object_lookup(dev, file, args->handle); | ||
| 349 | if (dobj == NULL) | ||
| 350 | return -ENOENT; | ||
| 351 | |||
| 352 | if (!dobj->obj.filp) { | ||
| 353 | drm_gem_object_unreference(&dobj->obj); | ||
| 354 | return -EINVAL; | ||
| 355 | } | ||
| 356 | |||
| 357 | addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE, | ||
| 358 | MAP_SHARED, args->offset); | ||
| 359 | drm_gem_object_unreference(&dobj->obj); | ||
| 360 | if (IS_ERR_VALUE(addr)) | ||
| 361 | return addr; | ||
| 362 | |||
| 363 | args->addr = addr; | ||
| 364 | |||
| 365 | return 0; | ||
| 366 | } | ||
| 367 | |||
| 368 | int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data, | ||
| 369 | struct drm_file *file) | ||
| 370 | { | ||
| 371 | struct drm_armada_gem_pwrite *args = data; | ||
| 372 | struct armada_gem_object *dobj; | ||
| 373 | char __user *ptr; | ||
| 374 | int ret; | ||
| 375 | |||
| 376 | DRM_DEBUG_DRIVER("handle %u off %u size %u ptr 0x%llx\n", | ||
| 377 | args->handle, args->offset, args->size, args->ptr); | ||
| 378 | |||
| 379 | if (args->size == 0) | ||
| 380 | return 0; | ||
| 381 | |||
| 382 | ptr = (char __user *)(uintptr_t)args->ptr; | ||
| 383 | |||
| 384 | if (!access_ok(VERIFY_READ, ptr, args->size)) | ||
| 385 | return -EFAULT; | ||
| 386 | |||
| 387 | ret = fault_in_multipages_readable(ptr, args->size); | ||
| 388 | if (ret) | ||
| 389 | return ret; | ||
| 390 | |||
| 391 | dobj = armada_gem_object_lookup(dev, file, args->handle); | ||
| 392 | if (dobj == NULL) | ||
| 393 | return -ENOENT; | ||
| 394 | |||
| 395 | /* Must be a kernel-mapped object */ | ||
| 396 | if (!dobj->addr) | ||
| 397 | return -EINVAL; | ||
| 398 | |||
| 399 | if (args->offset > dobj->obj.size || | ||
| 400 | args->size > dobj->obj.size - args->offset) { | ||
| 401 | DRM_ERROR("invalid size: object size %u\n", dobj->obj.size); | ||
| 402 | ret = -EINVAL; | ||
| 403 | goto unref; | ||
| 404 | } | ||
| 405 | |||
| 406 | if (copy_from_user(dobj->addr + args->offset, ptr, args->size)) { | ||
| 407 | ret = -EFAULT; | ||
| 408 | } else if (dobj->update) { | ||
| 409 | dobj->update(dobj->update_data); | ||
| 410 | ret = 0; | ||
| 411 | } | ||
| 412 | |||
| 413 | unref: | ||
| 414 | drm_gem_object_unreference_unlocked(&dobj->obj); | ||
| 415 | return ret; | ||
| 416 | } | ||
| 417 | |||
| 418 | /* Prime support */ | ||
| 419 | struct sg_table * | ||
| 420 | armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach, | ||
| 421 | enum dma_data_direction dir) | ||
| 422 | { | ||
| 423 | struct drm_gem_object *obj = attach->dmabuf->priv; | ||
| 424 | struct armada_gem_object *dobj = drm_to_armada_gem(obj); | ||
| 425 | struct scatterlist *sg; | ||
| 426 | struct sg_table *sgt; | ||
| 427 | int i, num; | ||
| 428 | |||
| 429 | sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); | ||
| 430 | if (!sgt) | ||
| 431 | return NULL; | ||
| 432 | |||
| 433 | if (dobj->obj.filp) { | ||
| 434 | struct address_space *mapping; | ||
| 435 | gfp_t gfp; | ||
| 436 | int count; | ||
| 437 | |||
| 438 | count = dobj->obj.size / PAGE_SIZE; | ||
| 439 | if (sg_alloc_table(sgt, count, GFP_KERNEL)) | ||
| 440 | goto free_sgt; | ||
| 441 | |||
| 442 | mapping = file_inode(dobj->obj.filp)->i_mapping; | ||
| 443 | gfp = mapping_gfp_mask(mapping); | ||
| 444 | |||
| 445 | for_each_sg(sgt->sgl, sg, count, i) { | ||
| 446 | struct page *page; | ||
| 447 | |||
| 448 | page = shmem_read_mapping_page_gfp(mapping, i, gfp); | ||
| 449 | if (IS_ERR(page)) { | ||
| 450 | num = i; | ||
| 451 | goto release; | ||
| 452 | } | ||
| 453 | |||
| 454 | sg_set_page(sg, page, PAGE_SIZE, 0); | ||
| 455 | } | ||
| 456 | |||
| 457 | if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) { | ||
| 458 | num = sgt->nents; | ||
| 459 | goto release; | ||
| 460 | } | ||
| 461 | } else if (dobj->page) { | ||
| 462 | /* Single contiguous page */ | ||
| 463 | if (sg_alloc_table(sgt, 1, GFP_KERNEL)) | ||
| 464 | goto free_sgt; | ||
| 465 | |||
| 466 | sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0); | ||
| 467 | |||
| 468 | if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) | ||
| 469 | goto free_table; | ||
| 470 | } else if (dobj->linear) { | ||
| 471 | /* Single contiguous physical region - no struct page */ | ||
| 472 | if (sg_alloc_table(sgt, 1, GFP_KERNEL)) | ||
| 473 | goto free_sgt; | ||
| 474 | sg_dma_address(sgt->sgl) = dobj->dev_addr; | ||
| 475 | sg_dma_len(sgt->sgl) = dobj->obj.size; | ||
| 476 | } else { | ||
| 477 | goto free_sgt; | ||
| 478 | } | ||
| 479 | return sgt; | ||
| 480 | |||
| 481 | release: | ||
| 482 | for_each_sg(sgt->sgl, sg, num, i) | ||
| 483 | page_cache_release(sg_page(sg)); | ||
| 484 | free_table: | ||
| 485 | sg_free_table(sgt); | ||
| 486 | free_sgt: | ||
| 487 | kfree(sgt); | ||
| 488 | return NULL; | ||
| 489 | } | ||
| 490 | |||
| 491 | static void armada_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach, | ||
| 492 | struct sg_table *sgt, enum dma_data_direction dir) | ||
| 493 | { | ||
| 494 | struct drm_gem_object *obj = attach->dmabuf->priv; | ||
| 495 | struct armada_gem_object *dobj = drm_to_armada_gem(obj); | ||
| 496 | int i; | ||
| 497 | |||
| 498 | if (!dobj->linear) | ||
| 499 | dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); | ||
| 500 | |||
| 501 | if (dobj->obj.filp) { | ||
| 502 | struct scatterlist *sg; | ||
| 503 | for_each_sg(sgt->sgl, sg, sgt->nents, i) | ||
| 504 | page_cache_release(sg_page(sg)); | ||
| 505 | } | ||
| 506 | |||
| 507 | sg_free_table(sgt); | ||
| 508 | kfree(sgt); | ||
| 509 | } | ||
| 510 | |||
| 511 | static void *armada_gem_dmabuf_no_kmap(struct dma_buf *buf, unsigned long n) | ||
| 512 | { | ||
| 513 | return NULL; | ||
| 514 | } | ||
| 515 | |||
| 516 | static void | ||
| 517 | armada_gem_dmabuf_no_kunmap(struct dma_buf *buf, unsigned long n, void *addr) | ||
| 518 | { | ||
| 519 | } | ||
| 520 | |||
| 521 | static int | ||
| 522 | armada_gem_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma) | ||
| 523 | { | ||
| 524 | return -EINVAL; | ||
| 525 | } | ||
| 526 | |||
| 527 | static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = { | ||
| 528 | .map_dma_buf = armada_gem_prime_map_dma_buf, | ||
| 529 | .unmap_dma_buf = armada_gem_prime_unmap_dma_buf, | ||
| 530 | .release = drm_gem_dmabuf_release, | ||
| 531 | .kmap_atomic = armada_gem_dmabuf_no_kmap, | ||
| 532 | .kunmap_atomic = armada_gem_dmabuf_no_kunmap, | ||
| 533 | .kmap = armada_gem_dmabuf_no_kmap, | ||
| 534 | .kunmap = armada_gem_dmabuf_no_kunmap, | ||
| 535 | .mmap = armada_gem_dmabuf_mmap, | ||
| 536 | }; | ||
| 537 | |||
| 538 | struct dma_buf * | ||
| 539 | armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, | ||
| 540 | int flags) | ||
| 541 | { | ||
| 542 | return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size, | ||
| 543 | O_RDWR); | ||
| 544 | } | ||
| 545 | |||
| 546 | struct drm_gem_object * | ||
| 547 | armada_gem_prime_import(struct drm_device *dev, struct dma_buf *buf) | ||
| 548 | { | ||
| 549 | struct dma_buf_attachment *attach; | ||
| 550 | struct armada_gem_object *dobj; | ||
| 551 | |||
| 552 | if (buf->ops == &armada_gem_prime_dmabuf_ops) { | ||
| 553 | struct drm_gem_object *obj = buf->priv; | ||
| 554 | if (obj->dev == dev) { | ||
| 555 | /* | ||
| 556 | * Importing our own dmabuf(s) increases the | ||
| 557 | * refcount on the gem object itself. | ||
| 558 | */ | ||
| 559 | drm_gem_object_reference(obj); | ||
| 560 | dma_buf_put(buf); | ||
| 561 | return obj; | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | attach = dma_buf_attach(buf, dev->dev); | ||
| 566 | if (IS_ERR(attach)) | ||
| 567 | return ERR_CAST(attach); | ||
| 568 | |||
| 569 | dobj = armada_gem_alloc_private_object(dev, buf->size); | ||
| 570 | if (!dobj) { | ||
| 571 | dma_buf_detach(buf, attach); | ||
| 572 | return ERR_PTR(-ENOMEM); | ||
| 573 | } | ||
| 574 | |||
| 575 | dobj->obj.import_attach = attach; | ||
| 576 | |||
| 577 | /* | ||
| 578 | * Don't call dma_buf_map_attachment() here - it maps the | ||
| 579 | * scatterlist immediately for DMA, and this is not always | ||
| 580 | * an appropriate thing to do. | ||
| 581 | */ | ||
| 582 | return &dobj->obj; | ||
| 583 | } | ||
| 584 | |||
| 585 | int armada_gem_map_import(struct armada_gem_object *dobj) | ||
| 586 | { | ||
| 587 | int ret; | ||
| 588 | |||
| 589 | dobj->sgt = dma_buf_map_attachment(dobj->obj.import_attach, | ||
| 590 | DMA_TO_DEVICE); | ||
| 591 | if (!dobj->sgt) { | ||
| 592 | DRM_ERROR("dma_buf_map_attachment() returned NULL\n"); | ||
| 593 | return -EINVAL; | ||
| 594 | } | ||
| 595 | if (IS_ERR(dobj->sgt)) { | ||
| 596 | ret = PTR_ERR(dobj->sgt); | ||
| 597 | dobj->sgt = NULL; | ||
| 598 | DRM_ERROR("dma_buf_map_attachment() error: %d\n", ret); | ||
| 599 | return ret; | ||
| 600 | } | ||
| 601 | if (dobj->sgt->nents > 1) { | ||
| 602 | DRM_ERROR("dma_buf_map_attachment() returned an (unsupported) scattered list\n"); | ||
| 603 | return -EINVAL; | ||
| 604 | } | ||
| 605 | if (sg_dma_len(dobj->sgt->sgl) < dobj->obj.size) { | ||
| 606 | DRM_ERROR("dma_buf_map_attachment() returned a small buffer\n"); | ||
| 607 | return -EINVAL; | ||
| 608 | } | ||
| 609 | dobj->dev_addr = sg_dma_address(dobj->sgt->sgl); | ||
| 610 | return 0; | ||
| 611 | } | ||
diff --git a/drivers/gpu/drm/armada/armada_gem.h b/drivers/gpu/drm/armada/armada_gem.h new file mode 100644 index 000000000000..00b6cd461a03 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_gem.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #ifndef ARMADA_GEM_H | ||
| 9 | #define ARMADA_GEM_H | ||
| 10 | |||
| 11 | /* GEM */ | ||
| 12 | struct armada_gem_object { | ||
| 13 | struct drm_gem_object obj; | ||
| 14 | void *addr; | ||
| 15 | phys_addr_t phys_addr; | ||
| 16 | resource_size_t dev_addr; | ||
| 17 | struct drm_mm_node *linear; /* for linear backed */ | ||
| 18 | struct page *page; /* for page backed */ | ||
| 19 | struct sg_table *sgt; /* for imported */ | ||
| 20 | void (*update)(void *); | ||
| 21 | void *update_data; | ||
| 22 | }; | ||
| 23 | |||
| 24 | extern const struct vm_operations_struct armada_gem_vm_ops; | ||
| 25 | |||
| 26 | #define drm_to_armada_gem(o) container_of(o, struct armada_gem_object, obj) | ||
| 27 | |||
| 28 | void armada_gem_free_object(struct drm_gem_object *); | ||
| 29 | int armada_gem_linear_back(struct drm_device *, struct armada_gem_object *); | ||
| 30 | void *armada_gem_map_object(struct drm_device *, struct armada_gem_object *); | ||
| 31 | struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *, | ||
| 32 | size_t); | ||
| 33 | int armada_gem_dumb_create(struct drm_file *, struct drm_device *, | ||
| 34 | struct drm_mode_create_dumb *); | ||
| 35 | int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *, | ||
| 36 | uint32_t, uint64_t *); | ||
| 37 | int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *, | ||
| 38 | uint32_t); | ||
| 39 | struct dma_buf *armada_gem_prime_export(struct drm_device *dev, | ||
| 40 | struct drm_gem_object *obj, int flags); | ||
| 41 | struct drm_gem_object *armada_gem_prime_import(struct drm_device *, | ||
| 42 | struct dma_buf *); | ||
| 43 | int armada_gem_map_import(struct armada_gem_object *); | ||
| 44 | |||
| 45 | static inline struct armada_gem_object *armada_gem_object_lookup( | ||
| 46 | struct drm_device *dev, struct drm_file *dfile, unsigned handle) | ||
| 47 | { | ||
| 48 | struct drm_gem_object *obj = drm_gem_object_lookup(dev, dfile, handle); | ||
| 49 | |||
| 50 | return obj ? drm_to_armada_gem(obj) : NULL; | ||
| 51 | } | ||
| 52 | #endif | ||
diff --git a/drivers/gpu/drm/armada/armada_hw.h b/drivers/gpu/drm/armada/armada_hw.h new file mode 100644 index 000000000000..216a4b50c36b --- /dev/null +++ b/drivers/gpu/drm/armada/armada_hw.h | |||
| @@ -0,0 +1,316 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * Rewritten from the dovefb driver, and Armada510 manuals. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #ifndef ARMADA_HW_H | ||
| 10 | #define ARMADA_HW_H | ||
| 11 | |||
| 12 | /* | ||
| 13 | * Note: the following registers are written from IRQ context: | ||
| 14 | * LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL | ||
| 15 | * LCD_SPU_DMA_START_ADDR_[YUV][01], LCD_SPU_DMA_PITCH_YC, | ||
| 16 | * LCD_SPU_DMA_PITCH_UV, LCD_SPU_DMA_OVSA_HPXL_VLN, | ||
| 17 | * LCD_SPU_DMA_HPXL_VLN, LCD_SPU_DZM_HPXL_VLN, LCD_SPU_DMA_CTRL0 | ||
| 18 | */ | ||
| 19 | enum { | ||
| 20 | LCD_SPU_ADV_REG = 0x0084, /* Armada 510 */ | ||
| 21 | LCD_SPU_DMA_START_ADDR_Y0 = 0x00c0, | ||
| 22 | LCD_SPU_DMA_START_ADDR_U0 = 0x00c4, | ||
| 23 | LCD_SPU_DMA_START_ADDR_V0 = 0x00c8, | ||
| 24 | LCD_CFG_DMA_START_ADDR_0 = 0x00cc, | ||
| 25 | LCD_SPU_DMA_START_ADDR_Y1 = 0x00d0, | ||
| 26 | LCD_SPU_DMA_START_ADDR_U1 = 0x00d4, | ||
| 27 | LCD_SPU_DMA_START_ADDR_V1 = 0x00d8, | ||
| 28 | LCD_CFG_DMA_START_ADDR_1 = 0x00dc, | ||
| 29 | LCD_SPU_DMA_PITCH_YC = 0x00e0, | ||
| 30 | LCD_SPU_DMA_PITCH_UV = 0x00e4, | ||
| 31 | LCD_SPU_DMA_OVSA_HPXL_VLN = 0x00e8, | ||
| 32 | LCD_SPU_DMA_HPXL_VLN = 0x00ec, | ||
| 33 | LCD_SPU_DZM_HPXL_VLN = 0x00f0, | ||
| 34 | LCD_CFG_GRA_START_ADDR0 = 0x00f4, | ||
| 35 | LCD_CFG_GRA_START_ADDR1 = 0x00f8, | ||
| 36 | LCD_CFG_GRA_PITCH = 0x00fc, | ||
| 37 | LCD_SPU_GRA_OVSA_HPXL_VLN = 0x0100, | ||
| 38 | LCD_SPU_GRA_HPXL_VLN = 0x0104, | ||
| 39 | LCD_SPU_GZM_HPXL_VLN = 0x0108, | ||
| 40 | LCD_SPU_HWC_OVSA_HPXL_VLN = 0x010c, | ||
| 41 | LCD_SPU_HWC_HPXL_VLN = 0x0110, | ||
| 42 | LCD_SPUT_V_H_TOTAL = 0x0114, | ||
| 43 | LCD_SPU_V_H_ACTIVE = 0x0118, | ||
| 44 | LCD_SPU_H_PORCH = 0x011c, | ||
| 45 | LCD_SPU_V_PORCH = 0x0120, | ||
| 46 | LCD_SPU_BLANKCOLOR = 0x0124, | ||
| 47 | LCD_SPU_ALPHA_COLOR1 = 0x0128, | ||
| 48 | LCD_SPU_ALPHA_COLOR2 = 0x012c, | ||
| 49 | LCD_SPU_COLORKEY_Y = 0x0130, | ||
| 50 | LCD_SPU_COLORKEY_U = 0x0134, | ||
| 51 | LCD_SPU_COLORKEY_V = 0x0138, | ||
| 52 | LCD_CFG_RDREG4F = 0x013c, /* Armada 510 */ | ||
| 53 | LCD_SPU_SPI_RXDATA = 0x0140, | ||
| 54 | LCD_SPU_ISA_RXDATA = 0x0144, | ||
| 55 | LCD_SPU_HWC_RDDAT = 0x0158, | ||
| 56 | LCD_SPU_GAMMA_RDDAT = 0x015c, | ||
| 57 | LCD_SPU_PALETTE_RDDAT = 0x0160, | ||
| 58 | LCD_SPU_IOPAD_IN = 0x0178, | ||
| 59 | LCD_CFG_RDREG5F = 0x017c, | ||
| 60 | LCD_SPU_SPI_CTRL = 0x0180, | ||
| 61 | LCD_SPU_SPI_TXDATA = 0x0184, | ||
| 62 | LCD_SPU_SMPN_CTRL = 0x0188, | ||
| 63 | LCD_SPU_DMA_CTRL0 = 0x0190, | ||
| 64 | LCD_SPU_DMA_CTRL1 = 0x0194, | ||
| 65 | LCD_SPU_SRAM_CTRL = 0x0198, | ||
| 66 | LCD_SPU_SRAM_WRDAT = 0x019c, | ||
| 67 | LCD_SPU_SRAM_PARA0 = 0x01a0, /* Armada 510 */ | ||
| 68 | LCD_SPU_SRAM_PARA1 = 0x01a4, | ||
| 69 | LCD_CFG_SCLK_DIV = 0x01a8, | ||
| 70 | LCD_SPU_CONTRAST = 0x01ac, | ||
| 71 | LCD_SPU_SATURATION = 0x01b0, | ||
| 72 | LCD_SPU_CBSH_HUE = 0x01b4, | ||
| 73 | LCD_SPU_DUMB_CTRL = 0x01b8, | ||
| 74 | LCD_SPU_IOPAD_CONTROL = 0x01bc, | ||
| 75 | LCD_SPU_IRQ_ENA = 0x01c0, | ||
| 76 | LCD_SPU_IRQ_ISR = 0x01c4, | ||
| 77 | }; | ||
| 78 | |||
| 79 | /* For LCD_SPU_ADV_REG */ | ||
| 80 | enum { | ||
| 81 | ADV_VSYNC_L_OFF = 0xfff << 20, | ||
| 82 | ADV_GRACOLORKEY = 1 << 19, | ||
| 83 | ADV_VIDCOLORKEY = 1 << 18, | ||
| 84 | ADV_HWC32BLEND = 1 << 15, | ||
| 85 | ADV_HWC32ARGB = 1 << 14, | ||
| 86 | ADV_HWC32ENABLE = 1 << 13, | ||
| 87 | ADV_VSYNCOFFEN = 1 << 12, | ||
| 88 | ADV_VSYNC_H_OFF = 0xfff << 0, | ||
| 89 | }; | ||
| 90 | |||
| 91 | enum { | ||
| 92 | CFG_565 = 0, | ||
| 93 | CFG_1555 = 1, | ||
| 94 | CFG_888PACK = 2, | ||
| 95 | CFG_X888 = 3, | ||
| 96 | CFG_8888 = 4, | ||
| 97 | CFG_422PACK = 5, | ||
| 98 | CFG_422 = 6, | ||
| 99 | CFG_420 = 7, | ||
| 100 | CFG_PSEUDO4 = 9, | ||
| 101 | CFG_PSEUDO8 = 10, | ||
| 102 | CFG_SWAPRB = 1 << 4, | ||
| 103 | CFG_SWAPUV = 1 << 3, | ||
| 104 | CFG_SWAPYU = 1 << 2, | ||
| 105 | CFG_YUV2RGB = 1 << 1, | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* For LCD_SPU_DMA_CTRL0 */ | ||
| 109 | enum { | ||
| 110 | CFG_NOBLENDING = 1 << 31, | ||
| 111 | CFG_GAMMA_ENA = 1 << 30, | ||
| 112 | CFG_CBSH_ENA = 1 << 29, | ||
| 113 | CFG_PALETTE_ENA = 1 << 28, | ||
| 114 | CFG_ARBFAST_ENA = 1 << 27, | ||
| 115 | CFG_HWC_1BITMOD = 1 << 26, | ||
| 116 | CFG_HWC_1BITENA = 1 << 25, | ||
| 117 | CFG_HWC_ENA = 1 << 24, | ||
| 118 | CFG_DMAFORMAT = 0xf << 20, | ||
| 119 | #define CFG_DMA_FMT(x) ((x) << 20) | ||
| 120 | CFG_GRAFORMAT = 0xf << 16, | ||
| 121 | #define CFG_GRA_FMT(x) ((x) << 16) | ||
| 122 | #define CFG_GRA_MOD(x) ((x) << 8) | ||
| 123 | CFG_GRA_FTOGGLE = 1 << 15, | ||
| 124 | CFG_GRA_HSMOOTH = 1 << 14, | ||
| 125 | CFG_GRA_TSTMODE = 1 << 13, | ||
| 126 | CFG_GRA_ENA = 1 << 8, | ||
| 127 | #define CFG_DMA_MOD(x) ((x) << 0) | ||
| 128 | CFG_DMA_FTOGGLE = 1 << 7, | ||
| 129 | CFG_DMA_HSMOOTH = 1 << 6, | ||
| 130 | CFG_DMA_TSTMODE = 1 << 5, | ||
| 131 | CFG_DMA_ENA = 1 << 0, | ||
| 132 | }; | ||
| 133 | |||
| 134 | enum { | ||
| 135 | CKMODE_DISABLE = 0, | ||
| 136 | CKMODE_Y = 1, | ||
| 137 | CKMODE_U = 2, | ||
| 138 | CKMODE_RGB = 3, | ||
| 139 | CKMODE_V = 4, | ||
| 140 | CKMODE_R = 5, | ||
| 141 | CKMODE_G = 6, | ||
| 142 | CKMODE_B = 7, | ||
| 143 | }; | ||
| 144 | |||
| 145 | /* For LCD_SPU_DMA_CTRL1 */ | ||
| 146 | enum { | ||
| 147 | CFG_FRAME_TRIG = 1 << 31, | ||
| 148 | CFG_VSYNC_INV = 1 << 27, | ||
| 149 | CFG_CKMODE_MASK = 0x7 << 24, | ||
| 150 | #define CFG_CKMODE(x) ((x) << 24) | ||
| 151 | CFG_CARRY = 1 << 23, | ||
| 152 | CFG_GATED_CLK = 1 << 21, | ||
| 153 | CFG_PWRDN_ENA = 1 << 20, | ||
| 154 | CFG_DSCALE_MASK = 0x3 << 18, | ||
| 155 | CFG_DSCALE_NONE = 0x0 << 18, | ||
| 156 | CFG_DSCALE_HALF = 0x1 << 18, | ||
| 157 | CFG_DSCALE_QUAR = 0x2 << 18, | ||
| 158 | CFG_ALPHAM_MASK = 0x3 << 16, | ||
| 159 | CFG_ALPHAM_VIDEO = 0x0 << 16, | ||
| 160 | CFG_ALPHAM_GRA = 0x1 << 16, | ||
| 161 | CFG_ALPHAM_CFG = 0x2 << 16, | ||
| 162 | CFG_ALPHA_MASK = 0xff << 8, | ||
| 163 | CFG_PIXCMD_MASK = 0xff, | ||
| 164 | }; | ||
| 165 | |||
| 166 | /* For LCD_SPU_SRAM_CTRL */ | ||
| 167 | enum { | ||
| 168 | SRAM_READ = 0 << 14, | ||
| 169 | SRAM_WRITE = 2 << 14, | ||
| 170 | SRAM_INIT = 3 << 14, | ||
| 171 | SRAM_HWC32_RAMR = 0xc << 8, | ||
| 172 | SRAM_HWC32_RAMG = 0xd << 8, | ||
| 173 | SRAM_HWC32_RAMB = 0xe << 8, | ||
| 174 | SRAM_HWC32_TRAN = 0xf << 8, | ||
| 175 | SRAM_HWC = 0xf << 8, | ||
| 176 | }; | ||
| 177 | |||
| 178 | /* For LCD_SPU_SRAM_PARA1 */ | ||
| 179 | enum { | ||
| 180 | CFG_CSB_256x32 = 1 << 15, /* cursor */ | ||
| 181 | CFG_CSB_256x24 = 1 << 14, /* palette */ | ||
| 182 | CFG_CSB_256x8 = 1 << 13, /* gamma */ | ||
| 183 | CFG_PDWN1920x32 = 1 << 8, /* Armada 510: power down vscale ram */ | ||
| 184 | CFG_PDWN256x32 = 1 << 7, /* power down cursor */ | ||
| 185 | CFG_PDWN256x24 = 1 << 6, /* power down palette */ | ||
| 186 | CFG_PDWN256x8 = 1 << 5, /* power down gamma */ | ||
| 187 | CFG_PDWNHWC = 1 << 4, /* Armada 510: power down all hwc ram */ | ||
| 188 | CFG_PDWN32x32 = 1 << 3, /* power down slave->smart ram */ | ||
| 189 | CFG_PDWN16x66 = 1 << 2, /* power down UV fifo */ | ||
| 190 | CFG_PDWN32x66 = 1 << 1, /* power down Y fifo */ | ||
| 191 | CFG_PDWN64x66 = 1 << 0, /* power down graphic fifo */ | ||
| 192 | }; | ||
| 193 | |||
| 194 | /* For LCD_CFG_SCLK_DIV */ | ||
| 195 | enum { | ||
| 196 | /* Armada 510 */ | ||
| 197 | SCLK_510_AXI = 0x0 << 30, | ||
| 198 | SCLK_510_EXTCLK0 = 0x1 << 30, | ||
| 199 | SCLK_510_PLL = 0x2 << 30, | ||
| 200 | SCLK_510_EXTCLK1 = 0x3 << 30, | ||
| 201 | SCLK_510_DIV_CHANGE = 1 << 29, | ||
| 202 | SCLK_510_FRAC_DIV_MASK = 0xfff << 16, | ||
| 203 | SCLK_510_INT_DIV_MASK = 0xffff << 0, | ||
| 204 | |||
| 205 | /* Armada 16x */ | ||
| 206 | SCLK_16X_AHB = 0x0 << 28, | ||
| 207 | SCLK_16X_PCLK = 0x1 << 28, | ||
| 208 | SCLK_16X_AXI = 0x4 << 28, | ||
| 209 | SCLK_16X_PLL = 0x8 << 28, | ||
| 210 | SCLK_16X_FRAC_DIV_MASK = 0xfff << 16, | ||
| 211 | SCLK_16X_INT_DIV_MASK = 0xffff << 0, | ||
| 212 | }; | ||
| 213 | |||
| 214 | /* For LCD_SPU_DUMB_CTRL */ | ||
| 215 | enum { | ||
| 216 | DUMB16_RGB565_0 = 0x0 << 28, | ||
| 217 | DUMB16_RGB565_1 = 0x1 << 28, | ||
| 218 | DUMB18_RGB666_0 = 0x2 << 28, | ||
| 219 | DUMB18_RGB666_1 = 0x3 << 28, | ||
| 220 | DUMB12_RGB444_0 = 0x4 << 28, | ||
| 221 | DUMB12_RGB444_1 = 0x5 << 28, | ||
| 222 | DUMB24_RGB888_0 = 0x6 << 28, | ||
| 223 | DUMB_BLANK = 0x7 << 28, | ||
| 224 | DUMB_MASK = 0xf << 28, | ||
| 225 | CFG_BIAS_OUT = 1 << 8, | ||
| 226 | CFG_REV_RGB = 1 << 7, | ||
| 227 | CFG_INV_CBLANK = 1 << 6, | ||
| 228 | CFG_INV_CSYNC = 1 << 5, /* Normally active high */ | ||
| 229 | CFG_INV_HENA = 1 << 4, | ||
| 230 | CFG_INV_VSYNC = 1 << 3, /* Normally active high */ | ||
| 231 | CFG_INV_HSYNC = 1 << 2, /* Normally active high */ | ||
| 232 | CFG_INV_PCLK = 1 << 1, | ||
| 233 | CFG_DUMB_ENA = 1 << 0, | ||
| 234 | }; | ||
| 235 | |||
| 236 | /* For LCD_SPU_IOPAD_CONTROL */ | ||
| 237 | enum { | ||
| 238 | CFG_VSCALE_LN_EN = 3 << 18, | ||
| 239 | CFG_GRA_VM_ENA = 1 << 15, | ||
| 240 | CFG_DMA_VM_ENA = 1 << 13, | ||
| 241 | CFG_CMD_VM_ENA = 1 << 11, | ||
| 242 | CFG_CSC_MASK = 3 << 8, | ||
| 243 | CFG_CSC_YUV_CCIR709 = 1 << 9, | ||
| 244 | CFG_CSC_YUV_CCIR601 = 0 << 9, | ||
| 245 | CFG_CSC_RGB_STUDIO = 1 << 8, | ||
| 246 | CFG_CSC_RGB_COMPUTER = 0 << 8, | ||
| 247 | CFG_IOPAD_MASK = 0xf << 0, | ||
| 248 | CFG_IOPAD_DUMB24 = 0x0 << 0, | ||
| 249 | CFG_IOPAD_DUMB18SPI = 0x1 << 0, | ||
| 250 | CFG_IOPAD_DUMB18GPIO = 0x2 << 0, | ||
| 251 | CFG_IOPAD_DUMB16SPI = 0x3 << 0, | ||
| 252 | CFG_IOPAD_DUMB16GPIO = 0x4 << 0, | ||
| 253 | CFG_IOPAD_DUMB12GPIO = 0x5 << 0, | ||
| 254 | CFG_IOPAD_SMART18 = 0x6 << 0, | ||
| 255 | CFG_IOPAD_SMART16 = 0x7 << 0, | ||
| 256 | CFG_IOPAD_SMART8 = 0x8 << 0, | ||
| 257 | }; | ||
| 258 | |||
| 259 | #define IOPAD_DUMB24 0x0 | ||
| 260 | |||
| 261 | /* For LCD_SPU_IRQ_ENA */ | ||
| 262 | enum { | ||
| 263 | DMA_FRAME_IRQ0_ENA = 1 << 31, | ||
| 264 | DMA_FRAME_IRQ1_ENA = 1 << 30, | ||
| 265 | DMA_FRAME_IRQ_ENA = DMA_FRAME_IRQ0_ENA | DMA_FRAME_IRQ1_ENA, | ||
| 266 | DMA_FF_UNDERFLOW_ENA = 1 << 29, | ||
| 267 | GRA_FRAME_IRQ0_ENA = 1 << 27, | ||
| 268 | GRA_FRAME_IRQ1_ENA = 1 << 26, | ||
| 269 | GRA_FRAME_IRQ_ENA = GRA_FRAME_IRQ0_ENA | GRA_FRAME_IRQ1_ENA, | ||
| 270 | GRA_FF_UNDERFLOW_ENA = 1 << 25, | ||
| 271 | VSYNC_IRQ_ENA = 1 << 23, | ||
| 272 | DUMB_FRAMEDONE_ENA = 1 << 22, | ||
| 273 | TWC_FRAMEDONE_ENA = 1 << 21, | ||
| 274 | HWC_FRAMEDONE_ENA = 1 << 20, | ||
| 275 | SLV_IRQ_ENA = 1 << 19, | ||
| 276 | SPI_IRQ_ENA = 1 << 18, | ||
| 277 | PWRDN_IRQ_ENA = 1 << 17, | ||
| 278 | ERR_IRQ_ENA = 1 << 16, | ||
| 279 | CLEAN_SPU_IRQ_ISR = 0xffff, | ||
| 280 | }; | ||
| 281 | |||
| 282 | /* For LCD_SPU_IRQ_ISR */ | ||
| 283 | enum { | ||
| 284 | DMA_FRAME_IRQ0 = 1 << 31, | ||
| 285 | DMA_FRAME_IRQ1 = 1 << 30, | ||
| 286 | DMA_FRAME_IRQ = DMA_FRAME_IRQ0 | DMA_FRAME_IRQ1, | ||
| 287 | DMA_FF_UNDERFLOW = 1 << 29, | ||
| 288 | GRA_FRAME_IRQ0 = 1 << 27, | ||
| 289 | GRA_FRAME_IRQ1 = 1 << 26, | ||
| 290 | GRA_FRAME_IRQ = GRA_FRAME_IRQ0 | GRA_FRAME_IRQ1, | ||
| 291 | GRA_FF_UNDERFLOW = 1 << 25, | ||
| 292 | VSYNC_IRQ = 1 << 23, | ||
| 293 | DUMB_FRAMEDONE = 1 << 22, | ||
| 294 | TWC_FRAMEDONE = 1 << 21, | ||
| 295 | HWC_FRAMEDONE = 1 << 20, | ||
| 296 | SLV_IRQ = 1 << 19, | ||
| 297 | SPI_IRQ = 1 << 18, | ||
| 298 | PWRDN_IRQ = 1 << 17, | ||
| 299 | ERR_IRQ = 1 << 16, | ||
| 300 | DMA_FRAME_IRQ0_LEVEL = 1 << 15, | ||
| 301 | DMA_FRAME_IRQ1_LEVEL = 1 << 14, | ||
| 302 | DMA_FRAME_CNT_ISR = 3 << 12, | ||
| 303 | GRA_FRAME_IRQ0_LEVEL = 1 << 11, | ||
| 304 | GRA_FRAME_IRQ1_LEVEL = 1 << 10, | ||
| 305 | GRA_FRAME_CNT_ISR = 3 << 8, | ||
| 306 | VSYNC_IRQ_LEVEL = 1 << 7, | ||
| 307 | DUMB_FRAMEDONE_LEVEL = 1 << 6, | ||
| 308 | TWC_FRAMEDONE_LEVEL = 1 << 5, | ||
| 309 | HWC_FRAMEDONE_LEVEL = 1 << 4, | ||
| 310 | SLV_FF_EMPTY = 1 << 3, | ||
| 311 | DMA_FF_ALLEMPTY = 1 << 2, | ||
| 312 | GRA_FF_ALLEMPTY = 1 << 1, | ||
| 313 | PWRDN_IRQ_LEVEL = 1 << 0, | ||
| 314 | }; | ||
| 315 | |||
| 316 | #endif | ||
diff --git a/drivers/gpu/drm/armada/armada_ioctlP.h b/drivers/gpu/drm/armada/armada_ioctlP.h new file mode 100644 index 000000000000..bd8c4562066c --- /dev/null +++ b/drivers/gpu/drm/armada/armada_ioctlP.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #ifndef ARMADA_IOCTLP_H | ||
| 9 | #define ARMADA_IOCTLP_H | ||
| 10 | |||
| 11 | #define ARMADA_IOCTL_PROTO(name)\ | ||
| 12 | extern int armada_##name##_ioctl(struct drm_device *, void *, struct drm_file *) | ||
| 13 | |||
| 14 | ARMADA_IOCTL_PROTO(gem_create); | ||
| 15 | ARMADA_IOCTL_PROTO(gem_mmap); | ||
| 16 | ARMADA_IOCTL_PROTO(gem_pwrite); | ||
| 17 | |||
| 18 | #endif | ||
diff --git a/drivers/gpu/drm/armada/armada_output.c b/drivers/gpu/drm/armada/armada_output.c new file mode 100644 index 000000000000..d685a5421485 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_output.c | |||
| @@ -0,0 +1,158 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #include <drm/drmP.h> | ||
| 9 | #include <drm/drm_crtc_helper.h> | ||
| 10 | #include <drm/drm_edid.h> | ||
| 11 | #include <drm/drm_encoder_slave.h> | ||
| 12 | #include "armada_output.h" | ||
| 13 | #include "armada_drm.h" | ||
| 14 | |||
| 15 | struct armada_connector { | ||
| 16 | struct drm_connector conn; | ||
| 17 | const struct armada_output_type *type; | ||
| 18 | }; | ||
| 19 | |||
| 20 | #define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn) | ||
| 21 | |||
| 22 | struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn) | ||
| 23 | { | ||
| 24 | struct drm_encoder *enc = conn->encoder; | ||
| 25 | |||
| 26 | return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]); | ||
| 27 | } | ||
| 28 | |||
| 29 | static enum drm_connector_status armada_drm_connector_detect( | ||
| 30 | struct drm_connector *conn, bool force) | ||
| 31 | { | ||
| 32 | struct armada_connector *dconn = drm_to_armada_conn(conn); | ||
| 33 | enum drm_connector_status status = connector_status_disconnected; | ||
| 34 | |||
| 35 | if (dconn->type->detect) { | ||
| 36 | status = dconn->type->detect(conn, force); | ||
| 37 | } else { | ||
| 38 | struct drm_encoder *enc = armada_drm_connector_encoder(conn); | ||
| 39 | |||
| 40 | if (enc) | ||
| 41 | status = encoder_helper_funcs(enc)->detect(enc, conn); | ||
| 42 | } | ||
| 43 | |||
| 44 | return status; | ||
| 45 | } | ||
| 46 | |||
| 47 | static void armada_drm_connector_destroy(struct drm_connector *conn) | ||
| 48 | { | ||
| 49 | struct armada_connector *dconn = drm_to_armada_conn(conn); | ||
| 50 | |||
| 51 | drm_sysfs_connector_remove(conn); | ||
| 52 | drm_connector_cleanup(conn); | ||
| 53 | kfree(dconn); | ||
| 54 | } | ||
| 55 | |||
| 56 | static int armada_drm_connector_set_property(struct drm_connector *conn, | ||
| 57 | struct drm_property *property, uint64_t value) | ||
| 58 | { | ||
| 59 | struct armada_connector *dconn = drm_to_armada_conn(conn); | ||
| 60 | |||
| 61 | if (!dconn->type->set_property) | ||
| 62 | return -EINVAL; | ||
| 63 | |||
| 64 | return dconn->type->set_property(conn, property, value); | ||
| 65 | } | ||
| 66 | |||
| 67 | static const struct drm_connector_funcs armada_drm_conn_funcs = { | ||
| 68 | .dpms = drm_helper_connector_dpms, | ||
| 69 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
| 70 | .detect = armada_drm_connector_detect, | ||
| 71 | .destroy = armada_drm_connector_destroy, | ||
| 72 | .set_property = armada_drm_connector_set_property, | ||
| 73 | }; | ||
| 74 | |||
| 75 | void armada_drm_encoder_prepare(struct drm_encoder *encoder) | ||
| 76 | { | ||
| 77 | encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF); | ||
| 78 | } | ||
| 79 | |||
| 80 | void armada_drm_encoder_commit(struct drm_encoder *encoder) | ||
| 81 | { | ||
| 82 | encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON); | ||
| 83 | } | ||
| 84 | |||
| 85 | bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder, | ||
| 86 | const struct drm_display_mode *mode, struct drm_display_mode *adjusted) | ||
| 87 | { | ||
| 88 | return true; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Shouldn't this be a generic helper function? */ | ||
| 92 | int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn, | ||
| 93 | struct drm_display_mode *mode) | ||
| 94 | { | ||
| 95 | struct drm_encoder *encoder = armada_drm_connector_encoder(conn); | ||
| 96 | int valid = MODE_BAD; | ||
| 97 | |||
| 98 | if (encoder) { | ||
| 99 | struct drm_encoder_slave *slave = to_encoder_slave(encoder); | ||
| 100 | |||
| 101 | valid = slave->slave_funcs->mode_valid(encoder, mode); | ||
| 102 | } | ||
| 103 | return valid; | ||
| 104 | } | ||
| 105 | |||
| 106 | int armada_drm_slave_encoder_set_property(struct drm_connector *conn, | ||
| 107 | struct drm_property *property, uint64_t value) | ||
| 108 | { | ||
| 109 | struct drm_encoder *encoder = armada_drm_connector_encoder(conn); | ||
| 110 | int rc = -EINVAL; | ||
| 111 | |||
| 112 | if (encoder) { | ||
| 113 | struct drm_encoder_slave *slave = to_encoder_slave(encoder); | ||
| 114 | |||
| 115 | rc = slave->slave_funcs->set_property(encoder, conn, property, | ||
| 116 | value); | ||
| 117 | } | ||
| 118 | return rc; | ||
| 119 | } | ||
| 120 | |||
| 121 | int armada_output_create(struct drm_device *dev, | ||
| 122 | const struct armada_output_type *type, const void *data) | ||
| 123 | { | ||
| 124 | struct armada_connector *dconn; | ||
| 125 | int ret; | ||
| 126 | |||
| 127 | dconn = kzalloc(sizeof(*dconn), GFP_KERNEL); | ||
| 128 | if (!dconn) | ||
| 129 | return -ENOMEM; | ||
| 130 | |||
| 131 | dconn->type = type; | ||
| 132 | |||
| 133 | ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs, | ||
| 134 | type->connector_type); | ||
| 135 | if (ret) { | ||
| 136 | DRM_ERROR("unable to init connector\n"); | ||
| 137 | goto err_destroy_dconn; | ||
| 138 | } | ||
| 139 | |||
| 140 | ret = type->create(&dconn->conn, data); | ||
| 141 | if (ret) | ||
| 142 | goto err_conn; | ||
| 143 | |||
| 144 | ret = drm_sysfs_connector_add(&dconn->conn); | ||
| 145 | if (ret) | ||
| 146 | goto err_sysfs; | ||
| 147 | |||
| 148 | return 0; | ||
| 149 | |||
| 150 | err_sysfs: | ||
| 151 | if (dconn->conn.encoder) | ||
| 152 | dconn->conn.encoder->funcs->destroy(dconn->conn.encoder); | ||
| 153 | err_conn: | ||
| 154 | drm_connector_cleanup(&dconn->conn); | ||
| 155 | err_destroy_dconn: | ||
| 156 | kfree(dconn); | ||
| 157 | return ret; | ||
| 158 | } | ||
diff --git a/drivers/gpu/drm/armada/armada_output.h b/drivers/gpu/drm/armada/armada_output.h new file mode 100644 index 000000000000..4126d43b5057 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_output.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #ifndef ARMADA_CONNETOR_H | ||
| 9 | #define ARMADA_CONNETOR_H | ||
| 10 | |||
| 11 | #define encoder_helper_funcs(encoder) \ | ||
| 12 | ((struct drm_encoder_helper_funcs *)encoder->helper_private) | ||
| 13 | |||
| 14 | struct armada_output_type { | ||
| 15 | int connector_type; | ||
| 16 | enum drm_connector_status (*detect)(struct drm_connector *, bool); | ||
| 17 | int (*create)(struct drm_connector *, const void *); | ||
| 18 | int (*set_property)(struct drm_connector *, struct drm_property *, | ||
| 19 | uint64_t); | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn); | ||
| 23 | |||
| 24 | void armada_drm_encoder_prepare(struct drm_encoder *encoder); | ||
| 25 | void armada_drm_encoder_commit(struct drm_encoder *encoder); | ||
| 26 | |||
| 27 | bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder, | ||
| 28 | const struct drm_display_mode *mode, struct drm_display_mode *adj); | ||
| 29 | |||
| 30 | int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn, | ||
| 31 | struct drm_display_mode *mode); | ||
| 32 | |||
| 33 | int armada_drm_slave_encoder_set_property(struct drm_connector *conn, | ||
| 34 | struct drm_property *property, uint64_t value); | ||
| 35 | |||
| 36 | int armada_output_create(struct drm_device *dev, | ||
| 37 | const struct armada_output_type *type, const void *data); | ||
| 38 | |||
| 39 | #endif | ||
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c new file mode 100644 index 000000000000..c5b06fdb459c --- /dev/null +++ b/drivers/gpu/drm/armada/armada_overlay.c | |||
| @@ -0,0 +1,477 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * Rewritten from the dovefb driver, and Armada510 manuals. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #include <drm/drmP.h> | ||
| 10 | #include "armada_crtc.h" | ||
| 11 | #include "armada_drm.h" | ||
| 12 | #include "armada_fb.h" | ||
| 13 | #include "armada_gem.h" | ||
| 14 | #include "armada_hw.h" | ||
| 15 | #include <drm/armada_drm.h> | ||
| 16 | #include "armada_ioctlP.h" | ||
| 17 | |||
| 18 | struct armada_plane_properties { | ||
| 19 | uint32_t colorkey_yr; | ||
| 20 | uint32_t colorkey_ug; | ||
| 21 | uint32_t colorkey_vb; | ||
| 22 | #define K2R(val) (((val) >> 0) & 0xff) | ||
| 23 | #define K2G(val) (((val) >> 8) & 0xff) | ||
| 24 | #define K2B(val) (((val) >> 16) & 0xff) | ||
| 25 | int16_t brightness; | ||
| 26 | uint16_t contrast; | ||
| 27 | uint16_t saturation; | ||
| 28 | uint32_t colorkey_mode; | ||
| 29 | }; | ||
| 30 | |||
| 31 | struct armada_plane { | ||
| 32 | struct drm_plane base; | ||
| 33 | spinlock_t lock; | ||
| 34 | struct drm_framebuffer *old_fb; | ||
| 35 | uint32_t src_hw; | ||
| 36 | uint32_t dst_hw; | ||
| 37 | uint32_t dst_yx; | ||
| 38 | uint32_t ctrl0; | ||
| 39 | struct { | ||
| 40 | struct armada_vbl_event update; | ||
| 41 | struct armada_regs regs[13]; | ||
| 42 | wait_queue_head_t wait; | ||
| 43 | } vbl; | ||
| 44 | struct armada_plane_properties prop; | ||
| 45 | }; | ||
| 46 | #define drm_to_armada_plane(p) container_of(p, struct armada_plane, base) | ||
| 47 | |||
| 48 | |||
| 49 | static void | ||
| 50 | armada_ovl_update_attr(struct armada_plane_properties *prop, | ||
| 51 | struct armada_crtc *dcrtc) | ||
| 52 | { | ||
| 53 | writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y); | ||
| 54 | writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U); | ||
| 55 | writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V); | ||
| 56 | |||
| 57 | writel_relaxed(prop->brightness << 16 | prop->contrast, | ||
| 58 | dcrtc->base + LCD_SPU_CONTRAST); | ||
| 59 | /* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */ | ||
| 60 | writel_relaxed(prop->saturation << 16, | ||
| 61 | dcrtc->base + LCD_SPU_SATURATION); | ||
| 62 | writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE); | ||
| 63 | |||
| 64 | spin_lock_irq(&dcrtc->irq_lock); | ||
| 65 | armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA, | ||
| 66 | CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK, | ||
| 67 | dcrtc->base + LCD_SPU_DMA_CTRL1); | ||
| 68 | |||
| 69 | armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG); | ||
| 70 | spin_unlock_irq(&dcrtc->irq_lock); | ||
| 71 | } | ||
| 72 | |||
| 73 | /* === Plane support === */ | ||
| 74 | static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data) | ||
| 75 | { | ||
| 76 | struct armada_plane *dplane = data; | ||
| 77 | struct drm_framebuffer *fb; | ||
| 78 | |||
| 79 | armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs); | ||
| 80 | |||
| 81 | spin_lock(&dplane->lock); | ||
| 82 | fb = dplane->old_fb; | ||
| 83 | dplane->old_fb = NULL; | ||
| 84 | spin_unlock(&dplane->lock); | ||
| 85 | |||
| 86 | if (fb) | ||
| 87 | armada_drm_queue_unref_work(dcrtc->crtc.dev, fb); | ||
| 88 | } | ||
| 89 | |||
| 90 | static unsigned armada_limit(int start, unsigned size, unsigned max) | ||
| 91 | { | ||
| 92 | int end = start + size; | ||
| 93 | if (end < 0) | ||
| 94 | return 0; | ||
| 95 | if (start < 0) | ||
| 96 | start = 0; | ||
| 97 | return (unsigned)end > max ? max - start : end - start; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int | ||
| 101 | armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, | ||
| 102 | struct drm_framebuffer *fb, | ||
| 103 | int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h, | ||
| 104 | uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) | ||
| 105 | { | ||
| 106 | struct armada_plane *dplane = drm_to_armada_plane(plane); | ||
| 107 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | ||
| 108 | uint32_t val, ctrl0; | ||
| 109 | unsigned idx = 0; | ||
| 110 | int ret; | ||
| 111 | |||
| 112 | crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay); | ||
| 113 | crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay); | ||
| 114 | ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) | | ||
| 115 | CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) | | ||
| 116 | CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA; | ||
| 117 | |||
| 118 | /* Does the position/size result in nothing to display? */ | ||
| 119 | if (crtc_w == 0 || crtc_h == 0) { | ||
| 120 | ctrl0 &= ~CFG_DMA_ENA; | ||
| 121 | } | ||
| 122 | |||
| 123 | /* | ||
| 124 | * FIXME: if the starting point is off screen, we need to | ||
| 125 | * adjust src_x, src_y, src_w, src_h appropriately, and | ||
| 126 | * according to the scale. | ||
| 127 | */ | ||
| 128 | |||
| 129 | if (!dcrtc->plane) { | ||
| 130 | dcrtc->plane = plane; | ||
| 131 | armada_ovl_update_attr(&dplane->prop, dcrtc); | ||
| 132 | } | ||
| 133 | |||
| 134 | /* FIXME: overlay on an interlaced display */ | ||
| 135 | /* Just updating the position/size? */ | ||
| 136 | if (plane->fb == fb && dplane->ctrl0 == ctrl0) { | ||
| 137 | val = (src_h & 0xffff0000) | src_w >> 16; | ||
| 138 | dplane->src_hw = val; | ||
| 139 | writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN); | ||
| 140 | val = crtc_h << 16 | crtc_w; | ||
| 141 | dplane->dst_hw = val; | ||
| 142 | writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN); | ||
| 143 | val = crtc_y << 16 | crtc_x; | ||
| 144 | dplane->dst_yx = val; | ||
| 145 | writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN); | ||
| 146 | return 0; | ||
| 147 | } else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) { | ||
| 148 | /* Power up the Y/U/V FIFOs on ENA 0->1 transitions */ | ||
| 149 | armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66, | ||
| 150 | dcrtc->base + LCD_SPU_SRAM_PARA1); | ||
| 151 | } | ||
| 152 | |||
| 153 | ret = wait_event_timeout(dplane->vbl.wait, | ||
| 154 | list_empty(&dplane->vbl.update.node), | ||
| 155 | HZ/25); | ||
| 156 | if (ret < 0) | ||
| 157 | return ret; | ||
| 158 | |||
| 159 | if (plane->fb != fb) { | ||
| 160 | struct armada_gem_object *obj = drm_fb_obj(fb); | ||
| 161 | uint32_t sy, su, sv; | ||
| 162 | |||
| 163 | /* | ||
| 164 | * Take a reference on the new framebuffer - we want to | ||
| 165 | * hold on to it while the hardware is displaying it. | ||
| 166 | */ | ||
| 167 | drm_framebuffer_reference(fb); | ||
| 168 | |||
| 169 | if (plane->fb) { | ||
| 170 | struct drm_framebuffer *older_fb; | ||
| 171 | |||
| 172 | spin_lock_irq(&dplane->lock); | ||
| 173 | older_fb = dplane->old_fb; | ||
| 174 | dplane->old_fb = plane->fb; | ||
| 175 | spin_unlock_irq(&dplane->lock); | ||
| 176 | if (older_fb) | ||
| 177 | armada_drm_queue_unref_work(dcrtc->crtc.dev, | ||
| 178 | older_fb); | ||
| 179 | } | ||
| 180 | |||
| 181 | src_y >>= 16; | ||
| 182 | src_x >>= 16; | ||
| 183 | sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] + | ||
| 184 | src_x * fb->bits_per_pixel / 8; | ||
| 185 | su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] + | ||
| 186 | src_x; | ||
| 187 | sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] + | ||
| 188 | src_x; | ||
| 189 | |||
| 190 | armada_reg_queue_set(dplane->vbl.regs, idx, sy, | ||
| 191 | LCD_SPU_DMA_START_ADDR_Y0); | ||
| 192 | armada_reg_queue_set(dplane->vbl.regs, idx, su, | ||
| 193 | LCD_SPU_DMA_START_ADDR_U0); | ||
| 194 | armada_reg_queue_set(dplane->vbl.regs, idx, sv, | ||
| 195 | LCD_SPU_DMA_START_ADDR_V0); | ||
| 196 | armada_reg_queue_set(dplane->vbl.regs, idx, sy, | ||
| 197 | LCD_SPU_DMA_START_ADDR_Y1); | ||
| 198 | armada_reg_queue_set(dplane->vbl.regs, idx, su, | ||
| 199 | LCD_SPU_DMA_START_ADDR_U1); | ||
| 200 | armada_reg_queue_set(dplane->vbl.regs, idx, sv, | ||
| 201 | LCD_SPU_DMA_START_ADDR_V1); | ||
| 202 | |||
| 203 | val = fb->pitches[0] << 16 | fb->pitches[0]; | ||
| 204 | armada_reg_queue_set(dplane->vbl.regs, idx, val, | ||
| 205 | LCD_SPU_DMA_PITCH_YC); | ||
| 206 | val = fb->pitches[1] << 16 | fb->pitches[2]; | ||
| 207 | armada_reg_queue_set(dplane->vbl.regs, idx, val, | ||
| 208 | LCD_SPU_DMA_PITCH_UV); | ||
| 209 | } | ||
| 210 | |||
| 211 | val = (src_h & 0xffff0000) | src_w >> 16; | ||
| 212 | if (dplane->src_hw != val) { | ||
| 213 | dplane->src_hw = val; | ||
| 214 | armada_reg_queue_set(dplane->vbl.regs, idx, val, | ||
| 215 | LCD_SPU_DMA_HPXL_VLN); | ||
| 216 | } | ||
| 217 | val = crtc_h << 16 | crtc_w; | ||
| 218 | if (dplane->dst_hw != val) { | ||
| 219 | dplane->dst_hw = val; | ||
| 220 | armada_reg_queue_set(dplane->vbl.regs, idx, val, | ||
| 221 | LCD_SPU_DZM_HPXL_VLN); | ||
| 222 | } | ||
| 223 | val = crtc_y << 16 | crtc_x; | ||
| 224 | if (dplane->dst_yx != val) { | ||
| 225 | dplane->dst_yx = val; | ||
| 226 | armada_reg_queue_set(dplane->vbl.regs, idx, val, | ||
| 227 | LCD_SPU_DMA_OVSA_HPXL_VLN); | ||
| 228 | } | ||
| 229 | if (dplane->ctrl0 != ctrl0) { | ||
| 230 | dplane->ctrl0 = ctrl0; | ||
| 231 | armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0, | ||
| 232 | CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE | | ||
| 233 | CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE | | ||
| 234 | CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU | | ||
| 235 | CFG_YUV2RGB) | CFG_DMA_ENA, | ||
| 236 | LCD_SPU_DMA_CTRL0); | ||
| 237 | } | ||
| 238 | if (idx) { | ||
| 239 | armada_reg_queue_end(dplane->vbl.regs, idx); | ||
| 240 | armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update); | ||
| 241 | } | ||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int armada_plane_disable(struct drm_plane *plane) | ||
| 246 | { | ||
| 247 | struct armada_plane *dplane = drm_to_armada_plane(plane); | ||
| 248 | struct drm_framebuffer *fb; | ||
| 249 | struct armada_crtc *dcrtc; | ||
| 250 | |||
| 251 | if (!dplane->base.crtc) | ||
| 252 | return 0; | ||
| 253 | |||
| 254 | dcrtc = drm_to_armada_crtc(dplane->base.crtc); | ||
| 255 | dcrtc->plane = NULL; | ||
| 256 | |||
| 257 | spin_lock_irq(&dcrtc->irq_lock); | ||
| 258 | armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update); | ||
| 259 | armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0); | ||
| 260 | dplane->ctrl0 = 0; | ||
| 261 | spin_unlock_irq(&dcrtc->irq_lock); | ||
| 262 | |||
| 263 | /* Power down the Y/U/V FIFOs */ | ||
| 264 | armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0, | ||
| 265 | dcrtc->base + LCD_SPU_SRAM_PARA1); | ||
| 266 | |||
| 267 | if (plane->fb) | ||
| 268 | drm_framebuffer_unreference(plane->fb); | ||
| 269 | |||
| 270 | spin_lock_irq(&dplane->lock); | ||
| 271 | fb = dplane->old_fb; | ||
| 272 | dplane->old_fb = NULL; | ||
| 273 | spin_unlock_irq(&dplane->lock); | ||
| 274 | if (fb) | ||
| 275 | drm_framebuffer_unreference(fb); | ||
| 276 | |||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | |||
| 280 | static void armada_plane_destroy(struct drm_plane *plane) | ||
| 281 | { | ||
| 282 | kfree(plane); | ||
| 283 | } | ||
| 284 | |||
| 285 | static int armada_plane_set_property(struct drm_plane *plane, | ||
| 286 | struct drm_property *property, uint64_t val) | ||
| 287 | { | ||
| 288 | struct armada_private *priv = plane->dev->dev_private; | ||
| 289 | struct armada_plane *dplane = drm_to_armada_plane(plane); | ||
| 290 | bool update_attr = false; | ||
| 291 | |||
| 292 | if (property == priv->colorkey_prop) { | ||
| 293 | #define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8) | ||
| 294 | dplane->prop.colorkey_yr = CCC(K2R(val)); | ||
| 295 | dplane->prop.colorkey_ug = CCC(K2G(val)); | ||
| 296 | dplane->prop.colorkey_vb = CCC(K2B(val)); | ||
| 297 | #undef CCC | ||
| 298 | update_attr = true; | ||
| 299 | } else if (property == priv->colorkey_min_prop) { | ||
| 300 | dplane->prop.colorkey_yr &= ~0x00ff0000; | ||
| 301 | dplane->prop.colorkey_yr |= K2R(val) << 16; | ||
| 302 | dplane->prop.colorkey_ug &= ~0x00ff0000; | ||
| 303 | dplane->prop.colorkey_ug |= K2G(val) << 16; | ||
| 304 | dplane->prop.colorkey_vb &= ~0x00ff0000; | ||
| 305 | dplane->prop.colorkey_vb |= K2B(val) << 16; | ||
| 306 | update_attr = true; | ||
| 307 | } else if (property == priv->colorkey_max_prop) { | ||
| 308 | dplane->prop.colorkey_yr &= ~0xff000000; | ||
| 309 | dplane->prop.colorkey_yr |= K2R(val) << 24; | ||
| 310 | dplane->prop.colorkey_ug &= ~0xff000000; | ||
| 311 | dplane->prop.colorkey_ug |= K2G(val) << 24; | ||
| 312 | dplane->prop.colorkey_vb &= ~0xff000000; | ||
| 313 | dplane->prop.colorkey_vb |= K2B(val) << 24; | ||
| 314 | update_attr = true; | ||
| 315 | } else if (property == priv->colorkey_val_prop) { | ||
| 316 | dplane->prop.colorkey_yr &= ~0x0000ff00; | ||
| 317 | dplane->prop.colorkey_yr |= K2R(val) << 8; | ||
| 318 | dplane->prop.colorkey_ug &= ~0x0000ff00; | ||
| 319 | dplane->prop.colorkey_ug |= K2G(val) << 8; | ||
| 320 | dplane->prop.colorkey_vb &= ~0x0000ff00; | ||
| 321 | dplane->prop.colorkey_vb |= K2B(val) << 8; | ||
| 322 | update_attr = true; | ||
| 323 | } else if (property == priv->colorkey_alpha_prop) { | ||
| 324 | dplane->prop.colorkey_yr &= ~0x000000ff; | ||
| 325 | dplane->prop.colorkey_yr |= K2R(val); | ||
| 326 | dplane->prop.colorkey_ug &= ~0x000000ff; | ||
| 327 | dplane->prop.colorkey_ug |= K2G(val); | ||
| 328 | dplane->prop.colorkey_vb &= ~0x000000ff; | ||
| 329 | dplane->prop.colorkey_vb |= K2B(val); | ||
| 330 | update_attr = true; | ||
| 331 | } else if (property == priv->colorkey_mode_prop) { | ||
| 332 | dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK; | ||
| 333 | dplane->prop.colorkey_mode |= CFG_CKMODE(val); | ||
| 334 | update_attr = true; | ||
| 335 | } else if (property == priv->brightness_prop) { | ||
| 336 | dplane->prop.brightness = val - 256; | ||
| 337 | update_attr = true; | ||
| 338 | } else if (property == priv->contrast_prop) { | ||
| 339 | dplane->prop.contrast = val; | ||
| 340 | update_attr = true; | ||
| 341 | } else if (property == priv->saturation_prop) { | ||
| 342 | dplane->prop.saturation = val; | ||
| 343 | update_attr = true; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (update_attr && dplane->base.crtc) | ||
| 347 | armada_ovl_update_attr(&dplane->prop, | ||
| 348 | drm_to_armada_crtc(dplane->base.crtc)); | ||
| 349 | |||
| 350 | return 0; | ||
| 351 | } | ||
| 352 | |||
| 353 | static const struct drm_plane_funcs armada_plane_funcs = { | ||
| 354 | .update_plane = armada_plane_update, | ||
| 355 | .disable_plane = armada_plane_disable, | ||
| 356 | .destroy = armada_plane_destroy, | ||
| 357 | .set_property = armada_plane_set_property, | ||
| 358 | }; | ||
| 359 | |||
| 360 | static const uint32_t armada_formats[] = { | ||
| 361 | DRM_FORMAT_UYVY, | ||
| 362 | DRM_FORMAT_YUYV, | ||
| 363 | DRM_FORMAT_YUV420, | ||
| 364 | DRM_FORMAT_YVU420, | ||
| 365 | DRM_FORMAT_YUV422, | ||
| 366 | DRM_FORMAT_YVU422, | ||
| 367 | DRM_FORMAT_VYUY, | ||
| 368 | DRM_FORMAT_YVYU, | ||
| 369 | DRM_FORMAT_ARGB8888, | ||
| 370 | DRM_FORMAT_ABGR8888, | ||
| 371 | DRM_FORMAT_XRGB8888, | ||
| 372 | DRM_FORMAT_XBGR8888, | ||
| 373 | DRM_FORMAT_RGB888, | ||
| 374 | DRM_FORMAT_BGR888, | ||
| 375 | DRM_FORMAT_ARGB1555, | ||
| 376 | DRM_FORMAT_ABGR1555, | ||
| 377 | DRM_FORMAT_RGB565, | ||
| 378 | DRM_FORMAT_BGR565, | ||
| 379 | }; | ||
| 380 | |||
| 381 | static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = { | ||
| 382 | { CKMODE_DISABLE, "disabled" }, | ||
| 383 | { CKMODE_Y, "Y component" }, | ||
| 384 | { CKMODE_U, "U component" }, | ||
| 385 | { CKMODE_V, "V component" }, | ||
| 386 | { CKMODE_RGB, "RGB" }, | ||
| 387 | { CKMODE_R, "R component" }, | ||
| 388 | { CKMODE_G, "G component" }, | ||
| 389 | { CKMODE_B, "B component" }, | ||
| 390 | }; | ||
| 391 | |||
| 392 | static int armada_overlay_create_properties(struct drm_device *dev) | ||
| 393 | { | ||
| 394 | struct armada_private *priv = dev->dev_private; | ||
| 395 | |||
| 396 | if (priv->colorkey_prop) | ||
| 397 | return 0; | ||
| 398 | |||
| 399 | priv->colorkey_prop = drm_property_create_range(dev, 0, | ||
| 400 | "colorkey", 0, 0xffffff); | ||
| 401 | priv->colorkey_min_prop = drm_property_create_range(dev, 0, | ||
| 402 | "colorkey_min", 0, 0xffffff); | ||
| 403 | priv->colorkey_max_prop = drm_property_create_range(dev, 0, | ||
| 404 | "colorkey_max", 0, 0xffffff); | ||
| 405 | priv->colorkey_val_prop = drm_property_create_range(dev, 0, | ||
| 406 | "colorkey_val", 0, 0xffffff); | ||
| 407 | priv->colorkey_alpha_prop = drm_property_create_range(dev, 0, | ||
| 408 | "colorkey_alpha", 0, 0xffffff); | ||
| 409 | priv->colorkey_mode_prop = drm_property_create_enum(dev, 0, | ||
| 410 | "colorkey_mode", | ||
| 411 | armada_drm_colorkey_enum_list, | ||
| 412 | ARRAY_SIZE(armada_drm_colorkey_enum_list)); | ||
| 413 | priv->brightness_prop = drm_property_create_range(dev, 0, | ||
| 414 | "brightness", 0, 256 + 255); | ||
| 415 | priv->contrast_prop = drm_property_create_range(dev, 0, | ||
| 416 | "contrast", 0, 0x7fff); | ||
| 417 | priv->saturation_prop = drm_property_create_range(dev, 0, | ||
| 418 | "saturation", 0, 0x7fff); | ||
| 419 | |||
| 420 | if (!priv->colorkey_prop) | ||
| 421 | return -ENOMEM; | ||
| 422 | |||
| 423 | return 0; | ||
| 424 | } | ||
| 425 | |||
| 426 | int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs) | ||
| 427 | { | ||
| 428 | struct armada_private *priv = dev->dev_private; | ||
| 429 | struct drm_mode_object *mobj; | ||
| 430 | struct armada_plane *dplane; | ||
| 431 | int ret; | ||
| 432 | |||
| 433 | ret = armada_overlay_create_properties(dev); | ||
| 434 | if (ret) | ||
| 435 | return ret; | ||
| 436 | |||
| 437 | dplane = kzalloc(sizeof(*dplane), GFP_KERNEL); | ||
| 438 | if (!dplane) | ||
| 439 | return -ENOMEM; | ||
| 440 | |||
| 441 | spin_lock_init(&dplane->lock); | ||
| 442 | init_waitqueue_head(&dplane->vbl.wait); | ||
| 443 | armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl, | ||
| 444 | dplane); | ||
| 445 | |||
| 446 | drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs, | ||
| 447 | armada_formats, ARRAY_SIZE(armada_formats), false); | ||
| 448 | |||
| 449 | dplane->prop.colorkey_yr = 0xfefefe00; | ||
| 450 | dplane->prop.colorkey_ug = 0x01010100; | ||
| 451 | dplane->prop.colorkey_vb = 0x01010100; | ||
| 452 | dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB); | ||
| 453 | dplane->prop.brightness = 0; | ||
| 454 | dplane->prop.contrast = 0x4000; | ||
| 455 | dplane->prop.saturation = 0x4000; | ||
| 456 | |||
| 457 | mobj = &dplane->base.base; | ||
| 458 | drm_object_attach_property(mobj, priv->colorkey_prop, | ||
| 459 | 0x0101fe); | ||
| 460 | drm_object_attach_property(mobj, priv->colorkey_min_prop, | ||
| 461 | 0x0101fe); | ||
| 462 | drm_object_attach_property(mobj, priv->colorkey_max_prop, | ||
| 463 | 0x0101fe); | ||
| 464 | drm_object_attach_property(mobj, priv->colorkey_val_prop, | ||
| 465 | 0x0101fe); | ||
| 466 | drm_object_attach_property(mobj, priv->colorkey_alpha_prop, | ||
| 467 | 0x000000); | ||
| 468 | drm_object_attach_property(mobj, priv->colorkey_mode_prop, | ||
| 469 | CKMODE_RGB); | ||
| 470 | drm_object_attach_property(mobj, priv->brightness_prop, 256); | ||
| 471 | drm_object_attach_property(mobj, priv->contrast_prop, | ||
| 472 | dplane->prop.contrast); | ||
| 473 | drm_object_attach_property(mobj, priv->saturation_prop, | ||
| 474 | dplane->prop.saturation); | ||
| 475 | |||
| 476 | return 0; | ||
| 477 | } | ||
diff --git a/drivers/gpu/drm/armada/armada_slave.c b/drivers/gpu/drm/armada/armada_slave.c new file mode 100644 index 000000000000..00d0facb42f3 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_slave.c | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * Rewritten from the dovefb driver, and Armada510 manuals. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #include <drm/drmP.h> | ||
| 10 | #include <drm/drm_crtc_helper.h> | ||
| 11 | #include <drm/drm_edid.h> | ||
| 12 | #include <drm/drm_encoder_slave.h> | ||
| 13 | #include "armada_drm.h" | ||
| 14 | #include "armada_output.h" | ||
| 15 | #include "armada_slave.h" | ||
| 16 | |||
| 17 | static int armada_drm_slave_get_modes(struct drm_connector *conn) | ||
| 18 | { | ||
| 19 | struct drm_encoder *enc = armada_drm_connector_encoder(conn); | ||
| 20 | int count = 0; | ||
| 21 | |||
| 22 | if (enc) { | ||
| 23 | struct drm_encoder_slave *slave = to_encoder_slave(enc); | ||
| 24 | |||
| 25 | count = slave->slave_funcs->get_modes(enc, conn); | ||
| 26 | } | ||
| 27 | |||
| 28 | return count; | ||
| 29 | } | ||
| 30 | |||
| 31 | static void armada_drm_slave_destroy(struct drm_encoder *enc) | ||
| 32 | { | ||
| 33 | struct drm_encoder_slave *slave = to_encoder_slave(enc); | ||
| 34 | struct i2c_client *client = drm_i2c_encoder_get_client(enc); | ||
| 35 | |||
| 36 | if (slave->slave_funcs) | ||
| 37 | slave->slave_funcs->destroy(enc); | ||
| 38 | if (client) | ||
| 39 | i2c_put_adapter(client->adapter); | ||
| 40 | |||
| 41 | drm_encoder_cleanup(&slave->base); | ||
| 42 | kfree(slave); | ||
| 43 | } | ||
| 44 | |||
| 45 | static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = { | ||
| 46 | .destroy = armada_drm_slave_destroy, | ||
| 47 | }; | ||
| 48 | |||
| 49 | static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = { | ||
| 50 | .get_modes = armada_drm_slave_get_modes, | ||
| 51 | .mode_valid = armada_drm_slave_encoder_mode_valid, | ||
| 52 | .best_encoder = armada_drm_connector_encoder, | ||
| 53 | }; | ||
| 54 | |||
| 55 | static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = { | ||
| 56 | .dpms = drm_i2c_encoder_dpms, | ||
| 57 | .save = drm_i2c_encoder_save, | ||
| 58 | .restore = drm_i2c_encoder_restore, | ||
| 59 | .mode_fixup = drm_i2c_encoder_mode_fixup, | ||
| 60 | .prepare = drm_i2c_encoder_prepare, | ||
| 61 | .commit = drm_i2c_encoder_commit, | ||
| 62 | .mode_set = drm_i2c_encoder_mode_set, | ||
| 63 | .detect = drm_i2c_encoder_detect, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static int | ||
| 67 | armada_drm_conn_slave_create(struct drm_connector *conn, const void *data) | ||
| 68 | { | ||
| 69 | const struct armada_drm_slave_config *config = data; | ||
| 70 | struct drm_encoder_slave *slave; | ||
| 71 | struct i2c_adapter *adap; | ||
| 72 | int ret; | ||
| 73 | |||
| 74 | conn->interlace_allowed = config->interlace_allowed; | ||
| 75 | conn->doublescan_allowed = config->doublescan_allowed; | ||
| 76 | conn->polled = config->polled; | ||
| 77 | |||
| 78 | drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs); | ||
| 79 | |||
| 80 | slave = kzalloc(sizeof(*slave), GFP_KERNEL); | ||
| 81 | if (!slave) | ||
| 82 | return -ENOMEM; | ||
| 83 | |||
| 84 | slave->base.possible_crtcs = config->crtcs; | ||
| 85 | |||
| 86 | adap = i2c_get_adapter(config->i2c_adapter_id); | ||
| 87 | if (!adap) { | ||
| 88 | kfree(slave); | ||
| 89 | return -EPROBE_DEFER; | ||
| 90 | } | ||
| 91 | |||
| 92 | ret = drm_encoder_init(conn->dev, &slave->base, | ||
| 93 | &armada_drm_slave_encoder_funcs, | ||
| 94 | DRM_MODE_ENCODER_TMDS); | ||
| 95 | if (ret) { | ||
| 96 | DRM_ERROR("unable to init encoder\n"); | ||
| 97 | i2c_put_adapter(adap); | ||
| 98 | kfree(slave); | ||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | |||
| 102 | ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info); | ||
| 103 | i2c_put_adapter(adap); | ||
| 104 | if (ret) { | ||
| 105 | DRM_ERROR("unable to init encoder slave\n"); | ||
| 106 | armada_drm_slave_destroy(&slave->base); | ||
| 107 | return ret; | ||
| 108 | } | ||
| 109 | |||
| 110 | drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers); | ||
| 111 | |||
| 112 | ret = slave->slave_funcs->create_resources(&slave->base, conn); | ||
| 113 | if (ret) { | ||
| 114 | armada_drm_slave_destroy(&slave->base); | ||
| 115 | return ret; | ||
| 116 | } | ||
| 117 | |||
| 118 | ret = drm_mode_connector_attach_encoder(conn, &slave->base); | ||
| 119 | if (ret) { | ||
| 120 | armada_drm_slave_destroy(&slave->base); | ||
| 121 | return ret; | ||
| 122 | } | ||
| 123 | |||
| 124 | conn->encoder = &slave->base; | ||
| 125 | |||
| 126 | return ret; | ||
| 127 | } | ||
| 128 | |||
| 129 | static const struct armada_output_type armada_drm_conn_slave = { | ||
| 130 | .connector_type = DRM_MODE_CONNECTOR_HDMIA, | ||
| 131 | .create = armada_drm_conn_slave_create, | ||
| 132 | .set_property = armada_drm_slave_encoder_set_property, | ||
| 133 | }; | ||
| 134 | |||
| 135 | int armada_drm_connector_slave_create(struct drm_device *dev, | ||
| 136 | const struct armada_drm_slave_config *config) | ||
| 137 | { | ||
| 138 | return armada_output_create(dev, &armada_drm_conn_slave, config); | ||
| 139 | } | ||
diff --git a/drivers/gpu/drm/armada/armada_slave.h b/drivers/gpu/drm/armada/armada_slave.h new file mode 100644 index 000000000000..bf2374c96fc1 --- /dev/null +++ b/drivers/gpu/drm/armada/armada_slave.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | */ | ||
| 8 | #ifndef ARMADA_SLAVE_H | ||
| 9 | #define ARMADA_SLAVE_H | ||
| 10 | |||
| 11 | #include <linux/i2c.h> | ||
| 12 | #include <drm/drmP.h> | ||
| 13 | |||
| 14 | struct armada_drm_slave_config { | ||
| 15 | int i2c_adapter_id; | ||
| 16 | uint32_t crtcs; | ||
| 17 | uint8_t polled; | ||
| 18 | bool interlace_allowed; | ||
| 19 | bool doublescan_allowed; | ||
| 20 | struct i2c_board_info info; | ||
| 21 | }; | ||
| 22 | |||
| 23 | int armada_drm_connector_slave_create(struct drm_device *dev, | ||
| 24 | const struct armada_drm_slave_config *); | ||
| 25 | |||
| 26 | #endif | ||
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 24f499569a2f..e5e6e9124cbc 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
| @@ -1135,4 +1135,21 @@ extern int drm_format_horz_chroma_subsampling(uint32_t format); | |||
| 1135 | extern int drm_format_vert_chroma_subsampling(uint32_t format); | 1135 | extern int drm_format_vert_chroma_subsampling(uint32_t format); |
| 1136 | extern const char *drm_get_format_name(uint32_t format); | 1136 | extern const char *drm_get_format_name(uint32_t format); |
| 1137 | 1137 | ||
| 1138 | /* Helpers */ | ||
| 1139 | static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, | ||
| 1140 | uint32_t id) | ||
| 1141 | { | ||
| 1142 | struct drm_mode_object *mo; | ||
| 1143 | mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CRTC); | ||
| 1144 | return mo ? obj_to_crtc(mo) : NULL; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, | ||
| 1148 | uint32_t id) | ||
| 1149 | { | ||
| 1150 | struct drm_mode_object *mo; | ||
| 1151 | mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); | ||
| 1152 | return mo ? obj_to_encoder(mo) : NULL; | ||
| 1153 | } | ||
| 1154 | |||
| 1138 | #endif /* __DRM_CRTC_H__ */ | 1155 | #endif /* __DRM_CRTC_H__ */ |
diff --git a/include/uapi/drm/armada_drm.h b/include/uapi/drm/armada_drm.h new file mode 100644 index 000000000000..8dec3fdc99c7 --- /dev/null +++ b/include/uapi/drm/armada_drm.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2012 Russell King | ||
| 3 | * With inspiration from the i915 driver | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 as | ||
| 7 | * published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | #ifndef DRM_ARMADA_IOCTL_H | ||
| 10 | #define DRM_ARMADA_IOCTL_H | ||
| 11 | |||
| 12 | #define DRM_ARMADA_GEM_CREATE 0x00 | ||
| 13 | #define DRM_ARMADA_GEM_MMAP 0x02 | ||
| 14 | #define DRM_ARMADA_GEM_PWRITE 0x03 | ||
| 15 | |||
| 16 | #define ARMADA_IOCTL(dir, name, str) \ | ||
| 17 | DRM_##dir(DRM_COMMAND_BASE + DRM_ARMADA_##name, struct drm_armada_##str) | ||
| 18 | |||
| 19 | struct drm_armada_gem_create { | ||
| 20 | uint32_t handle; | ||
| 21 | uint32_t size; | ||
| 22 | }; | ||
| 23 | #define DRM_IOCTL_ARMADA_GEM_CREATE \ | ||
| 24 | ARMADA_IOCTL(IOWR, GEM_CREATE, gem_create) | ||
| 25 | |||
| 26 | struct drm_armada_gem_mmap { | ||
| 27 | uint32_t handle; | ||
| 28 | uint32_t pad; | ||
| 29 | uint64_t offset; | ||
| 30 | uint64_t size; | ||
| 31 | uint64_t addr; | ||
| 32 | }; | ||
| 33 | #define DRM_IOCTL_ARMADA_GEM_MMAP \ | ||
| 34 | ARMADA_IOCTL(IOWR, GEM_MMAP, gem_mmap) | ||
| 35 | |||
| 36 | struct drm_armada_gem_pwrite { | ||
| 37 | uint64_t ptr; | ||
| 38 | uint32_t handle; | ||
| 39 | uint32_t offset; | ||
| 40 | uint32_t size; | ||
| 41 | }; | ||
| 42 | #define DRM_IOCTL_ARMADA_GEM_PWRITE \ | ||
| 43 | ARMADA_IOCTL(IOW, GEM_PWRITE, gem_pwrite) | ||
| 44 | |||
| 45 | #endif | ||
