xref: /openbsd/sys/dev/pci/drm/apple/dcp_backlight.c (revision 3bef86f7)
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