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(int src, int dst, int *clip) 56 { 57 u64 tmp; 58 59 if (dst == 0) 60 return 0; 61 62 /* Only clip what we have. Keeps the result bounded. */ 63 *clip = min(*clip, dst); 64 65 tmp = mul_u32_u32(src, dst - *clip); 66 67 /* 68 * Round toward 1.0 when clipping so that we don't accidentally 69 * change upscaling to downscaling or vice versa. 70 */ 71 if (src < (dst << 16)) 72 return DIV_ROUND_UP_ULL(tmp, dst); 73 else 74 return DIV_ROUND_DOWN_ULL(tmp, dst); 75 } 76 77 /** 78 * drm_rect_clip_scaled - perform a scaled clip operation 79 * @src: source window rectangle 80 * @dst: destination window rectangle 81 * @clip: clip rectangle 82 * 83 * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by 84 * the corresponding amounts, retaining the vertical and horizontal scaling 85 * factors from @src to @dst. 86 * 87 * RETURNS: 88 * %true if rectangle @dst is still visible after being clipped, 89 * %false otherwise. 90 */ 91 bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, 92 const struct drm_rect *clip) 93 { 94 int diff; 95 96 diff = clip->x1 - dst->x1; 97 if (diff > 0) { 98 u32 new_src_w = clip_scaled(drm_rect_width(src), 99 drm_rect_width(dst), &diff); 100 101 src->x1 = src->x2 - new_src_w; 102 dst->x1 += diff; 103 } 104 diff = clip->y1 - dst->y1; 105 if (diff > 0) { 106 u32 new_src_h = clip_scaled(drm_rect_height(src), 107 drm_rect_height(dst), &diff); 108 109 src->y1 = src->y2 - new_src_h; 110 dst->y1 += diff; 111 } 112 diff = dst->x2 - clip->x2; 113 if (diff > 0) { 114 u32 new_src_w = clip_scaled(drm_rect_width(src), 115 drm_rect_width(dst), &diff); 116 117 src->x2 = src->x1 + new_src_w; 118 dst->x2 -= diff; 119 } 120 diff = dst->y2 - clip->y2; 121 if (diff > 0) { 122 u32 new_src_h = clip_scaled(drm_rect_height(src), 123 drm_rect_height(dst), &diff); 124 125 src->y2 = src->y1 + new_src_h; 126 dst->y2 -= diff; 127 } 128 129 return drm_rect_visible(dst); 130 } 131 EXPORT_SYMBOL(drm_rect_clip_scaled); 132 133 static int drm_calc_scale(int src, int dst) 134 { 135 int scale = 0; 136 137 if (WARN_ON(src < 0 || dst < 0)) 138 return -EINVAL; 139 140 if (dst == 0) 141 return 0; 142 143 if (src > (dst << 16)) 144 return DIV_ROUND_UP(src, dst); 145 else 146 scale = src / dst; 147 148 return scale; 149 } 150 151 /** 152 * drm_rect_calc_hscale - calculate the horizontal scaling factor 153 * @src: source window rectangle 154 * @dst: destination window rectangle 155 * @min_hscale: minimum allowed horizontal scaling factor 156 * @max_hscale: maximum allowed horizontal scaling factor 157 * 158 * Calculate the horizontal scaling factor as 159 * (@src width) / (@dst width). 160 * 161 * If the scale is below 1 << 16, round down. If the scale is above 162 * 1 << 16, round up. This will calculate the scale with the most 163 * pessimistic limit calculation. 164 * 165 * RETURNS: 166 * The horizontal scaling factor, or errno of out of limits. 167 */ 168 int drm_rect_calc_hscale(const struct drm_rect *src, 169 const struct drm_rect *dst, 170 int min_hscale, int max_hscale) 171 { 172 int src_w = drm_rect_width(src); 173 int dst_w = drm_rect_width(dst); 174 int hscale = drm_calc_scale(src_w, dst_w); 175 176 if (hscale < 0 || dst_w == 0) 177 return hscale; 178 179 if (hscale < min_hscale || hscale > max_hscale) 180 return -ERANGE; 181 182 return hscale; 183 } 184 EXPORT_SYMBOL(drm_rect_calc_hscale); 185 186 /** 187 * drm_rect_calc_vscale - calculate the vertical scaling factor 188 * @src: source window rectangle 189 * @dst: destination window rectangle 190 * @min_vscale: minimum allowed vertical scaling factor 191 * @max_vscale: maximum allowed vertical scaling factor 192 * 193 * Calculate the vertical scaling factor as 194 * (@src height) / (@dst height). 195 * 196 * If the scale is below 1 << 16, round down. If the scale is above 197 * 1 << 16, round up. This will calculate the scale with the most 198 * pessimistic limit calculation. 199 * 200 * RETURNS: 201 * The vertical scaling factor, or errno of out of limits. 202 */ 203 int drm_rect_calc_vscale(const struct drm_rect *src, 204 const struct drm_rect *dst, 205 int min_vscale, int max_vscale) 206 { 207 int src_h = drm_rect_height(src); 208 int dst_h = drm_rect_height(dst); 209 int vscale = drm_calc_scale(src_h, dst_h); 210 211 if (vscale < 0 || dst_h == 0) 212 return vscale; 213 214 if (vscale < min_vscale || vscale > max_vscale) 215 return -ERANGE; 216 217 return vscale; 218 } 219 EXPORT_SYMBOL(drm_rect_calc_vscale); 220 221 /** 222 * drm_rect_debug_print - print the rectangle information 223 * @prefix: prefix string 224 * @r: rectangle to print 225 * @fixed_point: rectangle is in 16.16 fixed point format 226 */ 227 void drm_rect_debug_print(const char *prefix, const struct drm_rect *r, bool fixed_point) 228 { 229 if (fixed_point) 230 DRM_DEBUG_KMS("%s" DRM_RECT_FP_FMT "\n", prefix, DRM_RECT_FP_ARG(r)); 231 else 232 DRM_DEBUG_KMS("%s" DRM_RECT_FMT "\n", prefix, DRM_RECT_ARG(r)); 233 } 234 EXPORT_SYMBOL(drm_rect_debug_print); 235 236 /** 237 * drm_rect_rotate - Rotate the rectangle 238 * @r: rectangle to be rotated 239 * @width: Width of the coordinate space 240 * @height: Height of the coordinate space 241 * @rotation: Transformation to be applied 242 * 243 * Apply @rotation to the coordinates of rectangle @r. 244 * 245 * @width and @height combined with @rotation define 246 * the location of the new origin. 247 * 248 * @width correcsponds to the horizontal and @height 249 * to the vertical axis of the untransformed coordinate 250 * space. 251 */ 252 void drm_rect_rotate(struct drm_rect *r, 253 int width, int height, 254 unsigned int rotation) 255 { 256 struct drm_rect tmp; 257 258 if (rotation & (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y)) { 259 tmp = *r; 260 261 if (rotation & DRM_MODE_REFLECT_X) { 262 r->x1 = width - tmp.x2; 263 r->x2 = width - tmp.x1; 264 } 265 266 if (rotation & DRM_MODE_REFLECT_Y) { 267 r->y1 = height - tmp.y2; 268 r->y2 = height - tmp.y1; 269 } 270 } 271 272 switch (rotation & DRM_MODE_ROTATE_MASK) { 273 case DRM_MODE_ROTATE_0: 274 break; 275 case DRM_MODE_ROTATE_90: 276 tmp = *r; 277 r->x1 = tmp.y1; 278 r->x2 = tmp.y2; 279 r->y1 = width - tmp.x2; 280 r->y2 = width - tmp.x1; 281 break; 282 case DRM_MODE_ROTATE_180: 283 tmp = *r; 284 r->x1 = width - tmp.x2; 285 r->x2 = width - tmp.x1; 286 r->y1 = height - tmp.y2; 287 r->y2 = height - tmp.y1; 288 break; 289 case DRM_MODE_ROTATE_270: 290 tmp = *r; 291 r->x1 = height - tmp.y2; 292 r->x2 = height - tmp.y1; 293 r->y1 = tmp.x1; 294 r->y2 = tmp.x2; 295 break; 296 default: 297 break; 298 } 299 } 300 EXPORT_SYMBOL(drm_rect_rotate); 301 302 /** 303 * drm_rect_rotate_inv - Inverse rotate the rectangle 304 * @r: rectangle to be rotated 305 * @width: Width of the coordinate space 306 * @height: Height of the coordinate space 307 * @rotation: Transformation whose inverse is to be applied 308 * 309 * Apply the inverse of @rotation to the coordinates 310 * of rectangle @r. 311 * 312 * @width and @height combined with @rotation define 313 * the location of the new origin. 314 * 315 * @width correcsponds to the horizontal and @height 316 * to the vertical axis of the original untransformed 317 * coordinate space, so that you never have to flip 318 * them when doing a rotatation and its inverse. 319 * That is, if you do :: 320 * 321 * drm_rect_rotate(&r, width, height, rotation); 322 * drm_rect_rotate_inv(&r, width, height, rotation); 323 * 324 * you will always get back the original rectangle. 325 */ 326 void drm_rect_rotate_inv(struct drm_rect *r, 327 int width, int height, 328 unsigned int rotation) 329 { 330 struct drm_rect tmp; 331 332 switch (rotation & DRM_MODE_ROTATE_MASK) { 333 case DRM_MODE_ROTATE_0: 334 break; 335 case DRM_MODE_ROTATE_90: 336 tmp = *r; 337 r->x1 = width - tmp.y2; 338 r->x2 = width - tmp.y1; 339 r->y1 = tmp.x1; 340 r->y2 = tmp.x2; 341 break; 342 case DRM_MODE_ROTATE_180: 343 tmp = *r; 344 r->x1 = width - tmp.x2; 345 r->x2 = width - tmp.x1; 346 r->y1 = height - tmp.y2; 347 r->y2 = height - tmp.y1; 348 break; 349 case DRM_MODE_ROTATE_270: 350 tmp = *r; 351 r->x1 = tmp.y1; 352 r->x2 = tmp.y2; 353 r->y1 = height - tmp.x2; 354 r->y2 = height - tmp.x1; 355 break; 356 default: 357 break; 358 } 359 360 if (rotation & (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y)) { 361 tmp = *r; 362 363 if (rotation & DRM_MODE_REFLECT_X) { 364 r->x1 = width - tmp.x2; 365 r->x2 = width - tmp.x1; 366 } 367 368 if (rotation & DRM_MODE_REFLECT_Y) { 369 r->y1 = height - tmp.y2; 370 r->y2 = height - tmp.y1; 371 } 372 } 373 } 374 EXPORT_SYMBOL(drm_rect_rotate_inv); 375