From: Varad Gautam <varad.gautam@xxxxxxxxxxxxx> 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@xxxxxxxxxx> Signed-off-by: Varad Gautam <varadgautam@xxxxxxxxx> CC: Haixia Shi <hshi@xxxxxxxxxxxx> CC: Stéphane Marchesin <marcheu@xxxxxxxxxxxx> --- drivers/gpu/drm/cirrus/cirrus_drv.h | 13 ++ drivers/gpu/drm/cirrus/cirrus_main.c | 13 ++ drivers/gpu/drm/cirrus/cirrus_mode.c | 281 +++++++++++++++++++++++++++++++++-- 3 files changed, 297 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index d680815f23e6..fbd76c4e6d57 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 @@ -137,6 +148,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 7d0431bbc6e3..20229b13d6d2 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 39bea39a565e..915028159975 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -254,6 +254,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, @@ -451,6 +676,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, @@ -465,7 +710,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 i, ret; cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) + @@ -481,17 +726,30 @@ static void cirrus_crtc_init(struct drm_device *dev) 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), - DRM_PLANE_TYPE_PRIMARY, NULL); + &cirrus_plane_funcs, + cirrus_plane_formats, + ARRAY_SIZE(cirrus_plane_formats), + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + goto cleanup_primary; + + 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), + DRM_PLANE_TYPE_CURSOR, NULL); if (ret) - goto cleanup; + goto cleanup_cursor; - ret = drm_crtc_init_with_planes(dev, &cirrus_crtc->base, primary, NULL, - &cirrus_crtc_funcs, NULL); + 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; @@ -504,7 +762,10 @@ static void cirrus_crtc_init(struct drm_device *dev) 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: -- 2.13.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel