1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Dell AIO Serial Backlight Driver 4 * 5 * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> 6 * Copyright (C) 2017 AceLan Kao <acelan.kao@canonical.com> 7 */ 8 9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 11 #include <linux/acpi.h> 12 #include <linux/backlight.h> 13 #include <linux/delay.h> 14 #include <linux/device.h> 15 #include <linux/err.h> 16 #include <linux/module.h> 17 #include <linux/mutex.h> 18 #include <linux/platform_device.h> 19 #include <linux/serdev.h> 20 #include <linux/string.h> 21 #include <linux/types.h> 22 #include <linux/wait.h> 23 #include <acpi/video.h> 24 #include "../serdev_helpers.h" 25 26 /* The backlight controller must respond within 1 second */ 27 #define DELL_BL_TIMEOUT msecs_to_jiffies(1000) 28 #define DELL_BL_MAX_BRIGHTNESS 100 29 30 /* Defines for the commands send to the controller */ 31 32 /* 1st byte Start Of Frame 3 MSB bits: cmd-len + 01010 SOF marker */ 33 #define DELL_SOF(len) (((len) << 5) | 0x0a) 34 #define GET_CMD_LEN 3 35 #define SET_CMD_LEN 4 36 37 /* 2nd byte command */ 38 #define CMD_GET_VERSION 0x06 39 #define CMD_SET_BRIGHTNESS 0x0b 40 #define CMD_GET_BRIGHTNESS 0x0c 41 #define CMD_SET_BL_POWER 0x0e 42 43 /* Indexes and other defines for response received from the controller */ 44 #define RESP_LEN 0 45 #define RESP_CMD 1 /* Echo of CMD byte from command */ 46 #define RESP_DATA 2 /* Start of received data */ 47 48 #define SET_RESP_LEN 3 49 #define GET_RESP_LEN 4 50 #define MIN_RESP_LEN 3 51 #define MAX_RESP_LEN 80 52 53 struct dell_uart_backlight { 54 struct mutex mutex; 55 wait_queue_head_t wait_queue; 56 struct device *dev; 57 struct backlight_device *bl; 58 u8 *resp; 59 u8 resp_idx; 60 u8 resp_len; 61 u8 resp_max_len; 62 u8 pending_cmd; 63 int status; 64 int power; 65 }; 66 67 /* Checksum: SUM(Length and Cmd and Data) xor 0xFF */ 68 static u8 dell_uart_checksum(u8 *buf, int len) 69 { 70 u8 val = 0; 71 72 while (len-- > 0) 73 val += buf[len]; 74 75 return val ^ 0xff; 76 } 77 78 static int dell_uart_bl_command(struct dell_uart_backlight *dell_bl, 79 const u8 *cmd, int cmd_len, 80 u8 *resp, int resp_max_len) 81 { 82 int ret; 83 84 ret = mutex_lock_killable(&dell_bl->mutex); 85 if (ret) 86 return ret; 87 88 dell_bl->status = -EBUSY; 89 dell_bl->resp = resp; 90 dell_bl->resp_idx = 0; 91 dell_bl->resp_len = -1; /* Invalid / unset */ 92 dell_bl->resp_max_len = resp_max_len; 93 dell_bl->pending_cmd = cmd[1]; 94 95 /* The TTY buffer should be big enough to take the entire cmd in one go */ 96 ret = serdev_device_write_buf(to_serdev_device(dell_bl->dev), cmd, cmd_len); 97 if (ret != cmd_len) { 98 dev_err(dell_bl->dev, "Error writing command: %d\n", ret); 99 dell_bl->status = (ret < 0) ? ret : -EIO; 100 goto out; 101 } 102 103 ret = wait_event_timeout(dell_bl->wait_queue, dell_bl->status != -EBUSY, 104 DELL_BL_TIMEOUT); 105 if (ret == 0) { 106 dev_err(dell_bl->dev, "Timed out waiting for response.\n"); 107 /* Clear busy status to discard bytes received after this */ 108 dell_bl->status = -ETIMEDOUT; 109 } 110 111 out: 112 mutex_unlock(&dell_bl->mutex); 113 return dell_bl->status; 114 } 115 116 static int dell_uart_set_brightness(struct dell_uart_backlight *dell_bl, int brightness) 117 { 118 u8 set_brightness[SET_CMD_LEN], resp[SET_RESP_LEN]; 119 120 set_brightness[0] = DELL_SOF(SET_CMD_LEN); 121 set_brightness[1] = CMD_SET_BRIGHTNESS; 122 set_brightness[2] = brightness; 123 set_brightness[3] = dell_uart_checksum(set_brightness, 3); 124 125 return dell_uart_bl_command(dell_bl, set_brightness, SET_CMD_LEN, resp, SET_RESP_LEN); 126 } 127 128 static int dell_uart_get_brightness(struct dell_uart_backlight *dell_bl) 129 { 130 struct device *dev = dell_bl->dev; 131 u8 get_brightness[GET_CMD_LEN], resp[GET_RESP_LEN]; 132 int ret; 133 134 get_brightness[0] = DELL_SOF(GET_CMD_LEN); 135 get_brightness[1] = CMD_GET_BRIGHTNESS; 136 get_brightness[2] = dell_uart_checksum(get_brightness, 2); 137 138 ret = dell_uart_bl_command(dell_bl, get_brightness, GET_CMD_LEN, resp, GET_RESP_LEN); 139 if (ret) 140 return ret; 141 142 if (resp[RESP_LEN] != GET_RESP_LEN) { 143 dev_err(dev, "Unexpected get brightness response length: %d\n", resp[RESP_LEN]); 144 return -EIO; 145 } 146 147 if (resp[RESP_DATA] > DELL_BL_MAX_BRIGHTNESS) { 148 dev_err(dev, "Unexpected get brightness response: %d\n", resp[RESP_DATA]); 149 return -EIO; 150 } 151 152 return resp[RESP_DATA]; 153 } 154 155 static int dell_uart_set_bl_power(struct dell_uart_backlight *dell_bl, int power) 156 { 157 u8 set_power[SET_CMD_LEN], resp[SET_RESP_LEN]; 158 int ret; 159 160 set_power[0] = DELL_SOF(SET_CMD_LEN); 161 set_power[1] = CMD_SET_BL_POWER; 162 set_power[2] = (power == FB_BLANK_UNBLANK) ? 1 : 0; 163 set_power[3] = dell_uart_checksum(set_power, 3); 164 165 ret = dell_uart_bl_command(dell_bl, set_power, SET_CMD_LEN, resp, SET_RESP_LEN); 166 if (ret) 167 return ret; 168 169 dell_bl->power = power; 170 return 0; 171 } 172 173 /* 174 * There is no command to get backlight power status, 175 * so we set the backlight power to "on" while initializing, 176 * and then track and report its status by power variable. 177 */ 178 static int dell_uart_get_bl_power(struct dell_uart_backlight *dell_bl) 179 { 180 return dell_bl->power; 181 } 182 183 static int dell_uart_update_status(struct backlight_device *bd) 184 { 185 struct dell_uart_backlight *dell_bl = bl_get_data(bd); 186 int ret; 187 188 ret = dell_uart_set_brightness(dell_bl, bd->props.brightness); 189 if (ret) 190 return ret; 191 192 if (bd->props.power != dell_uart_get_bl_power(dell_bl)) 193 return dell_uart_set_bl_power(dell_bl, bd->props.power); 194 195 return 0; 196 } 197 198 static int dell_uart_get_brightness_op(struct backlight_device *bd) 199 { 200 return dell_uart_get_brightness(bl_get_data(bd)); 201 } 202 203 static const struct backlight_ops dell_uart_backlight_ops = { 204 .update_status = dell_uart_update_status, 205 .get_brightness = dell_uart_get_brightness_op, 206 }; 207 208 static size_t dell_uart_bl_receive(struct serdev_device *serdev, const u8 *data, size_t len) 209 { 210 struct dell_uart_backlight *dell_bl = serdev_device_get_drvdata(serdev); 211 size_t i; 212 u8 csum; 213 214 dev_dbg(dell_bl->dev, "Recv: %*ph\n", (int)len, data); 215 216 /* Throw away unexpected bytes / remainder of response after an error */ 217 if (dell_bl->status != -EBUSY) { 218 dev_warn(dell_bl->dev, "Bytes received out of band, dropping them.\n"); 219 return len; 220 } 221 222 i = 0; 223 while (i < len && dell_bl->resp_idx != dell_bl->resp_len) { 224 dell_bl->resp[dell_bl->resp_idx] = data[i++]; 225 226 switch (dell_bl->resp_idx) { 227 case RESP_LEN: /* Length byte */ 228 dell_bl->resp_len = dell_bl->resp[RESP_LEN]; 229 if (dell_bl->resp_len < MIN_RESP_LEN || 230 dell_bl->resp_len > dell_bl->resp_max_len) { 231 dev_err(dell_bl->dev, "Response length %d out if range %d - %d\n", 232 dell_bl->resp_len, MIN_RESP_LEN, dell_bl->resp_max_len); 233 dell_bl->status = -EIO; 234 goto wakeup; 235 } 236 break; 237 case RESP_CMD: /* CMD byte */ 238 if (dell_bl->resp[RESP_CMD] != dell_bl->pending_cmd) { 239 dev_err(dell_bl->dev, "Response cmd 0x%02x != pending 0x%02x\n", 240 dell_bl->resp[RESP_CMD], dell_bl->pending_cmd); 241 dell_bl->status = -EIO; 242 goto wakeup; 243 } 244 break; 245 } 246 dell_bl->resp_idx++; 247 } 248 249 if (dell_bl->resp_idx != dell_bl->resp_len) 250 return len; /* Response not complete yet */ 251 252 csum = dell_uart_checksum(dell_bl->resp, dell_bl->resp_len - 1); 253 if (dell_bl->resp[dell_bl->resp_len - 1] == csum) { 254 dell_bl->status = 0; /* Success */ 255 } else { 256 dev_err(dell_bl->dev, "Checksum mismatch got 0x%02x expected 0x%02x\n", 257 dell_bl->resp[dell_bl->resp_len - 1], csum); 258 dell_bl->status = -EIO; 259 } 260 wakeup: 261 wake_up(&dell_bl->wait_queue); 262 return i; 263 } 264 265 static const struct serdev_device_ops dell_uart_bl_serdev_ops = { 266 .receive_buf = dell_uart_bl_receive, 267 .write_wakeup = serdev_device_write_wakeup, 268 }; 269 270 static int dell_uart_bl_serdev_probe(struct serdev_device *serdev) 271 { 272 u8 get_version[GET_CMD_LEN], resp[MAX_RESP_LEN]; 273 struct backlight_properties props = {}; 274 struct dell_uart_backlight *dell_bl; 275 struct device *dev = &serdev->dev; 276 int ret; 277 278 dell_bl = devm_kzalloc(dev, sizeof(*dell_bl), GFP_KERNEL); 279 if (!dell_bl) 280 return -ENOMEM; 281 282 mutex_init(&dell_bl->mutex); 283 init_waitqueue_head(&dell_bl->wait_queue); 284 dell_bl->dev = dev; 285 286 ret = devm_serdev_device_open(dev, serdev); 287 if (ret) 288 return dev_err_probe(dev, ret, "opening UART device\n"); 289 290 /* 9600 bps, no flow control, these are the default but set them to be sure */ 291 serdev_device_set_baudrate(serdev, 9600); 292 serdev_device_set_flow_control(serdev, false); 293 serdev_device_set_drvdata(serdev, dell_bl); 294 serdev_device_set_client_ops(serdev, &dell_uart_bl_serdev_ops); 295 296 get_version[0] = DELL_SOF(GET_CMD_LEN); 297 get_version[1] = CMD_GET_VERSION; 298 get_version[2] = dell_uart_checksum(get_version, 2); 299 300 ret = dell_uart_bl_command(dell_bl, get_version, GET_CMD_LEN, resp, MAX_RESP_LEN); 301 if (ret) 302 return dev_err_probe(dev, ret, "getting firmware version\n"); 303 304 dev_dbg(dev, "Firmware version: %.*s\n", resp[RESP_LEN] - 3, resp + RESP_DATA); 305 306 /* Initialize bl_power to a known value */ 307 ret = dell_uart_set_bl_power(dell_bl, FB_BLANK_UNBLANK); 308 if (ret) 309 return ret; 310 311 ret = dell_uart_get_brightness(dell_bl); 312 if (ret < 0) 313 return ret; 314 315 props.type = BACKLIGHT_PLATFORM; 316 props.brightness = ret; 317 props.max_brightness = DELL_BL_MAX_BRIGHTNESS; 318 props.power = dell_bl->power; 319 320 dell_bl->bl = devm_backlight_device_register(dev, "dell_uart_backlight", 321 dev, dell_bl, 322 &dell_uart_backlight_ops, 323 &props); 324 return PTR_ERR_OR_ZERO(dell_bl->bl); 325 } 326 327 struct serdev_device_driver dell_uart_bl_serdev_driver = { 328 .probe = dell_uart_bl_serdev_probe, 329 .driver = { 330 .name = KBUILD_MODNAME, 331 }, 332 }; 333 334 static int dell_uart_bl_pdev_probe(struct platform_device *pdev) 335 { 336 enum acpi_backlight_type bl_type; 337 struct serdev_device *serdev; 338 struct device *ctrl_dev; 339 int ret; 340 341 bl_type = acpi_video_get_backlight_type(); 342 if (bl_type != acpi_backlight_dell_uart) { 343 dev_dbg(&pdev->dev, "Not loading (ACPI backlight type = %d)\n", bl_type); 344 return -ENODEV; 345 } 346 347 ctrl_dev = get_serdev_controller("DELL0501", NULL, 0, "serial0"); 348 if (IS_ERR(ctrl_dev)) 349 return PTR_ERR(ctrl_dev); 350 351 serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); 352 put_device(ctrl_dev); 353 if (!serdev) 354 return -ENOMEM; 355 356 ret = serdev_device_add(serdev); 357 if (ret) { 358 dev_err(&pdev->dev, "error %d adding serdev\n", ret); 359 serdev_device_put(serdev); 360 return ret; 361 } 362 363 ret = serdev_device_driver_register(&dell_uart_bl_serdev_driver); 364 if (ret) 365 goto err_remove_serdev; 366 367 /* 368 * serdev device <-> driver matching relies on OF or ACPI matches and 369 * neither is available here, manually bind the driver. 370 */ 371 ret = device_driver_attach(&dell_uart_bl_serdev_driver.driver, &serdev->dev); 372 if (ret) 373 goto err_unregister_serdev_driver; 374 375 /* So that dell_uart_bl_pdev_remove() can remove the serdev */ 376 platform_set_drvdata(pdev, serdev); 377 return 0; 378 379 err_unregister_serdev_driver: 380 serdev_device_driver_unregister(&dell_uart_bl_serdev_driver); 381 err_remove_serdev: 382 serdev_device_remove(serdev); 383 return ret; 384 } 385 386 static void dell_uart_bl_pdev_remove(struct platform_device *pdev) 387 { 388 struct serdev_device *serdev = platform_get_drvdata(pdev); 389 390 serdev_device_driver_unregister(&dell_uart_bl_serdev_driver); 391 serdev_device_remove(serdev); 392 } 393 394 static struct platform_driver dell_uart_bl_pdev_driver = { 395 .probe = dell_uart_bl_pdev_probe, 396 .remove_new = dell_uart_bl_pdev_remove, 397 .driver = { 398 .name = "dell-uart-backlight", 399 }, 400 }; 401 module_platform_driver(dell_uart_bl_pdev_driver); 402 403 MODULE_ALIAS("platform:dell-uart-backlight"); 404 MODULE_DESCRIPTION("Dell AIO Serial Backlight driver"); 405 MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); 406 MODULE_LICENSE("GPL"); 407