1 // SPDX-License-Identifier: GPL-2.0-only OR MIT 2 /* Copyright (C) The Asahi Linux Contributors */ 3 4 #include <drm/drm_atomic.h> 5 #include <drm/drm_crtc.h> 6 #include <drm/drm_drv.h> 7 #include <drm/drm_modeset_lock.h> 8 9 #include <linux/backlight.h> 10 #include <linux/completion.h> 11 #include <linux/delay.h> 12 #include "linux/jiffies.h" 13 14 #include "dcp.h" 15 #include "dcp-internal.h" 16 17 #define MIN_BRIGHTNESS_PART1 2U 18 #define MAX_BRIGHTNESS_PART1 99U 19 #define MIN_BRIGHTNESS_PART2 103U 20 #define MAX_BRIGHTNESS_PART2 510U 21 22 /* 23 * lookup for display brightness 2 to 99 nits 24 * */ 25 static u32 brightness_part1[] = { 26 0x0000000, 0x0810038, 0x0f000bd, 0x143011c, 27 0x1850165, 0x1bc01a1, 0x1eb01d4, 0x2140200, 28 0x2380227, 0x2590249, 0x2770269, 0x2930285, 29 0x2ac02a0, 0x2c402b8, 0x2d902cf, 0x2ee02e4, 30 0x30102f8, 0x314030b, 0x325031c, 0x335032d, 31 0x345033d, 0x354034d, 0x362035b, 0x3700369, 32 0x37d0377, 0x38a0384, 0x3960390, 0x3a2039c, 33 0x3ad03a7, 0x3b803b3, 0x3c303bd, 0x3cd03c8, 34 0x3d703d2, 0x3e103dc, 0x3ea03e5, 0x3f303ef, 35 0x3fc03f8, 0x4050400, 0x40d0409, 0x4150411, 36 0x41d0419, 0x4250421, 0x42d0429, 0x4340431, 37 0x43c0438, 0x443043f, 0x44a0446, 0x451044d, 38 0x4570454, 0x45e045b, 0x4640461, 0x46b0468, 39 0x471046e, 0x4770474, 0x47d047a, 0x4830480, 40 0x4890486, 0x48e048b, 0x4940491, 0x4990497, 41 0x49f049c, 0x4a404a1, 0x4a904a7, 0x4ae04ac, 42 0x4b304b1, 0x4b804b6, 0x4bd04bb, 0x4c204c0, 43 0x4c704c5, 0x4cc04c9, 0x4d004ce, 0x4d504d3, 44 0x4d904d7, 0x4de04dc, 0x4e204e0, 0x4e704e4, 45 0x4eb04e9, 0x4ef04ed, 0x4f304f1, 0x4f704f5, 46 0x4fb04f9, 0x4ff04fd, 0x5030501, 0x5070505, 47 0x50b0509, 0x50f050d, 0x5130511, 0x5160515, 48 0x51a0518, 0x51e051c, 0x5210520, 0x5250523, 49 0x5290527, 0x52c052a, 0x52f052e, 0x5330531, 50 0x5360535, 0x53a0538, 0x53d053b, 0x540053f, 51 0x5440542, 0x5470545, 0x54a0548, 0x54d054c, 52 0x550054f, 0x5530552, 0x5560555, 0x5590558, 53 0x55c055b, 0x55f055e, 0x5620561, 0x5650564, 54 0x5680567, 0x56b056a, 0x56e056d, 0x571056f, 55 0x5740572, 0x5760575, 0x5790578, 0x57c057b, 56 0x57f057d, 0x5810580, 0x5840583, 0x5870585, 57 0x5890588, 0x58c058b, 0x58f058d 58 }; 59 60 static u32 brightness_part12[] = { 0x58f058d, 0x59d058f }; 61 62 /* 63 * lookup table for display brightness 103.3 to 510 nits 64 * */ 65 static u32 brightness_part2[] = { 66 0x59d058f, 0x5b805ab, 0x5d105c5, 0x5e805dd, 67 0x5fe05f3, 0x6120608, 0x625061c, 0x637062e, 68 0x6480640, 0x6580650, 0x6680660, 0x677066f, 69 0x685067e, 0x693068c, 0x6a00699, 0x6ac06a6, 70 0x6b806b2, 0x6c406be, 0x6cf06ca, 0x6da06d5, 71 0x6e506df, 0x6ef06ea, 0x6f906f4, 0x70206fe, 72 0x70c0707, 0x7150710, 0x71e0719, 0x7260722, 73 0x72f072a, 0x7370733, 0x73f073b, 0x7470743, 74 0x74e074a, 0x7560752, 0x75d0759, 0x7640760, 75 0x76b0768, 0x772076e, 0x7780775, 0x77f077c, 76 0x7850782, 0x78c0789, 0x792078f, 0x7980795, 77 0x79e079b, 0x7a407a1, 0x7aa07a7, 0x7af07ac, 78 0x7b507b2, 0x7ba07b8, 0x7c007bd, 0x7c507c2, 79 0x7ca07c8, 0x7cf07cd, 0x7d407d2, 0x7d907d7, 80 0x7de07dc, 0x7e307e1, 0x7e807e5, 0x7ec07ea, 81 0x7f107ef, 0x7f607f3, 0x7fa07f8, 0x7fe07fc 82 }; 83 84 85 static int dcp_get_brightness(struct backlight_device *bd) 86 { 87 struct apple_dcp *dcp = bl_get_data(bd); 88 89 return dcp->brightness.nits; 90 } 91 92 #define SCALE_FACTOR (1 << 10) 93 94 static u32 interpolate(int val, int min, int max, u32 *tbl, size_t tbl_size) 95 { 96 u32 frac; 97 u64 low, high; 98 u32 interpolated = (tbl_size - 1) * ((val - min) * SCALE_FACTOR) / (max - min); 99 100 size_t index = interpolated / SCALE_FACTOR; 101 102 if (WARN(index + 1 >= tbl_size, "invalid index %zu for brightness %u\n", index, val)) 103 return tbl[tbl_size / 2]; 104 105 frac = interpolated & (SCALE_FACTOR - 1); 106 low = tbl[index]; 107 high = tbl[index + 1]; 108 109 return ((frac * high) + ((SCALE_FACTOR - frac) * low)) / SCALE_FACTOR; 110 } 111 112 static u32 calculate_dac(struct apple_dcp *dcp, int val) 113 { 114 u32 dac; 115 116 if (val <= MIN_BRIGHTNESS_PART1) 117 return 16 * brightness_part1[0]; 118 else if (val == MAX_BRIGHTNESS_PART1) 119 return 16 * brightness_part1[ARRAY_SIZE(brightness_part1) - 1]; 120 else if (val == MIN_BRIGHTNESS_PART2) 121 return 16 * brightness_part2[0]; 122 else if (val >= MAX_BRIGHTNESS_PART2) 123 return brightness_part2[ARRAY_SIZE(brightness_part2) - 1]; 124 125 if (val < MAX_BRIGHTNESS_PART1) { 126 dac = interpolate(val, MIN_BRIGHTNESS_PART1, MAX_BRIGHTNESS_PART1, 127 brightness_part1, ARRAY_SIZE(brightness_part1)); 128 } else if (val > MIN_BRIGHTNESS_PART2) { 129 dac = interpolate(val, MIN_BRIGHTNESS_PART2, MAX_BRIGHTNESS_PART2, 130 brightness_part2, ARRAY_SIZE(brightness_part2)); 131 } else { 132 dac = interpolate(val, MAX_BRIGHTNESS_PART1, MIN_BRIGHTNESS_PART2, 133 brightness_part12, ARRAY_SIZE(brightness_part12)); 134 } 135 136 return 16 * dac; 137 } 138 139 static int drm_crtc_set_brightness(struct apple_dcp *dcp) 140 { 141 struct drm_atomic_state *state; 142 struct drm_crtc_state *crtc_state; 143 struct drm_modeset_acquire_ctx ctx; 144 struct drm_crtc *crtc = &dcp->crtc->base; 145 int ret = 0; 146 147 DRM_MODESET_LOCK_ALL_BEGIN(crtc->dev, ctx, 0, ret); 148 149 if (!dcp->brightness.update) 150 goto done; 151 152 state = drm_atomic_state_alloc(crtc->dev); 153 if (!state) 154 return -ENOMEM; 155 156 state->acquire_ctx = &ctx; 157 crtc_state = drm_atomic_get_crtc_state(state, crtc); 158 if (IS_ERR(crtc_state)) { 159 ret = PTR_ERR(crtc_state); 160 goto fail; 161 } 162 163 crtc_state->color_mgmt_changed |= true; 164 165 ret = drm_atomic_commit(state); 166 167 fail: 168 drm_atomic_state_put(state); 169 done: 170 DRM_MODESET_LOCK_ALL_END(crtc->dev, ctx, ret); 171 172 return ret; 173 } 174 175 int dcp_backlight_update(struct apple_dcp *dcp) 176 { 177 /* 178 * Do not actively try to change brightness if no mode is set. 179 * TODO: should this be reflected the in backlight's power property? 180 * defer this hopefully until it becomes irrelevant due to proper 181 * drm integrated backlight handling 182 */ 183 if (!dcp->valid_mode) 184 return 0; 185 186 /* Wait 1 vblank cycle in the hope an atomic swap has already updated 187 * the brightness */ 188 drm_msleep((1001 + 23) / 24); // 42ms for 23.976 fps 189 190 return drm_crtc_set_brightness(dcp); 191 } 192 193 static int dcp_set_brightness(struct backlight_device *bd) 194 { 195 int ret = 0; 196 struct apple_dcp *dcp = bl_get_data(bd); 197 struct drm_modeset_acquire_ctx ctx; 198 int brightness = backlight_get_brightness(bd); 199 200 DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret); 201 202 dcp->brightness.dac = calculate_dac(dcp, brightness); 203 dcp->brightness.update = true; 204 205 DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret); 206 207 return dcp_backlight_update(dcp); 208 } 209 210 static const struct backlight_ops dcp_backlight_ops = { 211 .options = BL_CORE_SUSPENDRESUME, 212 .get_brightness = dcp_get_brightness, 213 .update_status = dcp_set_brightness, 214 }; 215 216 int dcp_backlight_register(struct apple_dcp *dcp) 217 { 218 struct device *dev = dcp->dev; 219 struct backlight_device *bl_dev; 220 struct backlight_properties props = { 221 .type = BACKLIGHT_PLATFORM, 222 .brightness = dcp->brightness.nits, 223 .scale = BACKLIGHT_SCALE_LINEAR, 224 }; 225 props.max_brightness = min(dcp->brightness.maximum, MAX_BRIGHTNESS_PART2 - 1); 226 227 bl_dev = devm_backlight_device_register(dev, "apple-panel-bl", dev, dcp, 228 &dcp_backlight_ops, &props); 229 if (IS_ERR(bl_dev)) 230 return PTR_ERR(bl_dev); 231 232 dcp->brightness.bl_dev = bl_dev; 233 dcp->brightness.dac = calculate_dac(dcp, dcp->brightness.nits); 234 235 return 0; 236 } 237