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