diff options
Diffstat (limited to 'drivers/gpu/drm/drm_rect.c')
| -rw-r--r-- | drivers/gpu/drm/drm_rect.c | 303 |
1 files changed, 191 insertions, 112 deletions
diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index 7047ca025787..492acce0516f 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -24,7 +24,9 @@ #include <linux/errno.h> #include <linux/export.h> #include <linux/kernel.h> -#include <drm/drmP.h> + +#include <drm/drm_mode.h> +#include <drm/drm_print.h> #include <drm/drm_rect.h> /** @@ -50,49 +52,81 @@ bool drm_rect_intersect(struct drm_rect *r1, const struct drm_rect *r2) } EXPORT_SYMBOL(drm_rect_intersect); +static u32 clip_scaled(int src, int dst, int *clip) +{ + u64 tmp; + + if (dst == 0) + return 0; + + /* Only clip what we have. Keeps the result bounded. */ + *clip = min(*clip, dst); + + tmp = mul_u32_u32(src, dst - *clip); + + /* + * Round toward 1.0 when clipping so that we don't accidentally + * change upscaling to downscaling or vice versa. + */ + if (src < (dst << 16)) + return DIV_ROUND_UP_ULL(tmp, dst); + else + return DIV_ROUND_DOWN_ULL(tmp, dst); +} + /** * drm_rect_clip_scaled - perform a scaled clip operation * @src: source window rectangle * @dst: destination window rectangle * @clip: clip rectangle - * @hscale: horizontal scaling factor - * @vscale: vertical scaling factor * - * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by the - * same amounts multiplied by @hscale and @vscale. + * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by + * the corresponding amounts, retaining the vertical and horizontal scaling + * factors from @src to @dst. * * RETURNS: * %true if rectangle @dst is still visible after being clipped, - * %false otherwise + * %false otherwise. */ bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, - const struct drm_rect *clip, - int hscale, int vscale) + const struct drm_rect *clip) { int diff; diff = clip->x1 - dst->x1; if (diff > 0) { - int64_t tmp = src->x1 + (int64_t) diff * hscale; - src->x1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + u32 new_src_w = clip_scaled(drm_rect_width(src), + drm_rect_width(dst), &diff); + + src->x1 = src->x2 - new_src_w; + dst->x1 += diff; } diff = clip->y1 - dst->y1; if (diff > 0) { - int64_t tmp = src->y1 + (int64_t) diff * vscale; - src->y1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + u32 new_src_h = clip_scaled(drm_rect_height(src), + drm_rect_height(dst), &diff); + + src->y1 = src->y2 - new_src_h; + dst->y1 += diff; } diff = dst->x2 - clip->x2; if (diff > 0) { - int64_t tmp = src->x2 - (int64_t) diff * hscale; - src->x2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + u32 new_src_w = clip_scaled(drm_rect_width(src), + drm_rect_width(dst), &diff); + + src->x2 = src->x1 + new_src_w; + dst->x2 -= diff; } diff = dst->y2 - clip->y2; if (diff > 0) { - int64_t tmp = src->y2 - (int64_t) diff * vscale; - src->y2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + u32 new_src_h = clip_scaled(drm_rect_height(src), + drm_rect_height(dst), &diff); + + src->y2 = src->y1 + new_src_h; + dst->y2 -= diff; } - return drm_rect_intersect(dst, clip); + return drm_rect_visible(dst); } EXPORT_SYMBOL(drm_rect_clip_scaled); @@ -100,13 +134,16 @@ static int drm_calc_scale(int src, int dst) { int scale = 0; - if (src < 0 || dst < 0) + if (WARN_ON(src < 0 || dst < 0)) return -EINVAL; if (dst == 0) return 0; - scale = src / dst; + if (src > (dst << 16)) + return DIV_ROUND_UP(src, dst); + else + scale = src / dst; return scale; } @@ -121,6 +158,10 @@ static int drm_calc_scale(int src, int dst) * Calculate the horizontal scaling factor as * (@src width) / (@dst width). * + * If the scale is below 1 << 16, round down. If the scale is above + * 1 << 16, round up. This will calculate the scale with the most + * pessimistic limit calculation. + * * RETURNS: * The horizontal scaling factor, or errno of out of limits. */ @@ -152,6 +193,10 @@ EXPORT_SYMBOL(drm_rect_calc_hscale); * Calculate the vertical scaling factor as * (@src height) / (@dst height). * + * If the scale is below 1 << 16, round down. If the scale is above + * 1 << 16, round up. This will calculate the scale with the most + * pessimistic limit calculation. + * * RETURNS: * The vertical scaling factor, or errno of out of limits. */ @@ -174,122 +219,156 @@ int drm_rect_calc_vscale(const struct drm_rect *src, EXPORT_SYMBOL(drm_rect_calc_vscale); /** - * drm_calc_hscale_relaxed - calculate the horizontal scaling factor - * @src: source window rectangle - * @dst: destination window rectangle - * @min_hscale: minimum allowed horizontal scaling factor - * @max_hscale: maximum allowed horizontal scaling factor - * - * Calculate the horizontal scaling factor as - * (@src width) / (@dst width). + * drm_rect_debug_print - print the rectangle information + * @prefix: prefix string + * @r: rectangle to print + * @fixed_point: rectangle is in 16.16 fixed point format + */ +void drm_rect_debug_print(const char *prefix, const struct drm_rect *r, bool fixed_point) +{ + if (fixed_point) + DRM_DEBUG_KMS("%s" DRM_RECT_FP_FMT "\n", prefix, DRM_RECT_FP_ARG(r)); + else + DRM_DEBUG_KMS("%s" DRM_RECT_FMT "\n", prefix, DRM_RECT_ARG(r)); +} +EXPORT_SYMBOL(drm_rect_debug_print); + +/** + * drm_rect_rotate - Rotate the rectangle + * @r: rectangle to be rotated + * @width: Width of the coordinate space + * @height: Height of the coordinate space + * @rotation: Transformation to be applied * - * If the calculated scaling factor is below @min_vscale, - * decrease the height of rectangle @dst to compensate. + * Apply @rotation to the coordinates of rectangle @r. * - * If the calculated scaling factor is above @max_vscale, - * decrease the height of rectangle @src to compensate. + * @width and @height combined with @rotation define + * the location of the new origin. * - * RETURNS: - * The horizontal scaling factor. + * @width correcsponds to the horizontal and @height + * to the vertical axis of the untransformed coordinate + * space. */ -int drm_rect_calc_hscale_relaxed(struct drm_rect *src, - struct drm_rect *dst, - int min_hscale, int max_hscale) +void drm_rect_rotate(struct drm_rect *r, + int width, int height, + unsigned int rotation) { - int src_w = drm_rect_width(src); - int dst_w = drm_rect_width(dst); - int hscale = drm_calc_scale(src_w, dst_w); + struct drm_rect tmp; - if (hscale < 0 || dst_w == 0) - return hscale; + if (rotation & (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y)) { + tmp = *r; - if (hscale < min_hscale) { - int max_dst_w = src_w / min_hscale; + if (rotation & DRM_MODE_REFLECT_X) { + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + } - drm_rect_adjust_size(dst, max_dst_w - dst_w, 0); - - return min_hscale; + if (rotation & DRM_MODE_REFLECT_Y) { + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + } } - if (hscale > max_hscale) { - int max_src_w = dst_w * max_hscale; - - drm_rect_adjust_size(src, max_src_w - src_w, 0); - - return max_hscale; + switch (rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: + break; + case DRM_MODE_ROTATE_90: + tmp = *r; + r->x1 = tmp.y1; + r->x2 = tmp.y2; + r->y1 = width - tmp.x2; + r->y2 = width - tmp.x1; + break; + case DRM_MODE_ROTATE_180: + tmp = *r; + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + break; + case DRM_MODE_ROTATE_270: + tmp = *r; + r->x1 = height - tmp.y2; + r->x2 = height - tmp.y1; + r->y1 = tmp.x1; + r->y2 = tmp.x2; + break; + default: + break; } - - return hscale; } -EXPORT_SYMBOL(drm_rect_calc_hscale_relaxed); +EXPORT_SYMBOL(drm_rect_rotate); /** - * drm_rect_calc_vscale_relaxed - calculate the vertical scaling factor - * @src: source window rectangle - * @dst: destination window rectangle - * @min_vscale: minimum allowed vertical scaling factor - * @max_vscale: maximum allowed vertical scaling factor + * drm_rect_rotate_inv - Inverse rotate the rectangle + * @r: rectangle to be rotated + * @width: Width of the coordinate space + * @height: Height of the coordinate space + * @rotation: Transformation whose inverse is to be applied * - * Calculate the vertical scaling factor as - * (@src height) / (@dst height). + * Apply the inverse of @rotation to the coordinates + * of rectangle @r. * - * If the calculated scaling factor is below @min_vscale, - * decrease the height of rectangle @dst to compensate. + * @width and @height combined with @rotation define + * the location of the new origin. * - * If the calculated scaling factor is above @max_vscale, - * decrease the height of rectangle @src to compensate. + * @width correcsponds to the horizontal and @height + * to the vertical axis of the original untransformed + * coordinate space, so that you never have to flip + * them when doing a rotatation and its inverse. + * That is, if you do :: * - * RETURNS: - * The vertical scaling factor. + * drm_rect_rotate(&r, width, height, rotation); + * drm_rect_rotate_inv(&r, width, height, rotation); + * + * you will always get back the original rectangle. */ -int drm_rect_calc_vscale_relaxed(struct drm_rect *src, - struct drm_rect *dst, - int min_vscale, int max_vscale) +void drm_rect_rotate_inv(struct drm_rect *r, + int width, int height, + unsigned int rotation) { - int src_h = drm_rect_height(src); - int dst_h = drm_rect_height(dst); - int vscale = drm_calc_scale(src_h, dst_h); - - if (vscale < 0 || dst_h == 0) - return vscale; - - if (vscale < min_vscale) { - int max_dst_h = src_h / min_vscale; - - drm_rect_adjust_size(dst, 0, max_dst_h - dst_h); - - return min_vscale; + struct drm_rect tmp; + + switch (rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: + break; + case DRM_MODE_ROTATE_90: + tmp = *r; + r->x1 = width - tmp.y2; + r->x2 = width - tmp.y1; + r->y1 = tmp.x1; + r->y2 = tmp.x2; + break; + case DRM_MODE_ROTATE_180: + tmp = *r; + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + break; + case DRM_MODE_ROTATE_270: + tmp = *r; + r->x1 = tmp.y1; + r->x2 = tmp.y2; + r->y1 = height - tmp.x2; + r->y2 = height - tmp.x1; + break; + default: + break; } - if (vscale > max_vscale) { - int max_src_h = dst_h * max_vscale; + if (rotation & (DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y)) { + tmp = *r; - drm_rect_adjust_size(src, 0, max_src_h - src_h); + if (rotation & DRM_MODE_REFLECT_X) { + r->x1 = width - tmp.x2; + r->x2 = width - tmp.x1; + } - return max_vscale; + if (rotation & DRM_MODE_REFLECT_Y) { + r->y1 = height - tmp.y2; + r->y2 = height - tmp.y1; + } } - - return vscale; } -EXPORT_SYMBOL(drm_rect_calc_vscale_relaxed); - -/** - * drm_rect_debug_print - print the rectangle information - * @r: rectangle to print - * @fixed_point: rectangle is in 16.16 fixed point format - */ -void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point) -{ - int w = drm_rect_width(r); - int h = drm_rect_height(r); - - if (fixed_point) - DRM_DEBUG_KMS("%d.%06ux%d.%06u%+d.%06u%+d.%06u\n", - w >> 16, ((w & 0xffff) * 15625) >> 10, - h >> 16, ((h & 0xffff) * 15625) >> 10, - r->x1 >> 16, ((r->x1 & 0xffff) * 15625) >> 10, - r->y1 >> 16, ((r->y1 & 0xffff) * 15625) >> 10); - else - DRM_DEBUG_KMS("%dx%d%+d%+d\n", w, h, r->x1, r->y1); -} -EXPORT_SYMBOL(drm_rect_debug_print); +EXPORT_SYMBOL(drm_rect_rotate_inv); |
