Daniel Vetter | f1e2f66 | 2016-09-21 10:59:28 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2016 Intel Corporation |
| 3 | * |
| 4 | * Permission to use, copy, modify, distribute, and sell this software and its |
| 5 | * documentation for any purpose is hereby granted without fee, provided that |
| 6 | * the above copyright notice appear in all copies and that both that copyright |
| 7 | * notice and this permission notice appear in supporting documentation, and |
| 8 | * that the name of the copyright holders not be used in advertising or |
| 9 | * publicity pertaining to distribution of the software without specific, |
| 10 | * written prior permission. The copyright holders make no representations |
| 11 | * about the suitability of this software for any purpose. It is provided "as |
| 12 | * is" without express or implied warranty. |
| 13 | * |
| 14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| 15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
| 16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
| 17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
| 18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| 19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
| 20 | * OF THIS SOFTWARE. |
| 21 | */ |
| 22 | |
| 23 | #include <drm/drmP.h> |
| 24 | #include <drm/drm_crtc.h> |
| 25 | #include <drm/drm_color_mgmt.h> |
| 26 | |
| 27 | #include "drm_crtc_internal.h" |
| 28 | |
Daniel Vetter | a6acccf | 2016-09-21 10:59:29 +0200 | [diff] [blame] | 29 | /** |
| 30 | * DOC: overview |
| 31 | * |
| 32 | * Color management or color space adjustments is supported through a set of 5 |
| 33 | * properties on the &drm_crtc object. They are set up by calling |
| 34 | * drm_crtc_enable_color_mgmt(). |
| 35 | * |
| 36 | * "DEGAMMA_LUT”: |
| 37 | * Blob property to set the degamma lookup table (LUT) mapping pixel data |
| 38 | * from the framebuffer before it is given to the transformation matrix. |
| 39 | * The data is interpreted as an array of struct &drm_color_lut elements. |
| 40 | * Hardware might choose not to use the full precision of the LUT elements |
| 41 | * nor use all the elements of the LUT (for example the hardware might |
| 42 | * choose to interpolate between LUT[0] and LUT[4]). |
| 43 | * |
| 44 | * “DEGAMMA_LUT_SIZE”: |
| 45 | * Unsinged range property to give the size of the lookup table to be set |
| 46 | * on the DEGAMMA_LUT property (the size depends on the underlying |
| 47 | * hardware). If drivers support multiple LUT sizes then they should |
| 48 | * publish the largest size, and sub-sample smaller sized LUTs (e.g. for |
| 49 | * split-gamma modes) appropriately. |
| 50 | * |
| 51 | * “CTM”: |
| 52 | * Blob property to set the current transformation matrix (CTM) apply to |
| 53 | * pixel data after the lookup through the degamma LUT and before the |
| 54 | * lookup through the gamma LUT. The data is interpreted as a struct |
| 55 | * &drm_color_ctm. |
| 56 | * |
| 57 | * “GAMMA_LUT”: |
| 58 | * Blob property to set the gamma lookup table (LUT) mapping pixel data |
| 59 | * after the transformation matrix to data sent to the connector. The |
| 60 | * data is interpreted as an array of struct &drm_color_lut elements. |
| 61 | * Hardware might choose not to use the full precision of the LUT elements |
| 62 | * nor use all the elements of the LUT (for example the hardware might |
| 63 | * choose to interpolate between LUT[0] and LUT[4]). |
| 64 | * |
| 65 | * “GAMMA_LUT_SIZE”: |
| 66 | * Unsigned range property to give the size of the lookup table to be set |
| 67 | * on the GAMMA_LUT property (the size depends on the underlying hardware). |
| 68 | * If drivers support multiple LUT sizes then they should publish the |
| 69 | * largest size, and sub-sample smaller sized LUTs (e.g. for split-gamma |
| 70 | * modes) appropriately. |
| 71 | * |
| 72 | * There is also support for a legacy gamma table, which is set up by calling |
| 73 | * drm_mode_crtc_set_gamma_size(). Drivers which support both should use |
| 74 | * drm_atomic_helper_legacy_gamma_set() to alias the legacy gamma ramp with the |
| 75 | * "GAMMA_LUT" property above. |
| 76 | */ |
Daniel Vetter | f1e2f66 | 2016-09-21 10:59:28 +0200 | [diff] [blame] | 77 | |
| 78 | /** |
| 79 | * drm_crtc_enable_color_mgmt - enable color management properties |
| 80 | * @crtc: DRM CRTC |
| 81 | * @degamma_lut_size: the size of the degamma lut (before CSC) |
| 82 | * @has_ctm: whether to attach ctm_property for CSC matrix |
| 83 | * @gamma_lut_size: the size of the gamma lut (after CSC) |
| 84 | * |
| 85 | * This function lets the driver enable the color correction |
| 86 | * properties on a CRTC. This includes 3 degamma, csc and gamma |
| 87 | * properties that userspace can set and 2 size properties to inform |
| 88 | * the userspace of the lut sizes. Each of the properties are |
| 89 | * optional. The gamma and degamma properties are only attached if |
| 90 | * their size is not 0 and ctm_property is only attached if has_ctm is |
| 91 | * true. |
| 92 | */ |
| 93 | void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc, |
| 94 | uint degamma_lut_size, |
| 95 | bool has_ctm, |
| 96 | uint gamma_lut_size) |
| 97 | { |
| 98 | struct drm_device *dev = crtc->dev; |
| 99 | struct drm_mode_config *config = &dev->mode_config; |
| 100 | |
| 101 | if (degamma_lut_size) { |
| 102 | drm_object_attach_property(&crtc->base, |
| 103 | config->degamma_lut_property, 0); |
| 104 | drm_object_attach_property(&crtc->base, |
| 105 | config->degamma_lut_size_property, |
| 106 | degamma_lut_size); |
| 107 | } |
| 108 | |
| 109 | if (has_ctm) |
| 110 | drm_object_attach_property(&crtc->base, |
| 111 | config->ctm_property, 0); |
| 112 | |
| 113 | if (gamma_lut_size) { |
| 114 | drm_object_attach_property(&crtc->base, |
| 115 | config->gamma_lut_property, 0); |
| 116 | drm_object_attach_property(&crtc->base, |
| 117 | config->gamma_lut_size_property, |
| 118 | gamma_lut_size); |
| 119 | } |
| 120 | } |
| 121 | EXPORT_SYMBOL(drm_crtc_enable_color_mgmt); |
| 122 | |
| 123 | /** |
| 124 | * drm_mode_crtc_set_gamma_size - set the gamma table size |
| 125 | * @crtc: CRTC to set the gamma table size for |
| 126 | * @gamma_size: size of the gamma table |
| 127 | * |
| 128 | * Drivers which support gamma tables should set this to the supported gamma |
| 129 | * table size when initializing the CRTC. Currently the drm core only supports a |
| 130 | * fixed gamma table size. |
| 131 | * |
| 132 | * Returns: |
| 133 | * Zero on success, negative errno on failure. |
| 134 | */ |
| 135 | int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, |
| 136 | int gamma_size) |
| 137 | { |
| 138 | uint16_t *r_base, *g_base, *b_base; |
| 139 | int i; |
| 140 | |
| 141 | crtc->gamma_size = gamma_size; |
| 142 | |
| 143 | crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3, |
| 144 | GFP_KERNEL); |
| 145 | if (!crtc->gamma_store) { |
| 146 | crtc->gamma_size = 0; |
| 147 | return -ENOMEM; |
| 148 | } |
| 149 | |
| 150 | r_base = crtc->gamma_store; |
| 151 | g_base = r_base + gamma_size; |
| 152 | b_base = g_base + gamma_size; |
| 153 | for (i = 0; i < gamma_size; i++) { |
| 154 | r_base[i] = i << 8; |
| 155 | g_base[i] = i << 8; |
| 156 | b_base[i] = i << 8; |
| 157 | } |
| 158 | |
| 159 | |
| 160 | return 0; |
| 161 | } |
| 162 | EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); |
| 163 | |
| 164 | /** |
| 165 | * drm_mode_gamma_set_ioctl - set the gamma table |
| 166 | * @dev: DRM device |
| 167 | * @data: ioctl data |
| 168 | * @file_priv: DRM file info |
| 169 | * |
| 170 | * Set the gamma table of a CRTC to the one passed in by the user. Userspace can |
| 171 | * inquire the required gamma table size through drm_mode_gamma_get_ioctl. |
| 172 | * |
| 173 | * Called by the user via ioctl. |
| 174 | * |
| 175 | * Returns: |
| 176 | * Zero on success, negative errno on failure. |
| 177 | */ |
| 178 | int drm_mode_gamma_set_ioctl(struct drm_device *dev, |
| 179 | void *data, struct drm_file *file_priv) |
| 180 | { |
| 181 | struct drm_mode_crtc_lut *crtc_lut = data; |
| 182 | struct drm_crtc *crtc; |
| 183 | void *r_base, *g_base, *b_base; |
| 184 | int size; |
| 185 | int ret = 0; |
| 186 | |
| 187 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
| 188 | return -EINVAL; |
| 189 | |
| 190 | drm_modeset_lock_all(dev); |
| 191 | crtc = drm_crtc_find(dev, crtc_lut->crtc_id); |
| 192 | if (!crtc) { |
| 193 | ret = -ENOENT; |
| 194 | goto out; |
| 195 | } |
| 196 | |
| 197 | if (crtc->funcs->gamma_set == NULL) { |
| 198 | ret = -ENOSYS; |
| 199 | goto out; |
| 200 | } |
| 201 | |
| 202 | /* memcpy into gamma store */ |
| 203 | if (crtc_lut->gamma_size != crtc->gamma_size) { |
| 204 | ret = -EINVAL; |
| 205 | goto out; |
| 206 | } |
| 207 | |
| 208 | size = crtc_lut->gamma_size * (sizeof(uint16_t)); |
| 209 | r_base = crtc->gamma_store; |
| 210 | if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { |
| 211 | ret = -EFAULT; |
| 212 | goto out; |
| 213 | } |
| 214 | |
| 215 | g_base = r_base + size; |
| 216 | if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { |
| 217 | ret = -EFAULT; |
| 218 | goto out; |
| 219 | } |
| 220 | |
| 221 | b_base = g_base + size; |
| 222 | if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { |
| 223 | ret = -EFAULT; |
| 224 | goto out; |
| 225 | } |
| 226 | |
| 227 | ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); |
| 228 | |
| 229 | out: |
| 230 | drm_modeset_unlock_all(dev); |
| 231 | return ret; |
| 232 | |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * drm_mode_gamma_get_ioctl - get the gamma table |
| 237 | * @dev: DRM device |
| 238 | * @data: ioctl data |
| 239 | * @file_priv: DRM file info |
| 240 | * |
| 241 | * Copy the current gamma table into the storage provided. This also provides |
| 242 | * the gamma table size the driver expects, which can be used to size the |
| 243 | * allocated storage. |
| 244 | * |
| 245 | * Called by the user via ioctl. |
| 246 | * |
| 247 | * Returns: |
| 248 | * Zero on success, negative errno on failure. |
| 249 | */ |
| 250 | int drm_mode_gamma_get_ioctl(struct drm_device *dev, |
| 251 | void *data, struct drm_file *file_priv) |
| 252 | { |
| 253 | struct drm_mode_crtc_lut *crtc_lut = data; |
| 254 | struct drm_crtc *crtc; |
| 255 | void *r_base, *g_base, *b_base; |
| 256 | int size; |
| 257 | int ret = 0; |
| 258 | |
| 259 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
| 260 | return -EINVAL; |
| 261 | |
| 262 | drm_modeset_lock_all(dev); |
| 263 | crtc = drm_crtc_find(dev, crtc_lut->crtc_id); |
| 264 | if (!crtc) { |
| 265 | ret = -ENOENT; |
| 266 | goto out; |
| 267 | } |
| 268 | |
| 269 | /* memcpy into gamma store */ |
| 270 | if (crtc_lut->gamma_size != crtc->gamma_size) { |
| 271 | ret = -EINVAL; |
| 272 | goto out; |
| 273 | } |
| 274 | |
| 275 | size = crtc_lut->gamma_size * (sizeof(uint16_t)); |
| 276 | r_base = crtc->gamma_store; |
| 277 | if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { |
| 278 | ret = -EFAULT; |
| 279 | goto out; |
| 280 | } |
| 281 | |
| 282 | g_base = r_base + size; |
| 283 | if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { |
| 284 | ret = -EFAULT; |
| 285 | goto out; |
| 286 | } |
| 287 | |
| 288 | b_base = g_base + size; |
| 289 | if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { |
| 290 | ret = -EFAULT; |
| 291 | goto out; |
| 292 | } |
| 293 | out: |
| 294 | drm_modeset_unlock_all(dev); |
| 295 | return ret; |
| 296 | } |