Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) STMicroelectronics SA 2014
0004  * Authors: Vincent Abriou <vincent.abriou@st.com>
0005  *          Fabien Dessenne <fabien.dessenne@st.com>
0006  *          for STMicroelectronics.
0007  */
0008 
0009 #include <linux/dma-mapping.h>
0010 #include <linux/seq_file.h>
0011 
0012 #include <drm/drm_atomic.h>
0013 #include <drm/drm_device.h>
0014 #include <drm/drm_fb_cma_helper.h>
0015 #include <drm/drm_framebuffer.h>
0016 #include <drm/drm_gem_cma_helper.h>
0017 
0018 #include "sti_compositor.h"
0019 #include "sti_cursor.h"
0020 #include "sti_plane.h"
0021 #include "sti_vtg.h"
0022 
0023 /* Registers */
0024 #define CUR_CTL             0x00
0025 #define CUR_VPO             0x0C
0026 #define CUR_PML             0x14
0027 #define CUR_PMP             0x18
0028 #define CUR_SIZE            0x1C
0029 #define CUR_CML             0x20
0030 #define CUR_AWS             0x28
0031 #define CUR_AWE             0x2C
0032 
0033 #define CUR_CTL_CLUT_UPDATE BIT(1)
0034 
0035 #define STI_CURS_MIN_SIZE   1
0036 #define STI_CURS_MAX_SIZE   128
0037 
0038 /*
0039  * pixmap dma buffer structure
0040  *
0041  * @paddr:  physical address
0042  * @size:   buffer size
0043  * @base:   virtual address
0044  */
0045 struct dma_pixmap {
0046     dma_addr_t paddr;
0047     size_t size;
0048     void *base;
0049 };
0050 
0051 /*
0052  * STI Cursor structure
0053  *
0054  * @sti_plane:    sti_plane structure
0055  * @dev:          driver device
0056  * @regs:         cursor registers
0057  * @width:        cursor width
0058  * @height:       cursor height
0059  * @clut:         color look up table
0060  * @clut_paddr:   color look up table physical address
0061  * @pixmap:       pixmap dma buffer (clut8-format cursor)
0062  */
0063 struct sti_cursor {
0064     struct sti_plane plane;
0065     struct device *dev;
0066     void __iomem *regs;
0067     unsigned int width;
0068     unsigned int height;
0069     unsigned short *clut;
0070     dma_addr_t clut_paddr;
0071     struct dma_pixmap pixmap;
0072 };
0073 
0074 static const uint32_t cursor_supported_formats[] = {
0075     DRM_FORMAT_ARGB8888,
0076 };
0077 
0078 #define to_sti_cursor(x) container_of(x, struct sti_cursor, plane)
0079 
0080 #define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \
0081                    readl(cursor->regs + reg))
0082 
0083 static void cursor_dbg_vpo(struct seq_file *s, u32 val)
0084 {
0085     seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0x0FFF, (val >> 16) & 0x0FFF);
0086 }
0087 
0088 static void cursor_dbg_size(struct seq_file *s, u32 val)
0089 {
0090     seq_printf(s, "\t%d x %d", val & 0x07FF, (val >> 16) & 0x07FF);
0091 }
0092 
0093 static void cursor_dbg_pml(struct seq_file *s,
0094                struct sti_cursor *cursor, u32 val)
0095 {
0096     if (cursor->pixmap.paddr == val)
0097         seq_printf(s, "\tVirt @: %p", cursor->pixmap.base);
0098 }
0099 
0100 static void cursor_dbg_cml(struct seq_file *s,
0101                struct sti_cursor *cursor, u32 val)
0102 {
0103     if (cursor->clut_paddr == val)
0104         seq_printf(s, "\tVirt @: %p", cursor->clut);
0105 }
0106 
0107 static int cursor_dbg_show(struct seq_file *s, void *data)
0108 {
0109     struct drm_info_node *node = s->private;
0110     struct sti_cursor *cursor = (struct sti_cursor *)node->info_ent->data;
0111 
0112     seq_printf(s, "%s: (vaddr = 0x%p)",
0113            sti_plane_to_str(&cursor->plane), cursor->regs);
0114 
0115     DBGFS_DUMP(CUR_CTL);
0116     DBGFS_DUMP(CUR_VPO);
0117     cursor_dbg_vpo(s, readl(cursor->regs + CUR_VPO));
0118     DBGFS_DUMP(CUR_PML);
0119     cursor_dbg_pml(s, cursor, readl(cursor->regs + CUR_PML));
0120     DBGFS_DUMP(CUR_PMP);
0121     DBGFS_DUMP(CUR_SIZE);
0122     cursor_dbg_size(s, readl(cursor->regs + CUR_SIZE));
0123     DBGFS_DUMP(CUR_CML);
0124     cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML));
0125     DBGFS_DUMP(CUR_AWS);
0126     DBGFS_DUMP(CUR_AWE);
0127     seq_putc(s, '\n');
0128     return 0;
0129 }
0130 
0131 static struct drm_info_list cursor_debugfs_files[] = {
0132     { "cursor", cursor_dbg_show, 0, NULL },
0133 };
0134 
0135 static void cursor_debugfs_init(struct sti_cursor *cursor,
0136                 struct drm_minor *minor)
0137 {
0138     unsigned int i;
0139 
0140     for (i = 0; i < ARRAY_SIZE(cursor_debugfs_files); i++)
0141         cursor_debugfs_files[i].data = cursor;
0142 
0143     drm_debugfs_create_files(cursor_debugfs_files,
0144                  ARRAY_SIZE(cursor_debugfs_files),
0145                  minor->debugfs_root, minor);
0146 }
0147 
0148 static void sti_cursor_argb8888_to_clut8(struct sti_cursor *cursor, u32 *src)
0149 {
0150     u8  *dst = cursor->pixmap.base;
0151     unsigned int i, j;
0152     u32 a, r, g, b;
0153 
0154     for (i = 0; i < cursor->height; i++) {
0155         for (j = 0; j < cursor->width; j++) {
0156             /* Pick the 2 higher bits of each component */
0157             a = (*src >> 30) & 3;
0158             r = (*src >> 22) & 3;
0159             g = (*src >> 14) & 3;
0160             b = (*src >> 6) & 3;
0161             *dst = a << 6 | r << 4 | g << 2 | b;
0162             src++;
0163             dst++;
0164         }
0165     }
0166 }
0167 
0168 static void sti_cursor_init(struct sti_cursor *cursor)
0169 {
0170     unsigned short *base = cursor->clut;
0171     unsigned int a, r, g, b;
0172 
0173     /* Assign CLUT values, ARGB444 format */
0174     for (a = 0; a < 4; a++)
0175         for (r = 0; r < 4; r++)
0176             for (g = 0; g < 4; g++)
0177                 for (b = 0; b < 4; b++)
0178                     *base++ = (a * 5) << 12 |
0179                           (r * 5) << 8 |
0180                           (g * 5) << 4 |
0181                           (b * 5);
0182 }
0183 
0184 static int sti_cursor_atomic_check(struct drm_plane *drm_plane,
0185                    struct drm_atomic_state *state)
0186 {
0187     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0188                                          drm_plane);
0189     struct sti_plane *plane = to_sti_plane(drm_plane);
0190     struct sti_cursor *cursor = to_sti_cursor(plane);
0191     struct drm_crtc *crtc = new_plane_state->crtc;
0192     struct drm_framebuffer *fb = new_plane_state->fb;
0193     struct drm_crtc_state *crtc_state;
0194     struct drm_display_mode *mode;
0195     int dst_x, dst_y, dst_w, dst_h;
0196     int src_w, src_h;
0197 
0198     /* no need for further checks if the plane is being disabled */
0199     if (!crtc || !fb)
0200         return 0;
0201 
0202     crtc_state = drm_atomic_get_crtc_state(state, crtc);
0203     mode = &crtc_state->mode;
0204     dst_x = new_plane_state->crtc_x;
0205     dst_y = new_plane_state->crtc_y;
0206     dst_w = clamp_val(new_plane_state->crtc_w, 0,
0207               mode->crtc_hdisplay - dst_x);
0208     dst_h = clamp_val(new_plane_state->crtc_h, 0,
0209               mode->crtc_vdisplay - dst_y);
0210     /* src_x are in 16.16 format */
0211     src_w = new_plane_state->src_w >> 16;
0212     src_h = new_plane_state->src_h >> 16;
0213 
0214     if (src_w < STI_CURS_MIN_SIZE ||
0215         src_h < STI_CURS_MIN_SIZE ||
0216         src_w > STI_CURS_MAX_SIZE ||
0217         src_h > STI_CURS_MAX_SIZE) {
0218         DRM_ERROR("Invalid cursor size (%dx%d)\n",
0219                 src_w, src_h);
0220         return -EINVAL;
0221     }
0222 
0223     /* If the cursor size has changed, re-allocated the pixmap */
0224     if (!cursor->pixmap.base ||
0225         (cursor->width != src_w) ||
0226         (cursor->height != src_h)) {
0227         cursor->width = src_w;
0228         cursor->height = src_h;
0229 
0230         if (cursor->pixmap.base)
0231             dma_free_wc(cursor->dev, cursor->pixmap.size,
0232                     cursor->pixmap.base, cursor->pixmap.paddr);
0233 
0234         cursor->pixmap.size = cursor->width * cursor->height;
0235 
0236         cursor->pixmap.base = dma_alloc_wc(cursor->dev,
0237                            cursor->pixmap.size,
0238                            &cursor->pixmap.paddr,
0239                            GFP_KERNEL | GFP_DMA);
0240         if (!cursor->pixmap.base) {
0241             DRM_ERROR("Failed to allocate memory for pixmap\n");
0242             return -EINVAL;
0243         }
0244     }
0245 
0246     if (!drm_fb_cma_get_gem_obj(fb, 0)) {
0247         DRM_ERROR("Can't get CMA GEM object for fb\n");
0248         return -EINVAL;
0249     }
0250 
0251     DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
0252               crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)),
0253               drm_plane->base.id, sti_plane_to_str(plane));
0254     DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", dst_w, dst_h, dst_x, dst_y);
0255 
0256     return 0;
0257 }
0258 
0259 static void sti_cursor_atomic_update(struct drm_plane *drm_plane,
0260                      struct drm_atomic_state *state)
0261 {
0262     struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state,
0263                                       drm_plane);
0264     struct sti_plane *plane = to_sti_plane(drm_plane);
0265     struct sti_cursor *cursor = to_sti_cursor(plane);
0266     struct drm_crtc *crtc = newstate->crtc;
0267     struct drm_framebuffer *fb = newstate->fb;
0268     struct drm_display_mode *mode;
0269     int dst_x, dst_y;
0270     struct drm_gem_cma_object *cma_obj;
0271     u32 y, x;
0272     u32 val;
0273 
0274     if (!crtc || !fb)
0275         return;
0276 
0277     mode = &crtc->mode;
0278     dst_x = newstate->crtc_x;
0279     dst_y = newstate->crtc_y;
0280 
0281     cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
0282 
0283     /* Convert ARGB8888 to CLUT8 */
0284     sti_cursor_argb8888_to_clut8(cursor, (u32 *)cma_obj->vaddr);
0285 
0286     /* AWS and AWE depend on the mode */
0287     y = sti_vtg_get_line_number(*mode, 0);
0288     x = sti_vtg_get_pixel_number(*mode, 0);
0289     val = y << 16 | x;
0290     writel(val, cursor->regs + CUR_AWS);
0291     y = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
0292     x = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
0293     val = y << 16 | x;
0294     writel(val, cursor->regs + CUR_AWE);
0295 
0296     /* Set memory location, size, and position */
0297     writel(cursor->pixmap.paddr, cursor->regs + CUR_PML);
0298     writel(cursor->width, cursor->regs + CUR_PMP);
0299     writel(cursor->height << 16 | cursor->width, cursor->regs + CUR_SIZE);
0300 
0301     y = sti_vtg_get_line_number(*mode, dst_y);
0302     x = sti_vtg_get_pixel_number(*mode, dst_x);
0303     writel((y << 16) | x, cursor->regs + CUR_VPO);
0304 
0305     /* Set and fetch CLUT */
0306     writel(cursor->clut_paddr, cursor->regs + CUR_CML);
0307     writel(CUR_CTL_CLUT_UPDATE, cursor->regs + CUR_CTL);
0308 
0309     sti_plane_update_fps(plane, true, false);
0310 
0311     plane->status = STI_PLANE_UPDATED;
0312 }
0313 
0314 static void sti_cursor_atomic_disable(struct drm_plane *drm_plane,
0315                       struct drm_atomic_state *state)
0316 {
0317     struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state,
0318                                       drm_plane);
0319     struct sti_plane *plane = to_sti_plane(drm_plane);
0320 
0321     if (!oldstate->crtc) {
0322         DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
0323                  drm_plane->base.id);
0324         return;
0325     }
0326 
0327     DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
0328              oldstate->crtc->base.id,
0329              sti_mixer_to_str(to_sti_mixer(oldstate->crtc)),
0330              drm_plane->base.id, sti_plane_to_str(plane));
0331 
0332     plane->status = STI_PLANE_DISABLING;
0333 }
0334 
0335 static const struct drm_plane_helper_funcs sti_cursor_helpers_funcs = {
0336     .atomic_check = sti_cursor_atomic_check,
0337     .atomic_update = sti_cursor_atomic_update,
0338     .atomic_disable = sti_cursor_atomic_disable,
0339 };
0340 
0341 static int sti_cursor_late_register(struct drm_plane *drm_plane)
0342 {
0343     struct sti_plane *plane = to_sti_plane(drm_plane);
0344     struct sti_cursor *cursor = to_sti_cursor(plane);
0345 
0346     cursor_debugfs_init(cursor, drm_plane->dev->primary);
0347 
0348     return 0;
0349 }
0350 
0351 static const struct drm_plane_funcs sti_cursor_plane_helpers_funcs = {
0352     .update_plane = drm_atomic_helper_update_plane,
0353     .disable_plane = drm_atomic_helper_disable_plane,
0354     .destroy = drm_plane_cleanup,
0355     .reset = drm_atomic_helper_plane_reset,
0356     .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
0357     .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
0358     .late_register = sti_cursor_late_register,
0359 };
0360 
0361 struct drm_plane *sti_cursor_create(struct drm_device *drm_dev,
0362                     struct device *dev, int desc,
0363                     void __iomem *baseaddr,
0364                     unsigned int possible_crtcs)
0365 {
0366     struct sti_cursor *cursor;
0367     size_t size;
0368     int res;
0369 
0370     cursor = devm_kzalloc(dev, sizeof(*cursor), GFP_KERNEL);
0371     if (!cursor) {
0372         DRM_ERROR("Failed to allocate memory for cursor\n");
0373         return NULL;
0374     }
0375 
0376     /* Allocate clut buffer */
0377     size = 0x100 * sizeof(unsigned short);
0378     cursor->clut = dma_alloc_wc(dev, size, &cursor->clut_paddr,
0379                     GFP_KERNEL | GFP_DMA);
0380 
0381     if (!cursor->clut) {
0382         DRM_ERROR("Failed to allocate memory for cursor clut\n");
0383         goto err_clut;
0384     }
0385 
0386     cursor->dev = dev;
0387     cursor->regs = baseaddr;
0388     cursor->plane.desc = desc;
0389     cursor->plane.status = STI_PLANE_DISABLED;
0390 
0391     sti_cursor_init(cursor);
0392 
0393     res = drm_universal_plane_init(drm_dev, &cursor->plane.drm_plane,
0394                        possible_crtcs,
0395                        &sti_cursor_plane_helpers_funcs,
0396                        cursor_supported_formats,
0397                        ARRAY_SIZE(cursor_supported_formats),
0398                        NULL, DRM_PLANE_TYPE_CURSOR, NULL);
0399     if (res) {
0400         DRM_ERROR("Failed to initialize universal plane\n");
0401         goto err_plane;
0402     }
0403 
0404     drm_plane_helper_add(&cursor->plane.drm_plane,
0405                  &sti_cursor_helpers_funcs);
0406 
0407     sti_plane_init_property(&cursor->plane, DRM_PLANE_TYPE_CURSOR);
0408 
0409     return &cursor->plane.drm_plane;
0410 
0411 err_plane:
0412     dma_free_wc(dev, size, cursor->clut, cursor->clut_paddr);
0413 err_clut:
0414     devm_kfree(dev, cursor);
0415     return NULL;
0416 }