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(r: 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(a: src, b: 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(src: drm_rect_width(r: src), |
100 | dst: drm_rect_width(r: dst), clip: &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(src: drm_rect_height(r: src), |
108 | dst: drm_rect_height(r: dst), clip: &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(src: drm_rect_width(r: src), |
116 | dst: drm_rect_width(r: dst), clip: &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(src: drm_rect_height(r: src), |
124 | dst: drm_rect_height(r: dst), clip: &diff); |
125 | |
126 | src->y2 = src->y1 + new_src_h; |
127 | dst->y2 -= diff; |
128 | } |
129 | |
130 | return drm_rect_visible(r: 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(r: src); |
174 | int dst_w = drm_rect_width(r: dst); |
175 | int hscale = drm_calc_scale(src: src_w, dst: 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(r: src); |
209 | int dst_h = drm_rect_height(r: dst); |
210 | int vscale = drm_calc_scale(src: src_h, dst: 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 | |