1/* 2 * Copyright (c) 2018 Loongson Technology Co., Ltd. 3 * Authors: 4 * Chen Zhu <zhuchen@loongson.cn> 5 * Yaling Fang <fangyaling@loongson.cn> 6 * Dandan Zhang <zhangdandan@loongson.cn> 7 * Huacai Chen <chenhc@lemote.com> 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 */ 13 14#include <asm/cacheflush.h> 15#include <drm/drm_gem_cma_helper.h> 16#include "loongson_drv.h" 17 18static void flush_scache_range(void *addr, unsigned long size) 19{ 20 int inc; 21 unsigned long start, end; 22 23 start = (unsigned long)addr; 24 end = (unsigned long)addr + size; 25 inc = cpu_last_level_cache_line_size(); 26 27 for (; start < end; start += inc) 28 flush_cache_line(Cache_LEAF3, start); 29} 30 31/* 32 Hide the cursor off screen. We can't disable the cursor hardware because it 33 takes too long to re-activate and causes momentary corruption 34*/ 35static void loongson_hide_cursor(struct drm_crtc *crtc) 36{ 37 unsigned long flags; 38 volatile void __iomem *base; 39 struct drm_device *dev = crtc->dev; 40 struct loongson_drm_device *ldev = (struct loongson_drm_device *)dev->dev_private; 41 struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc); 42 unsigned int tmp, crtc_id = loongson_crtc->crtc_id; 43 44 base = ldev->mmio; 45 tmp = readl(base + FB_CUR_CFG_REG); 46 tmp &= ~0xff; 47 if (clone_mode(ldev)) { 48 spin_lock_irqsave(&loongson_reglock, flags); 49 writel(tmp | 0x00, base + FB_CUR_CFG_REG); 50 spin_unlock_irqrestore(&loongson_reglock, flags); 51 ldev->cursor_showed = false; 52 } else { 53 if (ldev->cursor_crtc_id != crtc_id) 54 return; 55 56 spin_lock_irqsave(&loongson_reglock, flags); 57 if (crtc_id) { 58 writel(tmp | 0x10, base + FB_CUR_CFG_REG); 59 } else { 60 writel(tmp | 0x00, base + FB_CUR_CFG_REG); 61 } 62 spin_unlock_irqrestore(&loongson_reglock, flags); 63 ldev->cursor_showed = false; 64 } 65} 66 67static void loongson_show_cursor(struct drm_crtc *crtc) 68{ 69 unsigned long flags; 70 volatile void __iomem *base; 71 struct drm_device *dev = crtc->dev; 72 struct loongson_drm_device *ldev = (struct loongson_drm_device *)dev->dev_private; 73 struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc); 74 unsigned int crtc_id = loongson_crtc->crtc_id; 75 76 base = ldev->mmio; 77 if (clone_mode(ldev)) { 78 spin_lock_irqsave(&loongson_reglock, flags); 79 writel(0x00050202, base + FB_CUR_CFG_REG); 80 spin_unlock_irqrestore(&loongson_reglock, flags); 81 ldev->cursor_crtc_id = 0; 82 ldev->cursor_showed = true; 83 } else { 84 if (ldev->cursor_crtc_id == crtc_id) { 85 spin_lock_irqsave(&loongson_reglock, flags); 86 if(crtc_id == 0){ 87 writel(0x00050202, base + FB_CUR_CFG_REG); 88 }else{ 89 writel(0x00050212, base + FB_CUR_CFG_REG); 90 } 91 spin_unlock_irqrestore(&loongson_reglock, flags); 92 93 ldev->cursor_showed = true; 94 ldev->cursor_crtc_id = crtc_id; 95 } 96 } 97} 98 99int loongson_crtc_cursor_set2(struct drm_crtc *crtc, 100 struct drm_file *file_priv, 101 uint32_t handle, 102 uint32_t width, 103 uint32_t height, 104 int32_t hot_x, int32_t hot_y) 105{ 106 u32 gpu_addr; 107 unsigned long flags; 108 unsigned int crtc_id; 109 volatile void __iomem *base; 110 struct drm_gem_object *obj; 111 struct drm_device *dev = crtc->dev; 112 struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc); 113 struct loongson_drm_device *ldev = (struct loongson_drm_device *)dev->dev_private; 114 struct drm_gem_cma_object *cma, *cursor = ldev->cursor; 115 116 base = ldev->mmio; 117 crtc_id = loongson_crtc->crtc_id; 118 119 if ((width != 32 || height != 32) && handle) { 120 return -EINVAL; 121 } 122 123 if (!handle || !file_priv) { 124 loongson_hide_cursor(crtc); 125 return 0; 126 } 127 128 obj = drm_gem_object_lookup(file_priv, handle); 129 if (!obj) 130 return -ENOENT; 131 132 cma = to_drm_gem_cma_obj(obj); 133 134 flush_scache_range(cma->vaddr, 32*32*4); 135 memcpy(cursor->vaddr, cma->vaddr, 32*32*4); 136 137 /* Program gpu address of cursor buffer */ 138 gpu_addr = ldev->cursor->paddr; 139 spin_lock_irqsave(&loongson_reglock, flags); 140 writel(gpu_addr, base + FB_CUR_ADDR_REG); 141 writel(0x00eeeeee, base + FB_CUR_BACK_REG); 142 writel(0x00aaaaaa, base + FB_CUR_FORE_REG); 143 spin_unlock_irqrestore(&loongson_reglock, flags); 144 145 loongson_show_cursor(crtc); 146 147 drm_gem_object_put(obj); 148 149 return 0; 150} 151 152int loongson_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) 153{ 154 unsigned long flags; 155 unsigned int tmp, crtc_id; 156 int xorign = 0, yorign = 0; 157 volatile void __iomem *base; 158 struct loongson_crtc *loongson_crtc = to_loongson_crtc(crtc); 159 struct loongson_drm_device *ldev = (struct loongson_drm_device *)crtc->dev->dev_private; 160 161 base = ldev->mmio; 162 crtc_id = loongson_crtc->crtc_id; 163 164 /* upper edge condition */ 165 yorign = y + crtc->y; 166 if (yorign < 0) 167 y = 0; 168 169 /* left edge conditon */ 170 xorign = x + crtc->x; 171 if (xorign < 0) 172 x = 0; 173 174 /* move from one crtc to another, check which crtc should he shown 175 * the x or y < 0, it means the cursor it out of current review, 176 * && xorign/ yorign > 0, it means the cursor is in the framebuffer 177 * but not in curren review */ 178 if ((x < 0 && xorign > 0) || (y < 0 && yorign > 0)) { 179 if(ldev->cursor_crtc_id == crtc_id && !clone_mode(ldev)) 180 /*the cursor is not show, so hide if the (x,y) is in active crtc*/ 181 loongson_hide_cursor(crtc); 182 return 0; 183 } 184 185 if (x < 0) 186 x = 0; 187 if (y < 0) 188 y = 0; 189 190 tmp = x & 0xffff; 191 tmp |= y << 16; 192 spin_lock_irqsave(&loongson_reglock, flags); 193 writel(tmp, base + FB_CUR_LOC_ADDR_REG); 194 spin_unlock_irqrestore(&loongson_reglock, flags); 195 if (ldev->cursor_crtc_id != crtc_id && !clone_mode(ldev)) { 196 ldev->cursor_crtc_id = crtc_id; 197 ldev->cursor_showed = false; 198 } 199 if (!ldev->cursor_showed) 200 loongson_show_cursor(crtc); 201 202 return 0; 203} 204