1 /* 2 * Copyright (C) 2011-2013 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 * SOFTWARE. 22 */ 23 24 #include <linux/errno.h> 25 #include <linux/export.h> 26 #include <linux/kernel.h> 27 28 #include <drm/drm_mode.h> 29 #include <drm/drm_print.h> 30 #include <drm/drm_rect.h> 31 32 /** 33 * drm_rect_intersect - intersect two rectangles 34 * @r1: first rectangle 35 * @r2: second rectangle 36 * 37 * Calculate the intersection of rectangles @r1 and @r2. 38 * @r1 will be overwritten with the intersection. 39 * 40 * RETURNS: 41 * %true if rectangle @r1 is still visible after the operation, 42 * %false otherwise. 43 */ 44 bool drm_rect_intersect(struct drm_rect *r1, const struct drm_rect *r2) 45 { 46 r1->x1 = max(r1->x1, r2->x1); 47 r1->y1 = max(r1->y1, r2->y1); 48 r1->x2 = min(r1->x2, r2->x2); 49 r1->y2 = min(r1->y2, r2->y2); 50 51 return drm_rect_visible(r1); 52 } 53 EXPORT_SYMBOL(drm_rect_intersect); 54 55 static u32 clip_scaled(u32 src, u32 dst, u32 clip) 56 { 57 u64 tmp = mul_u32_u32(src, dst - clip); 58 59 /* 60 * Round toward 1.0 when clipping so that we don't accidentally 61 * change upscaling to downscaling or vice versa. 62 */ 63 if (src < (dst << 16)) 64 return DIV_ROUND_UP_ULL(tmp, dst); 65 else 66 return DIV_ROUND_DOWN_ULL(tmp, dst); 67 } 68 69 /** 70 * drm_rect_clip_scaled - perform a scaled clip operation 71 * @src: source window rectangle 72 * @dst: destination window rectangle 73 * @clip: clip rectangle 74 * 75 * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by the 76 * same amounts multiplied by @hscale and @vscale. 77 * 78 * RETURNS: 79 * %true if rectangle @dst is still visible after being clipped, 80 * %false otherwise 81 */ 82 bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, 83 const struct drm_rect *clip) 84 { 85 int diff; 86 87 diff = clip->x1 - dst->x1; 88 if (diff > 0) { 89 u32 new_src_w = clip_scaled(drm_rect_width(src), 90 drm_rect_width(dst), diff); 91 92 src->x1 = clamp_t(int64_t, src->x2 - new_src_w, INT_MIN, INT_MAX); 93 dst->x1 = clip->x1; 94 } 95 diff = clip->y1 - dst->y1; 96 if (diff > 0) { 97 u32 new_src_h = clip_scaled(drm_rect_height(src), 98 drm_rect_height(dst), diff); 99 100 src->y1 = clamp_t(int64_t, src->y2 - new_src_h, INT_MIN, INT_MAX); 101 dst->y1 = clip->y1; 102 } 103 diff = dst->x2 - clip->x2; 104 if (diff > 0) { 105 u32 new_src_w = clip_scaled(drm_rect_width(src), 106 drm_rect_width(dst), diff); 107 108 src->x2 = clamp_t(int64_t, src->x1 + new_src_w, INT_MIN, INT_MAX); 109 dst->x2 = clip->x2; 110 } 111 diff = dst->y2 - clip->y2; 112 if (diff > 0) { 113 u32 new_src_h = clip_scaled(drm_rect_height(src), 114 drm_rect_height(dst), diff); 115 116 src->y2 = clamp_t(int64_t, src->y1 + new_src_h, INT_MIN, INT_MAX); 117 dst->y2 = clip->y2; 118 } 119 120 return drm_rect_visible(dst); 121 } 122 EXPORT_SYMBOL(drm_rect_clip_scaled); 123 124 static int drm_calc_scale(int src, int dst) 125 { 126 int scale = 0; 127 128 if (WARN_ON(src < 0 || dst < 0)) 129 return -EINVAL; 130 131 if (dst == 0) 132 return 0; 133 134 if (src > (dst << 16)) 135 return DIV_ROUND_UP(src, dst); 136 else 137 scale = src / dst; 138 139 return scale; 140 } 141 142 /** 143 * drm_rect_calc_hscale - calculate the horizontal scaling factor 144 * @src: source window rectangle 145 * @dst: destination window rectangle 146 * @min_hscale: minimum allowed horizontal scaling factor 147 * @max_hscale: maximum allowed horizontal scaling factor 148 * 149 * Calculate the horizontal scaling factor as 150 * (@src width) / (@dst width). 151 * 152 * If the scale is below 1 << 16, round down. If the scale is above 153 * 1 << 16, round up. This will calculate the scale with the most 154 * pessimistic limit calculation. 155 * 156 * RETURNS: 157 * The horizontal scaling factor, or errno of out of limits. 158 */ 159 int drm_rect_calc_hscale(const struct drm_rect *src, 160 const struct drm_rect *dst, 161 int min_hscale, int max_hscale) 162 { 163 int src_w = drm_rect_width(src); 164 int dst_w = drm_rect_width(dst); 165 int hscale = drm_calc_scale(src_w, dst_w); 166 167 if (hscale < 0 || dst_w == 0) 168 return hscale; 169 170 if (hscale < min_hscale || hscale > max_hscale) 171 return -ERANGE; 172 173 return hscale; 174 } 175 EXPORT_SYMBOL(drm_rect_calc_hscale); 176 177 /** 178 * drm_rect_calc_vscale - calculate the vertical scaling factor 179 * @src: source window rectangle 180 * @dst: destination window rectangle 181 * @min_vscale: minimum allowed vertical scaling factor 182 * @max_vscale: maximum allowed vertical scaling factor 183 * 184 * Calculate the vertical scaling factor as 185 * (@src height) / (@dst height). 186 * 187 * If the scale is below 1 << 16, round down. If the scale is above 188 * 1 << 16, round up. This will calculate the scale with the most 189 * pessimistic limit calculation. 190 * 191 * RETURNS: 192 * The vertical scaling factor, or errno of out of limits. 193 */ 194 int drm_rect_calc_vscale(const struct drm_rect *src, 195 const struct drm_rect *dst, 196 int min_vscale, int max_vscale) 197 { 198 int src_h = drm_rect_height(src); 199 int dst_h = drm_rect_height(dst); 200 int vscale = drm_calc_scale(src_h, dst_h); 201 202 if (vscale < 0 || dst_h == 0) 203 return vscale; 204 205 if (vscale < min_vscale || vscale > max_vscale) 206 return -ERANGE; 207 208 return vscale; 209 } 210 EXPORT_SYMBOL(drm_rect_calc_vscale); 211 212 /** 213 * drm_rect_debug_print - print the rectangle information 214 * @prefix: prefix string 215 * @r: rectangle to print 216 * @fixed_point: rectangle is in 16.16 fixed point format 217 */ 218 void drm_rect_debug_print(const char *prefix, const struct drm_rect *r, bool fixed_point) 219 { 220 if (fixed_point) 221 DRM_DEBUG_KMS("%s" DRM_RECT_FP_FMT "\n", prefix, DRM_RECT_FP_ARG(r)); 222 else 223 DRM_DEBUG_KMS("%s" DRM_RECT_FMT "\n", prefix, DRM_RECT_ARG(r)); 224 } 225 EXPORT_SYMBOL(drm_rect_debug_print); 226 227 /** 228 * drm_rect_rotate - Rotate the rectangle 229 * @r: rectangle to be rotated 230 * @width: Width of the coordinate space 231 * @height: Height of the coordinate space 232 * @rotation: Transformation to be applied 233 * 234 * Apply @rotation to the coordinates of rectangle @r. 235 * 236 * @width and @height combined with @rotation define 237 * the location of the new origin. 238 * 239 * @width correcsponds to the horizontal and @height 240 * to the vertical axis of the untransformed coordinate 241 * space. 242 */ 243 void drm_rect_rotate(struct drm_rect *r, 244 int width, int height, 245 unsigned int rotation) 246 { 247 struct drm_rect tmp; 248 249 if (rotation & (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y)) { 250 tmp = *r; 251 252 if (rotation & DRM_MODE_REFLECT_X) { 253 r->x1 = width - tmp.x2; 254 r->x2 = width - tmp.x1; 255 } 256 257 if (rotation & DRM_MODE_REFLECT_Y) { 258 r->y1 = height - tmp.y2; 259 r->y2 = height - tmp.y1; 260 } 261 } 262 263 switch (rotation & DRM_MODE_ROTATE_MASK) { 264 case DRM_MODE_ROTATE_0: 265 break; 266 case DRM_MODE_ROTATE_90: 267 tmp = *r; 268 r->x1 = tmp.y1; 269 r->x2 = tmp.y2; 270 r->y1 = width - tmp.x2; 271 r->y2 = width - tmp.x1; 272 break; 273 case DRM_MODE_ROTATE_180: 274 tmp = *r; 275 r->x1 = width - tmp.x2; 276 r->x2 = width - tmp.x1; 277 r->y1 = height - tmp.y2; 278 r->y2 = height - tmp.y1; 279 break; 280 case DRM_MODE_ROTATE_270: 281 tmp = *r; 282 r->x1 = height - tmp.y2; 283 r->x2 = height - tmp.y1; 284 r->y1 = tmp.x1; 285 r->y2 = tmp.x2; 286 break; 287 default: 288 break; 289 } 290 } 291 EXPORT_SYMBOL(drm_rect_rotate); 292 293 /** 294 * drm_rect_rotate_inv - Inverse rotate the rectangle 295 * @r: rectangle to be rotated 296 * @width: Width of the coordinate space 297 * @height: Height of the coordinate space 298 * @rotation: Transformation whose inverse is to be applied 299 * 300 * Apply the inverse of @rotation to the coordinates 301 * of rectangle @r. 302 * 303 * @width and @height combined with @rotation define 304 * the location of the new origin. 305 * 306 * @width correcsponds to the horizontal and @height 307 * to the vertical axis of the original untransformed 308 * coordinate space, so that you never have to flip 309 * them when doing a rotatation and its inverse. 310 * That is, if you do :: 311 * 312 * drm_rect_rotate(&r, width, height, rotation); 313 * drm_rect_rotate_inv(&r, width, height, rotation); 314 * 315 * you will always get back the original rectangle. 316 */ 317 void drm_rect_rotate_inv(struct drm_rect *r, 318 int width, int height, 319 unsigned int rotation) 320 { 321 struct drm_rect tmp; 322 323 switch (rotation & DRM_MODE_ROTATE_MASK) { 324 case DRM_MODE_ROTATE_0: 325 break; 326 case DRM_MODE_ROTATE_90: 327 tmp = *r; 328 r->x1 = width - tmp.y2; 329 r->x2 = width - tmp.y1; 330 r->y1 = tmp.x1; 331 r->y2 = tmp.x2; 332 break; 333 case DRM_MODE_ROTATE_180: 334 tmp = *r; 335 r->x1 = width - tmp.x2; 336 r->x2 = width - tmp.x1; 337 r->y1 = height - tmp.y2; 338 r->y2 = height - tmp.y1; 339 break; 340 case DRM_MODE_ROTATE_270: 341 tmp = *r; 342 r->x1 = tmp.y1; 343 r->x2 = tmp.y2; 344 r->y1 = height - tmp.x2; 345 r->y2 = height - tmp.x1; 346 break; 347 default: 348 break; 349 } 350 351 if (rotation & (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y)) { 352 tmp = *r; 353 354 if (rotation & DRM_MODE_REFLECT_X) { 355 r->x1 = width - tmp.x2; 356 r->x2 = width - tmp.x1; 357 } 358 359 if (rotation & DRM_MODE_REFLECT_Y) { 360 r->y1 = height - tmp.y2; 361 r->y2 = height - tmp.y1; 362 } 363 } 364 } 365 EXPORT_SYMBOL(drm_rect_rotate_inv); 366