Hello,
The v2 incorporates the feedback on v1 [1] and rebases the series to master. We no longer force 32bpp, fix incorrect error handling during plane initialization, and drom some irrelevant patches.
[1] https://lists.freedesktop.org/archives/dri-devel/2017-August/150703.html
Dominik Behr (1): drm/cirrus: initialize start and size fields
Varad Gautam (9): drm/cirrus: split out bo unpinning from cirrus_bo_push_sysram drm/cirrus: add drm_read to cirrus_driver_fops drm/cirrus: do not disable outputs on fbdev init for atomic. drm/cirrus: use universal plane interfaces for primary plane drm/cirrus: use atomic transition helpers for plane and crtc drm/cirrus: send vblank on crtc atomic_flush drm/cirrus: use atomic handlers for plane and crtc drm/cirrus: implement atomic hardware cursor support drm/cirrus: advertise DRIVER_ATOMIC
Zach Reizner (2): drm/cirrus: hardcode vram size drm/cirrus: implement PRIME export for cirrus
drivers/gpu/drm/cirrus/Makefile | 2 +- drivers/gpu/drm/cirrus/cirrus_drv.c | 13 +- drivers/gpu/drm/cirrus/cirrus_drv.h | 25 ++ drivers/gpu/drm/cirrus/cirrus_fbdev.c | 6 +- drivers/gpu/drm/cirrus/cirrus_main.c | 23 +- drivers/gpu/drm/cirrus/cirrus_mode.c | 682 ++++++++++++++++++++++++++-------- drivers/gpu/drm/cirrus/cirrus_prime.c | 63 ++++ drivers/gpu/drm/cirrus/cirrus_ttm.c | 55 ++- 8 files changed, 705 insertions(+), 164 deletions(-) create mode 100644 drivers/gpu/drm/cirrus/cirrus_prime.c
Thanks, Varad
From: Varad Gautam varad.gautam@collabora.com
add a cirrus_bo_unpin call, and move bo_{reserve,unreserve} operations to bo_{pin,unpin} to ensure correct pinning/unpinning and simplify the call sequence.
Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/cirrus_drv.h | 1 + drivers/gpu/drm/cirrus/cirrus_mode.c | 14 ++------- drivers/gpu/drm/cirrus/cirrus_ttm.c | 55 +++++++++++++++++++++++++++++------- 3 files changed, 48 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index be2d7e488062..e16b86dd6c19 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -254,6 +254,7 @@ static inline void cirrus_bo_unreserve(struct cirrus_bo *bo)
int cirrus_bo_push_sysram(struct cirrus_bo *bo); int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr); +int cirrus_bo_unpin(struct cirrus_bo *bo);
extern int cirrus_bpp;
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index a4c4a465b385..6a8d09c9a36b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -112,26 +112,17 @@ static int cirrus_crtc_do_set_base(struct drm_crtc *crtc, cirrus_fb = to_cirrus_framebuffer(fb); obj = cirrus_fb->obj; bo = gem_to_cirrus_bo(obj); - ret = cirrus_bo_reserve(bo, false); - if (ret) - return ret; + cirrus_bo_unpin(bo); cirrus_bo_push_sysram(bo); - cirrus_bo_unreserve(bo); }
cirrus_fb = to_cirrus_framebuffer(crtc->primary->fb); obj = cirrus_fb->obj; bo = gem_to_cirrus_bo(obj);
- ret = cirrus_bo_reserve(bo, false); - if (ret) - return ret; - ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); - if (ret) { - cirrus_bo_unreserve(bo); + if (ret) return ret; - }
if (&cdev->mode_info.gfbdev->gfb == cirrus_fb) { /* if pushing console in kmap it */ @@ -139,7 +130,6 @@ static int cirrus_crtc_do_set_base(struct drm_crtc *crtc, if (ret) DRM_ERROR("failed to kmap fbcon\n"); } - cirrus_bo_unreserve(bo);
cirrus_set_start_address(crtc, (u32)gpu_addr); return 0; diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index 1ff1838c0d44..a91d31da90ba 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -358,12 +358,17 @@ static inline u64 cirrus_bo_gpu_offset(struct cirrus_bo *bo)
int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr) { - int i, ret; + int i, ret = 0; + + ret = cirrus_bo_reserve(bo, false); + if (ret) + return ret;
if (bo->pin_count) { bo->pin_count++; if (gpu_addr) *gpu_addr = cirrus_bo_gpu_offset(bo); + goto out; }
cirrus_ttm_placement(bo, pl_flag); @@ -371,24 +376,51 @@ int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr) bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) - return ret; + goto out;
bo->pin_count = 1; if (gpu_addr) *gpu_addr = cirrus_bo_gpu_offset(bo); - return 0; + +out: + cirrus_bo_unreserve(bo); + return ret; +} + +int cirrus_bo_unpin(struct cirrus_bo *bo) +{ + int i, ret = 0; + + ret = cirrus_bo_reserve(bo, false); + if (ret) + return ret; + + if (!bo->pin_count || --bo->pin_count) + goto out; + + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); + if (ret) + goto out; + +out: + cirrus_bo_unreserve(bo); + return ret; }
int cirrus_bo_push_sysram(struct cirrus_bo *bo) { int i, ret; - if (!bo->pin_count) { + + ret = cirrus_bo_reserve(bo, false); + if (ret) + return ret; + + if (bo->pin_count) { DRM_ERROR("unpin bad %p\n", bo); - return 0; + goto out; } - bo->pin_count--; - if (bo->pin_count) - return 0;
if (bo->kmap.virtual) ttm_bo_kunmap(&bo->kmap); @@ -400,9 +432,12 @@ int cirrus_bo_push_sysram(struct cirrus_bo *bo) ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); if (ret) { DRM_ERROR("pushing to VRAM failed\n"); - return ret; + goto out; } - return 0; + +out: + cirrus_bo_unreserve(bo); + return ret; }
int cirrus_mmap(struct file *filp, struct vm_area_struct *vma)
From: Varad Gautam varad.gautam@collabora.com
allow reading the drm file from userspace.
Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/cirrus_drv.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 69c4e352dd78..452059780ddc 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -122,6 +122,7 @@ static int cirrus_pm_resume(struct device *dev) static const struct file_operations cirrus_driver_fops = { .owner = THIS_MODULE, .open = drm_open, + .read = drm_read, .release = drm_release, .unlocked_ioctl = drm_ioctl, .mmap = cirrus_mmap,
From: Varad Gautam varad.gautam@collabora.com
drm_helper_disable_unused_functions should not be called by atomic drivers, so disable it for later patches.
Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/cirrus_fbdev.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 32fbfba2c623..28383b828f47 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -293,9 +293,6 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) if (ret) return ret;
- /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(cdev->dev); - return drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); }
From: Dominik Behr dbehr@chromium.org
initialize start and size fields in fb info so user space drivers like fbdev can map the memory
cherry-pick from 3.14 to 3.18 kernel to let VMtest pass
dmesg now shows proper size and fb start
initially reviewed for chromiumos at: https://chromium-review.googlesource.com/167396 https://chromium-review.googlesource.com/282933 https://chromium-review.googlesource.com/339092
Signed-off-by: Dominik Behr dbehr@chromium.org Signed-off-by: Zhuo-hao Lee zhuo-hao.lee@intel.com Signed-off-by: Stphane Marchesin marcheu@chromium.org --- drivers/gpu/drm/cirrus/cirrus_fbdev.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 28383b828f47..30fb10cbb038 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -231,6 +231,9 @@ static int cirrusfb_create(struct drm_fb_helper *helper, info->screen_base = sysram; info->screen_size = size;
+ info->fix.smem_start = cdev->dev->mode_config.fb_base; + info->fix.smem_len = size; + info->fix.mmio_start = 0; info->fix.mmio_len = 0;
On Fri, 2017-09-08 at 19:05 +0530, Varad Gautam wrote:
From: Dominik Behr dbehr@chromium.org
initialize start and size fields in fb info so user space drivers like fbdev can map the memory
Hmm? That works just fine in vanilla 4.12 ...
cheers, Gerd
From: Zach Reizner zachr@google.com
There is no reliable way of detecting actual VRAM size, which is important in the case of cirrus because cursor data is always stored in the last 16K of VRAM. Because qemu effectivaly hardcodes 4MB but reports 32MB, we hardcode 4MB in the cirrus driver to ensure the cursor works properly.
initially reviewed at: https://chromium-review.googlesource.com/411344 Signed-off-by: Zach Reizner zachr@chromium.org CC: Stéphane Marchesin marcheu@chromium.org --- drivers/gpu/drm/cirrus/cirrus_main.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index b5f528543956..332bb2169508 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -98,7 +98,12 @@ static int cirrus_vram_init(struct cirrus_device *cdev) { /* BAR 0 is VRAM */ cdev->mc.vram_base = pci_resource_start(cdev->dev->pdev, 0); - cdev->mc.vram_size = pci_resource_len(cdev->dev->pdev, 0); + + /* + * While we can use the entire PCI bar for VRAM, qemu always expects to + * find the cursor data at the 4M - 16K point. + */ + cdev->mc.vram_size = 4 * 1024 * 1024;
if (!request_mem_region(cdev->mc.vram_base, cdev->mc.vram_size, "cirrusdrmfb_vram")) {
On Fri, 2017-09-08 at 19:05 +0530, Varad Gautam wrote:
From: Zach Reizner zachr@google.com
There is no reliable way of detecting actual VRAM size, which is important in the case of cirrus because cursor data is always stored in the last 16K of VRAM. Because qemu effectivaly hardcodes 4MB but reports 32MB, we hardcode 4MB in the cirrus driver to ensure the cursor works properly.
This comment is misleading. pci bar size != vram size. There is more stuff in the pci bar than just the vram, thats why it is larger than vram. It's not qemu mis-reporting something.
The actual vram size is 4MB. This is what the original hardware had, so the qemu emulation uses that too. So the fix is correct, but please adjust commit message and comment.
cheers, Gerd
From: Zach Reizner zachr@chromium.org
This patch implements PRIME export, but not import for cirrus.
initially reviewed at: https://chromium-review.googlesource.com/229688 https://chromium-review.googlesource.com/339057
Signed-off-by: Zach Reizner zachr@chromium.org Signed-off-by: Stphane Marchesin marcheu@chromium.org Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/Makefile | 2 +- drivers/gpu/drm/cirrus/cirrus_drv.c | 11 +++++- drivers/gpu/drm/cirrus/cirrus_drv.h | 10 ++++++ drivers/gpu/drm/cirrus/cirrus_prime.c | 63 +++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/cirrus/cirrus_prime.c
diff --git a/drivers/gpu/drm/cirrus/Makefile b/drivers/gpu/drm/cirrus/Makefile index 919c0a336c97..f6bcc21454c6 100644 --- a/drivers/gpu/drm/cirrus/Makefile +++ b/drivers/gpu/drm/cirrus/Makefile @@ -1,4 +1,4 @@ cirrus-y := cirrus_main.o cirrus_mode.o \ - cirrus_drv.o cirrus_fbdev.o cirrus_ttm.o + cirrus_drv.o cirrus_fbdev.o cirrus_ttm.o cirrus_prime.o
obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 452059780ddc..09461868ed46 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -130,7 +130,7 @@ static const struct file_operations cirrus_driver_fops = { .compat_ioctl = drm_compat_ioctl, }; static struct drm_driver driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, .load = cirrus_driver_load, .unload = cirrus_driver_unload, .fops = &cirrus_driver_fops, @@ -143,6 +143,15 @@ static struct drm_driver driver = { .gem_free_object_unlocked = cirrus_gem_free_object, .dumb_create = cirrus_dumb_create, .dumb_map_offset = cirrus_dumb_mmap_offset, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = cirrus_gem_prime_pin, + .gem_prime_get_sg_table = cirrus_gem_prime_get_sg_table, + .gem_prime_import_sg_table = cirrus_gem_prime_import_sg_table, + .gem_prime_vmap = cirrus_gem_prime_vmap, + .gem_prime_vunmap = cirrus_gem_prime_vunmap, };
static const struct dev_pm_ops cirrus_pm_ops = { diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index e16b86dd6c19..8c43cc963027 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -163,6 +163,7 @@ struct cirrus_bo { struct ttm_buffer_object bo; struct ttm_placement placement; struct ttm_bo_kmap_obj kmap; + struct ttm_bo_kmap_obj dma_buf_vmap; struct drm_gem_object gem; struct ttm_place placements[3]; int pin_count; @@ -256,6 +257,15 @@ int cirrus_bo_push_sysram(struct cirrus_bo *bo); int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr); int cirrus_bo_unpin(struct cirrus_bo *bo);
+struct sg_table *cirrus_gem_prime_get_sg_table(struct drm_gem_object *obj); +void *cirrus_gem_prime_vmap(struct drm_gem_object *obj); +void cirrus_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); +struct drm_gem_object *cirrus_gem_prime_import_sg_table( + struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt); +int cirrus_gem_prime_pin(struct drm_gem_object *obj); + extern int cirrus_bpp;
#endif /* __CIRRUS_DRV_H__ */ diff --git a/drivers/gpu/drm/cirrus/cirrus_prime.c b/drivers/gpu/drm/cirrus/cirrus_prime.c new file mode 100644 index 000000000000..a7fe6c73bfb6 --- /dev/null +++ b/drivers/gpu/drm/cirrus/cirrus_prime.c @@ -0,0 +1,63 @@ +/* + * Copyright © 2014 The Chromium OS Authors + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + */ + +#include <drm/ttm/ttm_page_alloc.h> +#include <drm/drmP.h> +#include "cirrus_drv.h" + +struct sg_table *cirrus_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ + struct cirrus_bo *cirrusbo = gem_to_cirrus_bo(obj); + unsigned long npages = cirrusbo->bo.num_pages; + + return drm_prime_pages_to_sg(cirrusbo->bo.ttm->pages, npages); +} + +void *cirrus_gem_prime_vmap(struct drm_gem_object *obj) +{ + struct cirrus_bo *cirrusbo = gem_to_cirrus_bo(obj); + int ret; + + ret = ttm_bo_kmap(&cirrusbo->bo, 0, cirrusbo->bo.num_pages, + &cirrusbo->dma_buf_vmap); + if (ret) + return ERR_PTR(ret); + + return cirrusbo->dma_buf_vmap.virtual; +} + +void cirrus_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ + struct cirrus_bo *cirrusbo = gem_to_cirrus_bo(obj); + + ttm_bo_kunmap(&cirrusbo->dma_buf_vmap); +} + +struct drm_gem_object *cirrus_gem_prime_import_sg_table( + struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt) +{ + return NULL; +} + +int cirrus_gem_prime_pin(struct drm_gem_object *obj) +{ + struct cirrus_bo *cirrusbo = gem_to_cirrus_bo(obj); + int ret = 0; + + ret = cirrus_bo_pin(cirrusbo, TTM_PL_FLAG_SYSTEM, NULL); + if (ret) + goto out; + + ttm_pool_populate(cirrusbo->bo.ttm); + +out: + return ret; +}
From: Varad Gautam varad.gautam@collabora.com
cirrus exposes one legacy primary plane tied to the crtc. convert this to use the universal planes interface in preparation for atomic.
v2: avoid early plane cleanup to fix faulty error handling. (krisman)
Signed-off-by: Varad Gautam varad.gautam@collabora.com Reviewed-by: Gabriel Krisman Bertazi krisman@collabora.co.uk --- drivers/gpu/drm/cirrus/cirrus_mode.c | 45 +++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 6a8d09c9a36b..d12399986c81 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -348,11 +348,29 @@ static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { .commit = cirrus_crtc_commit, };
+static const uint32_t cirrus_plane_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB565, +}; + +static const struct drm_plane_funcs cirrus_plane_funcs = { + .update_plane = drm_primary_helper_update, + .disable_plane = drm_primary_helper_disable, + .destroy = drm_primary_helper_destroy, +}; + +static const struct drm_plane_helper_funcs cirrus_plane_helper_funcs = { +}; + /* CRTC setup */ static void cirrus_crtc_init(struct drm_device *dev) { struct cirrus_device *cdev = dev->dev_private; struct cirrus_crtc *cirrus_crtc; + struct drm_plane *primary; + int ret;
cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) + (CIRRUSFB_CONN_LIMIT * sizeof(struct drm_connector *)), @@ -361,12 +379,37 @@ static void cirrus_crtc_init(struct drm_device *dev) if (cirrus_crtc == NULL) return;
- drm_crtc_init(dev, &cirrus_crtc->base, &cirrus_crtc_funcs); + primary = kzalloc(sizeof(*primary), GFP_KERNEL); + if (primary == NULL) + goto cleanup_crtc;
+ drm_plane_helper_add(primary, &cirrus_plane_helper_funcs); + ret = drm_universal_plane_init(dev, primary, 1, + &cirrus_plane_funcs, + cirrus_plane_formats, + ARRAY_SIZE(cirrus_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + kfree(primary); + goto cleanup_crtc; + } + + ret = drm_crtc_init_with_planes(dev, &cirrus_crtc->base, primary, NULL, + &cirrus_crtc_funcs, NULL); + if (ret) + goto cleanup; drm_mode_crtc_set_gamma_size(&cirrus_crtc->base, CIRRUS_LUT_SIZE); cdev->mode_info.crtc = cirrus_crtc;
drm_crtc_helper_add(&cirrus_crtc->base, &cirrus_helper_funcs); + return; + +cleanup: + drm_plane_cleanup(primary); + kfree(primary); +cleanup_crtc: + kfree(cirrus_crtc); + return; }
static void cirrus_encoder_mode_set(struct drm_encoder *encoder,
From: Varad Gautam varad.gautam@collabora.com
split the driver to fit into atomic semantics, and switch to using the atomic transition layer helpers for legacy modesetting.
Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/cirrus_drv.h | 1 + drivers/gpu/drm/cirrus/cirrus_main.c | 3 + drivers/gpu/drm/cirrus/cirrus_mode.c | 350 ++++++++++++++++++++++------------- 3 files changed, 227 insertions(+), 127 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 8c43cc963027..1db0849b4bcb 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -167,6 +167,7 @@ struct cirrus_bo { struct drm_gem_object gem; struct ttm_place placements[3]; int pin_count; + u64 gpu_addr; }; #define gem_to_cirrus_bo(gobj) container_of((gobj), struct cirrus_bo, gem)
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 332bb2169508..f146a4129742 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -9,6 +9,7 @@ * Dave Airlie */ #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h>
#include "cirrus_drv.h" @@ -82,6 +83,8 @@ cirrus_user_framebuffer_create(struct drm_device *dev,
static const struct drm_mode_config_funcs cirrus_mode_funcs = { .fb_create = cirrus_user_framebuffer_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, };
/* Unmap the framebuffer from the core and release the memory */ diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index d12399986c81..ee023c865165 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -15,6 +15,8 @@ * Copyright 1999-2001 Jeff Garzik jgarzik@pobox.com */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_plane_helper.h>
@@ -72,95 +74,20 @@ static void cirrus_crtc_dpms(struct drm_crtc *crtc, int mode) WREG_GFX(0xe, gr0e); }
-static void cirrus_set_start_address(struct drm_crtc *crtc, unsigned offset) -{ - struct cirrus_device *cdev = crtc->dev->dev_private; - u32 addr; - u8 tmp; - - addr = offset >> 2; - WREG_CRT(0x0c, (u8)((addr >> 8) & 0xff)); - WREG_CRT(0x0d, (u8)(addr & 0xff)); - - WREG8(CRT_INDEX, 0x1b); - tmp = RREG8(CRT_DATA); - tmp &= 0xf2; - tmp |= (addr >> 16) & 0x01; - tmp |= (addr >> 15) & 0x0c; - WREG_CRT(0x1b, tmp); - WREG8(CRT_INDEX, 0x1d); - tmp = RREG8(CRT_DATA); - tmp &= 0x7f; - tmp |= (addr >> 12) & 0x80; - WREG_CRT(0x1d, tmp); -} - -/* cirrus is different - we will force move buffers out of VRAM */ -static int cirrus_crtc_do_set_base(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int x, int y, int atomic) -{ - struct cirrus_device *cdev = crtc->dev->dev_private; - struct drm_gem_object *obj; - struct cirrus_framebuffer *cirrus_fb; - struct cirrus_bo *bo; - int ret; - u64 gpu_addr; - - /* push the previous fb to system ram */ - if (!atomic && fb) { - cirrus_fb = to_cirrus_framebuffer(fb); - obj = cirrus_fb->obj; - bo = gem_to_cirrus_bo(obj); - cirrus_bo_unpin(bo); - cirrus_bo_push_sysram(bo); - } - - cirrus_fb = to_cirrus_framebuffer(crtc->primary->fb); - obj = cirrus_fb->obj; - bo = gem_to_cirrus_bo(obj); - - ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); - if (ret) - return ret; - - if (&cdev->mode_info.gfbdev->gfb == cirrus_fb) { - /* if pushing console in kmap it */ - ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); - if (ret) - DRM_ERROR("failed to kmap fbcon\n"); - } - - cirrus_set_start_address(crtc, (u32)gpu_addr); - return 0; -} - -static int cirrus_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - return cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0); -} - /* - * The meat of this driver. The core passes us a mode and we have to program - * it. The modesetting here is the bare minimum required to satisfy the qemu - * emulation of this hardware, and running this against a real device is - * likely to result in an inadequately programmed mode. We've already had - * the opportunity to modify the mode, so whatever we receive here should - * be something that can be correctly programmed and displayed + * The core passes us a mode and we have to program it. The modesetting here + * is the bare minimum required to satisfy the qemu emulation of this + * hardware, and running this against a real device is likely to result in + * an inadequately programmed mode. */ -static int cirrus_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, struct drm_framebuffer *old_fb) +static void cirrus_mode_set_nofb(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct cirrus_device *cdev = dev->dev_private; - const struct drm_framebuffer *fb = crtc->primary->fb; + struct drm_display_mode *mode = &crtc->mode; int hsyncstart, hsyncend, htotal, hdispend; int vtotal, vdispend; int tmp; - int sr07 = 0, hdr = 0;
htotal = mode->htotal / 8; hsyncend = mode->hsync_end / 8; @@ -225,54 +152,11 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc, /* Disable Hercules/CGA compatibility */ WREG_CRT(VGA_CRTC_MODE, 0x03);
- WREG8(SEQ_INDEX, 0x7); - sr07 = RREG8(SEQ_DATA); - sr07 &= 0xe0; - hdr = 0; - switch (fb->format->cpp[0] * 8) { - case 8: - sr07 |= 0x11; - break; - case 16: - sr07 |= 0x17; - hdr = 0xc1; - break; - case 24: - sr07 |= 0x15; - hdr = 0xc5; - break; - case 32: - sr07 |= 0x19; - hdr = 0xc5; - break; - default: - return -1; - } - - WREG_SEQ(0x7, sr07); - - /* Program the pitch */ - tmp = fb->pitches[0] / 8; - WREG_CRT(VGA_CRTC_OFFSET, tmp); - - /* Enable extended blanking and pitch bits, and enable full memory */ - tmp = 0x22; - tmp |= (fb->pitches[0] >> 7) & 0x10; - tmp |= (fb->pitches[0] >> 6) & 0x40; - WREG_CRT(0x1b, tmp); - /* Enable high-colour modes */ WREG_GFX(VGA_GFX_MODE, 0x40);
/* And set graphics mode */ WREG_GFX(VGA_GFX_MISC, 0x01); - - WREG_HDR(hdr); - cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0); - - /* Unblank (needed on S3 resume, vgabios doesn't do it then) */ - outb(0x20, 0x3c0); - return 0; }
/* @@ -338,16 +222,33 @@ static const struct drm_crtc_funcs cirrus_crtc_funcs = { .gamma_set = cirrus_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, .destroy = cirrus_crtc_destroy, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, };
static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { .dpms = cirrus_crtc_dpms, - .mode_set = cirrus_crtc_mode_set, - .mode_set_base = cirrus_crtc_mode_set_base, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_base = drm_helper_crtc_mode_set_base, + .mode_set_nofb = cirrus_mode_set_nofb, .prepare = cirrus_crtc_prepare, .commit = cirrus_crtc_commit, };
+static int cirrus_plane_update(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, + int crtc_y, unsigned int crtc_w, + unsigned int crtc_h, uint32_t src_x, + uint32_t src_y, uint32_t src_w, uint32_t src_h, + struct drm_modeset_acquire_ctx *ctx) +{ + return drm_plane_helper_update(plane, crtc, fb, + crtc_x, crtc_y, crtc_w, + crtc_h, src_x, src_y, src_w, src_h); +} + static const uint32_t cirrus_plane_formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, @@ -356,12 +257,202 @@ static const uint32_t cirrus_plane_formats[] = { };
static const struct drm_plane_funcs cirrus_plane_funcs = { - .update_plane = drm_primary_helper_update, + .update_plane = cirrus_plane_update, .disable_plane = drm_primary_helper_disable, .destroy = drm_primary_helper_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, };
+static int cirrus_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct cirrus_device *cdev = plane->dev->dev_private; + struct drm_gem_object *obj; + struct cirrus_framebuffer *cirrus_fb; + struct cirrus_bo *bo; + int ret; + + if (!new_state->fb) + return 0; + + if (plane->old_fb) { + cirrus_fb = to_cirrus_framebuffer(plane->old_fb); + obj = cirrus_fb->obj; + bo = gem_to_cirrus_bo(obj); + cirrus_bo_push_sysram(bo); + } + + cirrus_fb = to_cirrus_framebuffer(new_state->fb); + obj = cirrus_fb->obj; + bo = gem_to_cirrus_bo(obj); + + ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &bo->gpu_addr); + if (ret) + return ret; + + if (&cdev->mode_info.gfbdev->gfb == cirrus_fb) { + /* if pushing console in kmap it */ + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + if (ret) + return ret; + } + + return 0; +} + +static void cirrus_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_gem_object *obj; + struct cirrus_bo *bo; + + if (!plane->state->fb) { + /* we never executed prepare_fb, so there's nothing to + * unpin. + */ + return; + } + + obj = to_cirrus_framebuffer(plane->state->fb)->obj; + bo = gem_to_cirrus_bo(obj); + + cirrus_bo_unpin(bo); +} + +static int cirrus_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct cirrus_device *cdev = plane->dev->dev_private; + struct drm_framebuffer *fb = state->fb; + struct drm_crtc *crtc = state->crtc ? state->crtc : plane->crtc; + struct drm_crtc_state *crtc_state; + struct drm_rect clip = { 0 }; + int ret; + + if (!crtc || !fb) + return 0; + + if (!cirrus_check_framebuffer(cdev, fb->width, fb->height, + fb->format->cpp[0], fb->pitches[0])) + return -EINVAL; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (!crtc_state) + return -EINVAL; + + clip.x2 = crtc_state->adjusted_mode.hdisplay; + clip.y2 = crtc_state->adjusted_mode.vdisplay; + + ret = drm_plane_helper_check_state(state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); + if (ret) + return ret; + + return 0; +} + +static void cirrus_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + return; +} + +static void cirrus_set_framebuffer_regs(struct drm_plane *plane) +{ + struct cirrus_device *cdev = plane->dev->dev_private; + struct drm_framebuffer *fb = plane->state->fb; + int sr07 = 0, hdr = 0, tmp; + + WREG8(SEQ_INDEX, 0x7); + sr07 = RREG8(SEQ_DATA); + sr07 &= 0xe0; + switch (fb->format->cpp[0] * 8) { + case 8: + sr07 |= 0x11; + break; + case 16: + sr07 |= 0x17; + hdr = 0xc1; + break; + case 24: + sr07 |= 0x15; + hdr = 0xc5; + break; + case 32: + sr07 |= 0x19; + hdr = 0xc5; + break; + default: + /* Should never reach here. */ + break; + } + + WREG_SEQ(0x7, sr07); + + /* Program the pitch */ + tmp = fb->pitches[0] / 8; + WREG_CRT(VGA_CRTC_OFFSET, tmp); + + /* Enable extended blanking and pitch bits, and enable full memory */ + tmp = 0x22; + tmp |= (fb->pitches[0] >> 7) & 0x10; + tmp |= (fb->pitches[0] >> 6) & 0x40; + WREG_CRT(0x1b, tmp); + + WREG_HDR(hdr); +} + +static void cirrus_set_start_address(struct drm_crtc *crtc, unsigned offset) +{ + struct cirrus_device *cdev = crtc->dev->dev_private; + u32 addr; + u8 tmp; + + addr = offset >> 2; + WREG_CRT(0x0c, (u8)((addr >> 8) & 0xff)); + WREG_CRT(0x0d, (u8)(addr & 0xff)); + + WREG8(CRT_INDEX, 0x1b); + tmp = RREG8(CRT_DATA); + tmp &= 0xf2; + tmp |= (addr >> 16) & 0x01; + tmp |= (addr >> 15) & 0x0c; + WREG_CRT(0x1b, tmp); + WREG8(CRT_INDEX, 0x1d); + tmp = RREG8(CRT_DATA); + tmp &= 0x7f; + tmp |= (addr >> 12) & 0x80; + WREG_CRT(0x1d, tmp); +} + +static void cirrus_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct drm_gem_object *obj; + struct cirrus_bo *bo; + + cirrus_set_framebuffer_regs(plane); + + obj = to_cirrus_framebuffer(state->fb)->obj; + bo = gem_to_cirrus_bo(obj); + cirrus_set_start_address(state->crtc, (u32)bo->gpu_addr); + + /* Unblank (needed on S3 resume, vgabios doesn't do it then) */ + outb(0x20, 0x3c0); +} + + static const struct drm_plane_helper_funcs cirrus_plane_helper_funcs = { + .prepare_fb = cirrus_plane_prepare_fb, + .cleanup_fb = cirrus_plane_cleanup_fb, + .atomic_check = cirrus_plane_atomic_check, + .atomic_disable = cirrus_plane_atomic_disable, + .atomic_update = cirrus_plane_atomic_update, };
/* CRTC setup */ @@ -509,6 +600,9 @@ static const struct drm_connector_funcs cirrus_vga_connector_funcs = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = cirrus_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, };
static struct drm_connector *cirrus_vga_init(struct drm_device *dev) @@ -565,6 +659,8 @@ int cirrus_modeset_init(struct cirrus_device *cdev)
drm_mode_connector_attach_encoder(connector, encoder);
+ drm_mode_config_reset(cdev->dev); + ret = cirrus_fbdev_init(cdev); if (ret) { DRM_ERROR("cirrus_fbdev_init failed\n");
From: Varad Gautam varad.gautam@collabora.com
the hardware does not provide interrupts on vblank, so we just send a fake vblank event on atomic_flush.
Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/cirrus_mode.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index ee023c865165..4c95447ac445 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -217,6 +217,23 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc) kfree(cirrus_crtc); }
+static void cirrus_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *dev = crtc->dev; + struct drm_pending_vblank_event *event; + unsigned long flags; + + if (crtc->state && crtc->state->event) { + event = crtc->state->event; + crtc->state->event = NULL; + + spin_lock_irqsave(&dev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + } +} + /* These provide the minimum set of functions required to handle a CRTC */ static const struct drm_crtc_funcs cirrus_crtc_funcs = { .gamma_set = cirrus_crtc_gamma_set, @@ -234,6 +251,7 @@ static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { .mode_set_nofb = cirrus_mode_set_nofb, .prepare = cirrus_crtc_prepare, .commit = cirrus_crtc_commit, + .atomic_flush = cirrus_crtc_atomic_flush, };
static int cirrus_plane_update(struct drm_plane *plane,
From: Varad Gautam varad.gautam@collabora.com
move from transition helpers to actual atomic handlers.
Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/cirrus_mode.c | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 4c95447ac445..fb3d808ffb5f 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -160,15 +160,6 @@ static void cirrus_mode_set_nofb(struct drm_crtc *crtc) }
/* - * This is called before a mode is programmed. A typical use might be to - * enable DPMS during the programming to avoid seeing intermediate stages, - * but that's not relevant to us - */ -static void cirrus_crtc_prepare(struct drm_crtc *crtc) -{ -} - -/* * This is called after a mode is programmed. It should reverse anything done * by the prepare function */ @@ -237,8 +228,9 @@ static void cirrus_crtc_atomic_flush(struct drm_crtc *crtc, /* These provide the minimum set of functions required to handle a CRTC */ static const struct drm_crtc_funcs cirrus_crtc_funcs = { .gamma_set = cirrus_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, + .set_config = drm_atomic_helper_set_config, .destroy = cirrus_crtc_destroy, + .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, @@ -246,27 +238,11 @@ static const struct drm_crtc_funcs cirrus_crtc_funcs = {
static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { .dpms = cirrus_crtc_dpms, - .mode_set = drm_helper_crtc_mode_set, - .mode_set_base = drm_helper_crtc_mode_set_base, .mode_set_nofb = cirrus_mode_set_nofb, - .prepare = cirrus_crtc_prepare, .commit = cirrus_crtc_commit, .atomic_flush = cirrus_crtc_atomic_flush, };
-static int cirrus_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, int crtc_x, - int crtc_y, unsigned int crtc_w, - unsigned int crtc_h, uint32_t src_x, - uint32_t src_y, uint32_t src_w, uint32_t src_h, - struct drm_modeset_acquire_ctx *ctx) -{ - return drm_plane_helper_update(plane, crtc, fb, - crtc_x, crtc_y, crtc_w, - crtc_h, src_x, src_y, src_w, src_h); -} - static const uint32_t cirrus_plane_formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, @@ -275,7 +251,7 @@ static const uint32_t cirrus_plane_formats[] = { };
static const struct drm_plane_funcs cirrus_plane_funcs = { - .update_plane = cirrus_plane_update, + .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_primary_helper_disable, .destroy = drm_primary_helper_destroy, .reset = drm_atomic_helper_plane_reset,
From: Varad Gautam varad.gautam@collabora.com
This enables cursor plane on cirrus. It only supports ARGB 8-bit cursors from userspace, and downconverts them to 1-bit black and white with masking, which is all cirrus hardware can support. Only cursors with size 32x32 or 64x64 will work.
initial non-atomic version: Reviewed-at: https://chromium-review.googlesource.com/335579 https://chromium-review.googlesource.com/339091 Signed-off-by: Zach Reizner zachr@google.com
Signed-off-by: Varad Gautam varadgautam@gmail.com
CC: Haixia Shi hshi@chromium.org CC: Stéphane Marchesin marcheu@chromium.org
v2: rework faulty error handling (krisman krisman@collabora.co.uk) --- drivers/gpu/drm/cirrus/cirrus_drv.h | 13 ++ drivers/gpu/drm/cirrus/cirrus_main.c | 13 ++ drivers/gpu/drm/cirrus/cirrus_mode.c | 273 ++++++++++++++++++++++++++++++++++- 3 files changed, 294 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 1db0849b4bcb..639af38c035d 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -50,6 +50,17 @@ WREG8(SEQ_DATA, v); \ } while (0) \
+#define PAL_ADDR 8 +#define PAL_DATA 9 + +#define WREG_PAL(addr, r, g, b) \ + do { \ + WREG8(PAL_ADDR, addr); \ + WREG8(PAL_DATA, r); \ + WREG8(PAL_DATA, g); \ + WREG8(PAL_DATA, b); \ + } while (0) \ + #define CRT_INDEX 0x14 #define CRT_DATA 0x15
@@ -136,6 +147,8 @@ struct cirrus_device { void __iomem *rmmio;
struct cirrus_mc mc; + resource_size_t cursor_ram_size; + void __iomem *cursor_iomem; struct cirrus_mode_info mode_info;
int num_crtc; diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index f146a4129742..deb531b6e3db 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -94,6 +94,8 @@ static void cirrus_vram_fini(struct cirrus_device *cdev) cdev->rmmio = NULL; if (cdev->mc.vram_base) release_mem_region(cdev->mc.vram_base, cdev->mc.vram_size); + if (cdev->cursor_iomem) + iounmap(cdev->cursor_iomem); }
/* Map the framebuffer from the card and configure the core */ @@ -107,12 +109,23 @@ static int cirrus_vram_init(struct cirrus_device *cdev) * find the cursor data at the 4M - 16K point. */ cdev->mc.vram_size = 4 * 1024 * 1024; + /* The last 16K of VRAM is for cursor */ + cdev->cursor_ram_size = 16 * 1024;
if (!request_mem_region(cdev->mc.vram_base, cdev->mc.vram_size, "cirrusdrmfb_vram")) { DRM_ERROR("can't reserve VRAM\n"); return -ENXIO; } + cdev->cursor_iomem = ioremap_nocache(cdev->mc.vram_base + + cdev->mc.vram_size - + cdev->cursor_ram_size, + cdev->cursor_ram_size); + if (!cdev->cursor_iomem) { + release_mem_region(cdev->mc.vram_base, cdev->mc.vram_size); + DRM_ERROR("can't ioremap cursor VRAM\n"); + return -ENXIO; + }
return 0; } diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index fb3d808ffb5f..66b0750eb33a 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -243,6 +243,231 @@ static const struct drm_crtc_helper_funcs cirrus_helper_funcs = { .atomic_flush = cirrus_crtc_atomic_flush, };
+static void cirrus_argb_to_cursor(void *src , void __iomem *dst, + uint32_t cursor_size) +{ + uint8_t *pixel = (uint8_t *)src; + const uint32_t row_size = cursor_size / 8; + const uint32_t plane_size = row_size * cursor_size; + uint32_t row_skip; + void __iomem *plane_0 = dst; + void __iomem *plane_1; + uint32_t x; + uint32_t y; + + switch (cursor_size) { + case 32: + row_skip = 0; + plane_1 = plane_0 + plane_size; + break; + case 64: + row_skip = row_size; + plane_1 = plane_0 + row_size; + break; + default: + DRM_DEBUG("Cursor plane format is undefined for given size"); + return; + } + + for (y = 0; y < cursor_size; y++) { + uint8_t bits_0 = 0; + uint8_t bits_1 = 0; + + for (x = 0; x < cursor_size; x++) { + uint8_t alpha = pixel[3]; + int intensity = pixel[0] + pixel[1] + pixel[2]; + + intensity /= 3; + bits_0 <<= 1; + bits_1 <<= 1; + if (alpha > 0x7f) { + bits_1 |= 1; + if (intensity > 0x7f) + bits_0 |= 1; + } + if ((x % 8) == 7) { + iowrite8(bits_0, plane_0); + iowrite8(bits_1, plane_1); + plane_0++; + plane_1++; + bits_0 = 0; + bits_1 = 0; + } + pixel += 4; + } + plane_0 += row_skip; + plane_1 += row_skip; + } +} + +static int cirrus_bo_to_cursor(struct cirrus_device *cdev, + struct drm_framebuffer *fb, + uint32_t cursor_size, uint32_t cursor_index) +{ + const uint32_t pixel_count = cursor_size * cursor_size; + const uint32_t plane_size = pixel_count / 8; + const uint32_t cursor_offset = cursor_index * plane_size * 2; + int ret = 0; + struct drm_device *dev = cdev->dev; + struct drm_gem_object *obj; + struct cirrus_bo *bo; + struct ttm_bo_kmap_obj bo_kmap; + bool is_iomem; + struct ttm_tt *ttm; + void *bo_ptr; + + if ((cursor_size == 32 && cursor_index >= 64) || + (cursor_size == 64 && cursor_index >= 16)) { + DRM_ERROR("Cursor index is out of bounds\n"); + return -EINVAL; + } + + mutex_lock(&dev->struct_mutex); + obj = to_cirrus_framebuffer(fb)->obj; + if (obj == NULL) { + ret = -ENOENT; + DRM_ERROR("Buffer handle for cursor is invalid\n"); + goto out_unlock; + } + + bo = gem_to_cirrus_bo(obj); + ttm = bo->bo.ttm; + + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo_kmap); + if (ret) { + DRM_ERROR("Cursor failed kmap of buffer object\n"); + goto out_unlock; + } + + bo_ptr = ttm_kmap_obj_virtual(&bo_kmap, &is_iomem); + + cirrus_argb_to_cursor(bo_ptr, cdev->cursor_iomem + cursor_offset, + cursor_size); + + ttm_bo_kunmap(&bo_kmap); +out_unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} + + +int cirrus_cursor_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_gem_object *obj; + struct cirrus_bo *bo; + uint32_t pixel_count; + uint32_t expected_pages; + + if (!fb) + return 0; + if (fb->width != fb->height) { + DRM_DEBUG("Cursors are expected to have square dimensions\n"); + return -EINVAL; + } + + if (!(fb->width == 32 || fb->width == 64)) { + DRM_ERROR("Cursor dimension are expected to be 32 or 64\n"); + return -EINVAL; + } + + obj = to_cirrus_framebuffer(fb)->obj; + if (obj == NULL) { + DRM_ERROR("Buffer handle for cursor is invalid\n"); + return -ENOENT; + } + bo = gem_to_cirrus_bo(obj); + pixel_count = fb->width * fb->width; + expected_pages = DIV_ROUND_UP(pixel_count * 4, PAGE_SIZE); + if (bo->bo.num_pages < expected_pages) { + DRM_ERROR("Buffer object for cursor is too small\n"); + return -EINVAL; + } + + return 0; +} + +static void cirrus_cursor_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + int ret; + struct drm_device *dev = plane->state->crtc->dev; + struct cirrus_device *cdev = dev->dev_private; + struct drm_framebuffer *fb = plane->state->fb; + uint8_t cursor_index = 0; + int width, x, y; + int sr10, sr10_index; + int sr11, sr11_index; + int sr12, sr13; + + width = fb->width; + if (fb != old_state->fb) { + WREG8(SEQ_INDEX, 0x12); + sr12 = RREG8(SEQ_DATA); + sr12 &= 0xfe; + WREG_SEQ(0x12, sr12); + + /* This may still fail if the bo reservation fails. */ + ret = cirrus_bo_to_cursor(cdev, fb, width, cursor_index); + if (ret) + return; + + WREG8(SEQ_INDEX, 0x12); + sr12 = RREG8(SEQ_DATA); + sr12 &= 0xfa; + sr12 |= 0x03; /* enables cursor and write to extra DAC LUT */ + if (width == 64) + sr12 |= 0x04; + WREG_SEQ(0x12, sr12); + + /* Background set to black, foreground set to white */ + WREG_PAL(0x00, 0, 0, 0); + WREG_PAL(0x0f, 255, 255, 255); + + sr12 &= ~0x2; /* Disables writes to the extra LUT */ + WREG_SEQ(0x12, sr12); + + sr13 = 0; + if (width == 64) + sr13 |= (cursor_index & 0x0f) << 2; + else + sr13 |= cursor_index & 0x3f; + WREG_SEQ(0x13, sr13); + } + + x = plane->state->crtc_x + fb->hot_x; + y = plane->state->crtc_y + fb->hot_y; + if (x < 0) + x = 0; + if (x > 0x7ff) + x = 0x7ff; + if (y < 0) + y = 0; + if (y > 0x7ff) + y = 0x7ff; + + sr10 = (x >> 3) & 0xff; + sr10_index = 0x10; + sr10_index |= (x & 0x07) << 5; + WREG_SEQ(sr10_index, sr10); + sr11 = (y >> 3) & 0xff; + sr11_index = 0x11; + sr11_index |= (y & 0x07) << 5; + WREG_SEQ(sr11_index, sr11); +} + +void cirrus_cursor_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct cirrus_device *cdev = plane->dev->dev_private; + int sr12; + + WREG8(SEQ_INDEX, 0x12); + sr12 = (RREG8(SEQ_DATA) | 0x04) & 0xfe; + WREG8(SEQ_DATA, sr12); +} + static const uint32_t cirrus_plane_formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, @@ -440,6 +665,26 @@ static void cirrus_plane_atomic_update(struct drm_plane *plane, outb(0x20, 0x3c0); }
+static const uint32_t cirrus_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static const struct drm_plane_funcs cirrus_cursor_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_primary_helper_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static const struct drm_plane_helper_funcs cirrus_cursor_helper_funcs = { + .atomic_check = cirrus_cursor_atomic_check, + .atomic_update = cirrus_cursor_atomic_update, + .atomic_disable = cirrus_cursor_atomic_disable, + .prepare_fb = cirrus_plane_prepare_fb, + .cleanup_fb = cirrus_plane_cleanup_fb, +};
static const struct drm_plane_helper_funcs cirrus_plane_helper_funcs = { .prepare_fb = cirrus_plane_prepare_fb, @@ -454,7 +699,7 @@ static void cirrus_crtc_init(struct drm_device *dev) { struct cirrus_device *cdev = dev->dev_private; struct cirrus_crtc *cirrus_crtc; - struct drm_plane *primary; + struct drm_plane *primary, *cursor; int ret;
cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) + @@ -479,17 +724,35 @@ static void cirrus_crtc_init(struct drm_device *dev) goto cleanup_crtc; }
- ret = drm_crtc_init_with_planes(dev, &cirrus_crtc->base, primary, NULL, - &cirrus_crtc_funcs, NULL); + cursor = kzalloc(sizeof(*cursor), GFP_KERNEL); + if (cursor == NULL) + goto cleanup_primary; + + drm_plane_helper_add(cursor, &cirrus_cursor_helper_funcs); + ret = drm_universal_plane_init(dev, cursor, 1, + &cirrus_cursor_plane_funcs, + cirrus_cursor_formats, + ARRAY_SIZE(cirrus_cursor_formats), + NULL, DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + kfree(cursor); + goto cleanup_primary; + } + + ret = drm_crtc_init_with_planes(dev, &cirrus_crtc->base, primary, cursor, + &cirrus_crtc_funcs, NULL); if (ret) - goto cleanup; + goto cleanup_cursor; drm_mode_crtc_set_gamma_size(&cirrus_crtc->base, CIRRUS_LUT_SIZE); cdev->mode_info.crtc = cirrus_crtc;
drm_crtc_helper_add(&cirrus_crtc->base, &cirrus_helper_funcs); return;
-cleanup: +cleanup_cursor: + drm_plane_cleanup(cursor); + kfree(cursor); +cleanup_primary: drm_plane_cleanup(primary); kfree(primary); cleanup_crtc:
On Fri, 2017-09-08 at 19:05 +0530, Varad Gautam wrote:
From: Varad Gautam varad.gautam@collabora.com
This enables cursor plane on cirrus. It only supports ARGB 8-bit cursors from userspace, and downconverts them to 1-bit black and white with masking, which is all cirrus hardware can support. Only cursors with size 32x32 or 64x64 will work.
Isn't it better to just not implement hardware cursor then, so userspace can fallback to software cursor instead? Advertising ARGB cursor support to userspace, but then crippling those cursors to black'n'white doesn't look sensible to me ...
cheers, Gerd
From: Varad Gautam varad.gautam@collabora.com
allow userspace to use atomic ioctls.
we now pass the following tests when running with cirrus.bpp=32: igt/kms_atomic_transition: plane-all-transition plane-use-after-nonblocking-unbind plane-all-modeset-transition plane-toggle-modeset-transition 1x-modeset-transitions 1x-modeset-transitions-nonblocking igt/kms_atomic: plane_primary_legacy test_only plane_cursor_legacy plane_invalid_params crtc_invalid_params igt/kms_setmode igt/kms_rmfb drm-tests/atomictest: disable_primary fullscreen_video overlay_pageflip primary_pageflip video_overlay
Signed-off-by: Varad Gautam varad.gautam@collabora.com --- drivers/gpu/drm/cirrus/cirrus_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 09461868ed46..c2c010379b93 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -130,7 +130,8 @@ static const struct file_operations cirrus_driver_fops = { .compat_ioctl = drm_compat_ioctl, }; static struct drm_driver driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | + DRIVER_ATOMIC, .load = cirrus_driver_load, .unload = cirrus_driver_unload, .fops = &cirrus_driver_fops,
dri-devel@lists.freedesktop.org