| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | |
| 3 | #include <linux/export.h> |
| 4 | #include <linux/limits.h> |
| 5 | #include <linux/minmax.h> |
| 6 | #include <linux/screen_info.h> |
| 7 | |
| 8 | #include <drm/drm_fourcc.h> |
| 9 | #include <drm/drm_print.h> |
| 10 | |
| 11 | #include "drm_sysfb_helper.h" |
| 12 | |
| 13 | static s64 drm_sysfb_get_validated_size0(struct drm_device *dev, const char *name, |
| 14 | u64 value, u64 max) |
| 15 | { |
| 16 | if (!value) { |
| 17 | drm_warn(dev, "%s of 0 not allowed\n" , name); |
| 18 | return -EINVAL; |
| 19 | } else if (value > min(max, S64_MAX)) { |
| 20 | drm_warn(dev, "%s of %llu exceeds maximum of %llu\n" , name, value, max); |
| 21 | return -EINVAL; |
| 22 | } |
| 23 | return value; |
| 24 | } |
| 25 | |
| 26 | int drm_sysfb_get_width_si(struct drm_device *dev, const struct screen_info *si) |
| 27 | { |
| 28 | return drm_sysfb_get_validated_int0(dev, name: "width" , value: si->lfb_width, U16_MAX); |
| 29 | } |
| 30 | EXPORT_SYMBOL(drm_sysfb_get_width_si); |
| 31 | |
| 32 | int drm_sysfb_get_height_si(struct drm_device *dev, const struct screen_info *si) |
| 33 | { |
| 34 | return drm_sysfb_get_validated_int0(dev, name: "height" , value: si->lfb_height, U16_MAX); |
| 35 | } |
| 36 | EXPORT_SYMBOL(drm_sysfb_get_height_si); |
| 37 | |
| 38 | struct resource *drm_sysfb_get_memory_si(struct drm_device *dev, |
| 39 | const struct screen_info *si, |
| 40 | struct resource *res) |
| 41 | { |
| 42 | ssize_t num; |
| 43 | |
| 44 | num = screen_info_resources(si, r: res, num: 1); |
| 45 | if (!num) { |
| 46 | drm_warn(dev, "memory resource not found\n" ); |
| 47 | return NULL; |
| 48 | } |
| 49 | |
| 50 | return res; |
| 51 | } |
| 52 | EXPORT_SYMBOL(drm_sysfb_get_memory_si); |
| 53 | |
| 54 | int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si, |
| 55 | const struct drm_format_info *format, |
| 56 | unsigned int width, unsigned int height, u64 size) |
| 57 | { |
| 58 | u64 lfb_linelength = si->lfb_linelength; |
| 59 | |
| 60 | if (!lfb_linelength) |
| 61 | lfb_linelength = drm_format_info_min_pitch(info: format, plane: 0, buffer_width: width); |
| 62 | |
| 63 | return drm_sysfb_get_validated_int0(dev, name: "stride" , value: lfb_linelength, max: div64_u64(dividend: size, divisor: height)); |
| 64 | } |
| 65 | EXPORT_SYMBOL(drm_sysfb_get_stride_si); |
| 66 | |
| 67 | u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, |
| 68 | unsigned int height, unsigned int stride, u64 size) |
| 69 | { |
| 70 | u64 vsize = PAGE_ALIGN(height * stride); |
| 71 | |
| 72 | return drm_sysfb_get_validated_size0(dev, name: "visible size" , value: vsize, max: size); |
| 73 | } |
| 74 | EXPORT_SYMBOL(drm_sysfb_get_visible_size_si); |
| 75 | |
| 76 | const struct drm_format_info *drm_sysfb_get_format_si(struct drm_device *dev, |
| 77 | const struct drm_sysfb_format *formats, |
| 78 | size_t nformats, |
| 79 | const struct screen_info *si) |
| 80 | { |
| 81 | const struct drm_format_info *format = NULL; |
| 82 | struct pixel_format pixel; |
| 83 | size_t i; |
| 84 | int ret; |
| 85 | |
| 86 | ret = screen_info_pixel_format(si, f: &pixel); |
| 87 | if (ret) |
| 88 | return NULL; |
| 89 | |
| 90 | for (i = 0; i < nformats; ++i) { |
| 91 | const struct drm_sysfb_format *f = &formats[i]; |
| 92 | |
| 93 | if (pixel_format_equal(lhs: &pixel, rhs: &f->pixel)) { |
| 94 | format = drm_format_info(format: f->fourcc); |
| 95 | break; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | if (!format) |
| 100 | drm_warn(dev, "No compatible color format found\n" ); |
| 101 | |
| 102 | return format; |
| 103 | } |
| 104 | EXPORT_SYMBOL(drm_sysfb_get_format_si); |
| 105 | |