162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2014 462306a36Sopenharmony_ci * Authors: Vincent Abriou <vincent.abriou@st.com> 562306a36Sopenharmony_ci * Fabien Dessenne <fabien.dessenne@st.com> 662306a36Sopenharmony_ci * for STMicroelectronics. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1062306a36Sopenharmony_ci#include <linux/seq_file.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1362306a36Sopenharmony_ci#include <drm/drm_device.h> 1462306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h> 1562306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1662306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "sti_compositor.h" 1962306a36Sopenharmony_ci#include "sti_cursor.h" 2062306a36Sopenharmony_ci#include "sti_plane.h" 2162306a36Sopenharmony_ci#include "sti_vtg.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Registers */ 2462306a36Sopenharmony_ci#define CUR_CTL 0x00 2562306a36Sopenharmony_ci#define CUR_VPO 0x0C 2662306a36Sopenharmony_ci#define CUR_PML 0x14 2762306a36Sopenharmony_ci#define CUR_PMP 0x18 2862306a36Sopenharmony_ci#define CUR_SIZE 0x1C 2962306a36Sopenharmony_ci#define CUR_CML 0x20 3062306a36Sopenharmony_ci#define CUR_AWS 0x28 3162306a36Sopenharmony_ci#define CUR_AWE 0x2C 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define CUR_CTL_CLUT_UPDATE BIT(1) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define STI_CURS_MIN_SIZE 1 3662306a36Sopenharmony_ci#define STI_CURS_MAX_SIZE 128 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * pixmap dma buffer structure 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * @paddr: physical address 4262306a36Sopenharmony_ci * @size: buffer size 4362306a36Sopenharmony_ci * @base: virtual address 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct dma_pixmap { 4662306a36Sopenharmony_ci dma_addr_t paddr; 4762306a36Sopenharmony_ci size_t size; 4862306a36Sopenharmony_ci void *base; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * STI Cursor structure 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * @sti_plane: sti_plane structure 5562306a36Sopenharmony_ci * @dev: driver device 5662306a36Sopenharmony_ci * @regs: cursor registers 5762306a36Sopenharmony_ci * @width: cursor width 5862306a36Sopenharmony_ci * @height: cursor height 5962306a36Sopenharmony_ci * @clut: color look up table 6062306a36Sopenharmony_ci * @clut_paddr: color look up table physical address 6162306a36Sopenharmony_ci * @pixmap: pixmap dma buffer (clut8-format cursor) 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistruct sti_cursor { 6462306a36Sopenharmony_ci struct sti_plane plane; 6562306a36Sopenharmony_ci struct device *dev; 6662306a36Sopenharmony_ci void __iomem *regs; 6762306a36Sopenharmony_ci unsigned int width; 6862306a36Sopenharmony_ci unsigned int height; 6962306a36Sopenharmony_ci unsigned short *clut; 7062306a36Sopenharmony_ci dma_addr_t clut_paddr; 7162306a36Sopenharmony_ci struct dma_pixmap pixmap; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic const uint32_t cursor_supported_formats[] = { 7562306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define to_sti_cursor(x) container_of(x, struct sti_cursor, plane) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ 8162306a36Sopenharmony_ci readl(cursor->regs + reg)) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void cursor_dbg_vpo(struct seq_file *s, u32 val) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void cursor_dbg_size(struct seq_file *s, u32 val) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci seq_printf(s, "\t%d x %d", val & 0x07FF, (val >> 16) & 0x07FF); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void cursor_dbg_pml(struct seq_file *s, 9462306a36Sopenharmony_ci struct sti_cursor *cursor, u32 val) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci if (cursor->pixmap.paddr == val) 9762306a36Sopenharmony_ci seq_printf(s, "\tVirt @: %p", cursor->pixmap.base); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void cursor_dbg_cml(struct seq_file *s, 10162306a36Sopenharmony_ci struct sti_cursor *cursor, u32 val) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci if (cursor->clut_paddr == val) 10462306a36Sopenharmony_ci seq_printf(s, "\tVirt @: %p", cursor->clut); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int cursor_dbg_show(struct seq_file *s, void *data) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct drm_info_node *node = s->private; 11062306a36Sopenharmony_ci struct sti_cursor *cursor = (struct sti_cursor *)node->info_ent->data; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci seq_printf(s, "%s: (vaddr = 0x%p)", 11362306a36Sopenharmony_ci sti_plane_to_str(&cursor->plane), cursor->regs); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci DBGFS_DUMP(CUR_CTL); 11662306a36Sopenharmony_ci DBGFS_DUMP(CUR_VPO); 11762306a36Sopenharmony_ci cursor_dbg_vpo(s, readl(cursor->regs + CUR_VPO)); 11862306a36Sopenharmony_ci DBGFS_DUMP(CUR_PML); 11962306a36Sopenharmony_ci cursor_dbg_pml(s, cursor, readl(cursor->regs + CUR_PML)); 12062306a36Sopenharmony_ci DBGFS_DUMP(CUR_PMP); 12162306a36Sopenharmony_ci DBGFS_DUMP(CUR_SIZE); 12262306a36Sopenharmony_ci cursor_dbg_size(s, readl(cursor->regs + CUR_SIZE)); 12362306a36Sopenharmony_ci DBGFS_DUMP(CUR_CML); 12462306a36Sopenharmony_ci cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML)); 12562306a36Sopenharmony_ci DBGFS_DUMP(CUR_AWS); 12662306a36Sopenharmony_ci DBGFS_DUMP(CUR_AWE); 12762306a36Sopenharmony_ci seq_putc(s, '\n'); 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic struct drm_info_list cursor_debugfs_files[] = { 13262306a36Sopenharmony_ci { "cursor", cursor_dbg_show, 0, NULL }, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void cursor_debugfs_init(struct sti_cursor *cursor, 13662306a36Sopenharmony_ci struct drm_minor *minor) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci unsigned int i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cursor_debugfs_files); i++) 14162306a36Sopenharmony_ci cursor_debugfs_files[i].data = cursor; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci drm_debugfs_create_files(cursor_debugfs_files, 14462306a36Sopenharmony_ci ARRAY_SIZE(cursor_debugfs_files), 14562306a36Sopenharmony_ci minor->debugfs_root, minor); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void sti_cursor_argb8888_to_clut8(struct sti_cursor *cursor, u32 *src) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci u8 *dst = cursor->pixmap.base; 15162306a36Sopenharmony_ci unsigned int i, j; 15262306a36Sopenharmony_ci u32 a, r, g, b; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci for (i = 0; i < cursor->height; i++) { 15562306a36Sopenharmony_ci for (j = 0; j < cursor->width; j++) { 15662306a36Sopenharmony_ci /* Pick the 2 higher bits of each component */ 15762306a36Sopenharmony_ci a = (*src >> 30) & 3; 15862306a36Sopenharmony_ci r = (*src >> 22) & 3; 15962306a36Sopenharmony_ci g = (*src >> 14) & 3; 16062306a36Sopenharmony_ci b = (*src >> 6) & 3; 16162306a36Sopenharmony_ci *dst = a << 6 | r << 4 | g << 2 | b; 16262306a36Sopenharmony_ci src++; 16362306a36Sopenharmony_ci dst++; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void sti_cursor_init(struct sti_cursor *cursor) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci unsigned short *base = cursor->clut; 17162306a36Sopenharmony_ci unsigned int a, r, g, b; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Assign CLUT values, ARGB444 format */ 17462306a36Sopenharmony_ci for (a = 0; a < 4; a++) 17562306a36Sopenharmony_ci for (r = 0; r < 4; r++) 17662306a36Sopenharmony_ci for (g = 0; g < 4; g++) 17762306a36Sopenharmony_ci for (b = 0; b < 4; b++) 17862306a36Sopenharmony_ci *base++ = (a * 5) << 12 | 17962306a36Sopenharmony_ci (r * 5) << 8 | 18062306a36Sopenharmony_ci (g * 5) << 4 | 18162306a36Sopenharmony_ci (b * 5); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int sti_cursor_atomic_check(struct drm_plane *drm_plane, 18562306a36Sopenharmony_ci struct drm_atomic_state *state) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 18862306a36Sopenharmony_ci drm_plane); 18962306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 19062306a36Sopenharmony_ci struct sti_cursor *cursor = to_sti_cursor(plane); 19162306a36Sopenharmony_ci struct drm_crtc *crtc = new_plane_state->crtc; 19262306a36Sopenharmony_ci struct drm_framebuffer *fb = new_plane_state->fb; 19362306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 19462306a36Sopenharmony_ci struct drm_display_mode *mode; 19562306a36Sopenharmony_ci int dst_x, dst_y, dst_w, dst_h; 19662306a36Sopenharmony_ci int src_w, src_h; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* no need for further checks if the plane is being disabled */ 19962306a36Sopenharmony_ci if (!crtc || !fb) 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state, crtc); 20362306a36Sopenharmony_ci mode = &crtc_state->mode; 20462306a36Sopenharmony_ci dst_x = new_plane_state->crtc_x; 20562306a36Sopenharmony_ci dst_y = new_plane_state->crtc_y; 20662306a36Sopenharmony_ci dst_w = clamp_val(new_plane_state->crtc_w, 0, 20762306a36Sopenharmony_ci mode->crtc_hdisplay - dst_x); 20862306a36Sopenharmony_ci dst_h = clamp_val(new_plane_state->crtc_h, 0, 20962306a36Sopenharmony_ci mode->crtc_vdisplay - dst_y); 21062306a36Sopenharmony_ci /* src_x are in 16.16 format */ 21162306a36Sopenharmony_ci src_w = new_plane_state->src_w >> 16; 21262306a36Sopenharmony_ci src_h = new_plane_state->src_h >> 16; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (src_w < STI_CURS_MIN_SIZE || 21562306a36Sopenharmony_ci src_h < STI_CURS_MIN_SIZE || 21662306a36Sopenharmony_ci src_w > STI_CURS_MAX_SIZE || 21762306a36Sopenharmony_ci src_h > STI_CURS_MAX_SIZE) { 21862306a36Sopenharmony_ci DRM_ERROR("Invalid cursor size (%dx%d)\n", 21962306a36Sopenharmony_ci src_w, src_h); 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* If the cursor size has changed, re-allocated the pixmap */ 22462306a36Sopenharmony_ci if (!cursor->pixmap.base || 22562306a36Sopenharmony_ci (cursor->width != src_w) || 22662306a36Sopenharmony_ci (cursor->height != src_h)) { 22762306a36Sopenharmony_ci cursor->width = src_w; 22862306a36Sopenharmony_ci cursor->height = src_h; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (cursor->pixmap.base) 23162306a36Sopenharmony_ci dma_free_wc(cursor->dev, cursor->pixmap.size, 23262306a36Sopenharmony_ci cursor->pixmap.base, cursor->pixmap.paddr); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci cursor->pixmap.size = cursor->width * cursor->height; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci cursor->pixmap.base = dma_alloc_wc(cursor->dev, 23762306a36Sopenharmony_ci cursor->pixmap.size, 23862306a36Sopenharmony_ci &cursor->pixmap.paddr, 23962306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 24062306a36Sopenharmony_ci if (!cursor->pixmap.base) { 24162306a36Sopenharmony_ci DRM_ERROR("Failed to allocate memory for pixmap\n"); 24262306a36Sopenharmony_ci return -EINVAL; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!drm_fb_dma_get_gem_obj(fb, 0)) { 24762306a36Sopenharmony_ci DRM_ERROR("Can't get DMA GEM object for fb\n"); 24862306a36Sopenharmony_ci return -EINVAL; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", 25262306a36Sopenharmony_ci crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)), 25362306a36Sopenharmony_ci drm_plane->base.id, sti_plane_to_str(plane)); 25462306a36Sopenharmony_ci DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void sti_cursor_atomic_update(struct drm_plane *drm_plane, 26062306a36Sopenharmony_ci struct drm_atomic_state *state) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state, 26362306a36Sopenharmony_ci drm_plane); 26462306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 26562306a36Sopenharmony_ci struct sti_cursor *cursor = to_sti_cursor(plane); 26662306a36Sopenharmony_ci struct drm_crtc *crtc = newstate->crtc; 26762306a36Sopenharmony_ci struct drm_framebuffer *fb = newstate->fb; 26862306a36Sopenharmony_ci struct drm_display_mode *mode; 26962306a36Sopenharmony_ci int dst_x, dst_y; 27062306a36Sopenharmony_ci struct drm_gem_dma_object *dma_obj; 27162306a36Sopenharmony_ci u32 y, x; 27262306a36Sopenharmony_ci u32 val; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (!crtc || !fb) 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci mode = &crtc->mode; 27862306a36Sopenharmony_ci dst_x = newstate->crtc_x; 27962306a36Sopenharmony_ci dst_y = newstate->crtc_y; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci dma_obj = drm_fb_dma_get_gem_obj(fb, 0); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Convert ARGB8888 to CLUT8 */ 28462306a36Sopenharmony_ci sti_cursor_argb8888_to_clut8(cursor, (u32 *)dma_obj->vaddr); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* AWS and AWE depend on the mode */ 28762306a36Sopenharmony_ci y = sti_vtg_get_line_number(*mode, 0); 28862306a36Sopenharmony_ci x = sti_vtg_get_pixel_number(*mode, 0); 28962306a36Sopenharmony_ci val = y << 16 | x; 29062306a36Sopenharmony_ci writel(val, cursor->regs + CUR_AWS); 29162306a36Sopenharmony_ci y = sti_vtg_get_line_number(*mode, mode->vdisplay - 1); 29262306a36Sopenharmony_ci x = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1); 29362306a36Sopenharmony_ci val = y << 16 | x; 29462306a36Sopenharmony_ci writel(val, cursor->regs + CUR_AWE); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* Set memory location, size, and position */ 29762306a36Sopenharmony_ci writel(cursor->pixmap.paddr, cursor->regs + CUR_PML); 29862306a36Sopenharmony_ci writel(cursor->width, cursor->regs + CUR_PMP); 29962306a36Sopenharmony_ci writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci y = sti_vtg_get_line_number(*mode, dst_y); 30262306a36Sopenharmony_ci x = sti_vtg_get_pixel_number(*mode, dst_x); 30362306a36Sopenharmony_ci writel((y << 16) | x, cursor->regs + CUR_VPO); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Set and fetch CLUT */ 30662306a36Sopenharmony_ci writel(cursor->clut_paddr, cursor->regs + CUR_CML); 30762306a36Sopenharmony_ci writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci sti_plane_update_fps(plane, true, false); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci plane->status = STI_PLANE_UPDATED; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void sti_cursor_atomic_disable(struct drm_plane *drm_plane, 31562306a36Sopenharmony_ci struct drm_atomic_state *state) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state, 31862306a36Sopenharmony_ci drm_plane); 31962306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!oldstate->crtc) { 32262306a36Sopenharmony_ci DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", 32362306a36Sopenharmony_ci drm_plane->base.id); 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", 32862306a36Sopenharmony_ci oldstate->crtc->base.id, 32962306a36Sopenharmony_ci sti_mixer_to_str(to_sti_mixer(oldstate->crtc)), 33062306a36Sopenharmony_ci drm_plane->base.id, sti_plane_to_str(plane)); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci plane->status = STI_PLANE_DISABLING; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = { 33662306a36Sopenharmony_ci .atomic_check = sti_cursor_atomic_check, 33762306a36Sopenharmony_ci .atomic_update = sti_cursor_atomic_update, 33862306a36Sopenharmony_ci .atomic_disable = sti_cursor_atomic_disable, 33962306a36Sopenharmony_ci}; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int sti_cursor_late_register(struct drm_plane *drm_plane) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 34462306a36Sopenharmony_ci struct sti_cursor *cursor = to_sti_cursor(plane); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci cursor_debugfs_init(cursor, drm_plane->dev->primary); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic const struct drm_plane_funcs sti_cursor_plane_helpers_funcs = { 35262306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 35362306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 35462306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 35562306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 35662306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 35762306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 35862306a36Sopenharmony_ci .late_register = sti_cursor_late_register, 35962306a36Sopenharmony_ci}; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistruct drm_plane *sti_cursor_create(struct drm_device *drm_dev, 36262306a36Sopenharmony_ci struct device *dev, int desc, 36362306a36Sopenharmony_ci void __iomem *baseaddr, 36462306a36Sopenharmony_ci unsigned int possible_crtcs) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct sti_cursor *cursor; 36762306a36Sopenharmony_ci size_t size; 36862306a36Sopenharmony_ci int res; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci cursor = devm_kzalloc(dev, sizeof(*cursor), GFP_KERNEL); 37162306a36Sopenharmony_ci if (!cursor) { 37262306a36Sopenharmony_ci DRM_ERROR("Failed to allocate memory for cursor\n"); 37362306a36Sopenharmony_ci return NULL; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Allocate clut buffer */ 37762306a36Sopenharmony_ci size = 0x100 * sizeof(unsigned short); 37862306a36Sopenharmony_ci cursor->clut = dma_alloc_wc(dev, size, &cursor->clut_paddr, 37962306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!cursor->clut) { 38262306a36Sopenharmony_ci DRM_ERROR("Failed to allocate memory for cursor clut\n"); 38362306a36Sopenharmony_ci goto err_clut; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci cursor->dev = dev; 38762306a36Sopenharmony_ci cursor->regs = baseaddr; 38862306a36Sopenharmony_ci cursor->plane.desc = desc; 38962306a36Sopenharmony_ci cursor->plane.status = STI_PLANE_DISABLED; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci sti_cursor_init(cursor); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci res = drm_universal_plane_init(drm_dev, &cursor->plane.drm_plane, 39462306a36Sopenharmony_ci possible_crtcs, 39562306a36Sopenharmony_ci &sti_cursor_plane_helpers_funcs, 39662306a36Sopenharmony_ci cursor_supported_formats, 39762306a36Sopenharmony_ci ARRAY_SIZE(cursor_supported_formats), 39862306a36Sopenharmony_ci NULL, DRM_PLANE_TYPE_CURSOR, NULL); 39962306a36Sopenharmony_ci if (res) { 40062306a36Sopenharmony_ci DRM_ERROR("Failed to initialize universal plane\n"); 40162306a36Sopenharmony_ci goto err_plane; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci drm_plane_helper_add(&cursor->plane.drm_plane, 40562306a36Sopenharmony_ci &sti_cursor_helpers_funcs); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return &cursor->plane.drm_plane; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cierr_plane: 41262306a36Sopenharmony_ci dma_free_wc(dev, size, cursor->clut, cursor->clut_paddr); 41362306a36Sopenharmony_cierr_clut: 41462306a36Sopenharmony_ci devm_kfree(dev, cursor); 41562306a36Sopenharmony_ci return NULL; 41662306a36Sopenharmony_ci} 417