| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * gpio_backlight.c - Simple GPIO-controlled backlight |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 5 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 6 | | - * published by the Free Software Foundation. |
|---|
| 7 | 4 | */ |
|---|
| 8 | 5 | |
|---|
| 9 | 6 | #include <linux/backlight.h> |
|---|
| 10 | 7 | #include <linux/err.h> |
|---|
| 11 | 8 | #include <linux/fb.h> |
|---|
| 12 | | -#include <linux/gpio.h> /* Only for legacy support */ |
|---|
| 13 | 9 | #include <linux/gpio/consumer.h> |
|---|
| 14 | 10 | #include <linux/init.h> |
|---|
| 15 | 11 | #include <linux/kernel.h> |
|---|
| 16 | 12 | #include <linux/module.h> |
|---|
| 17 | 13 | #include <linux/of.h> |
|---|
| 18 | | -#include <linux/of_gpio.h> |
|---|
| 19 | 14 | #include <linux/platform_data/gpio_backlight.h> |
|---|
| 20 | 15 | #include <linux/platform_device.h> |
|---|
| 16 | +#include <linux/property.h> |
|---|
| 21 | 17 | #include <linux/slab.h> |
|---|
| 22 | 18 | |
|---|
| 23 | 19 | struct gpio_backlight { |
|---|
| 24 | | - struct device *dev; |
|---|
| 25 | 20 | struct device *fbdev; |
|---|
| 26 | | - |
|---|
| 27 | 21 | struct gpio_desc *gpiod; |
|---|
| 28 | | - int def_value; |
|---|
| 29 | 22 | }; |
|---|
| 30 | 23 | |
|---|
| 31 | 24 | static int gpio_backlight_update_status(struct backlight_device *bl) |
|---|
| 32 | 25 | { |
|---|
| 33 | 26 | struct gpio_backlight *gbl = bl_get_data(bl); |
|---|
| 34 | | - int brightness = bl->props.brightness; |
|---|
| 35 | 27 | |
|---|
| 36 | | - if (bl->props.power != FB_BLANK_UNBLANK || |
|---|
| 37 | | - bl->props.fb_blank != FB_BLANK_UNBLANK || |
|---|
| 38 | | - bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) |
|---|
| 39 | | - brightness = 0; |
|---|
| 40 | | - |
|---|
| 41 | | - gpiod_set_value_cansleep(gbl->gpiod, brightness); |
|---|
| 28 | + gpiod_set_value_cansleep(gbl->gpiod, backlight_get_brightness(bl)); |
|---|
| 42 | 29 | |
|---|
| 43 | 30 | return 0; |
|---|
| 44 | 31 | } |
|---|
| .. | .. |
|---|
| 57 | 44 | .check_fb = gpio_backlight_check_fb, |
|---|
| 58 | 45 | }; |
|---|
| 59 | 46 | |
|---|
| 60 | | -static int gpio_backlight_probe_dt(struct platform_device *pdev, |
|---|
| 61 | | - struct gpio_backlight *gbl) |
|---|
| 47 | +static int gpio_backlight_probe(struct platform_device *pdev) |
|---|
| 62 | 48 | { |
|---|
| 63 | 49 | struct device *dev = &pdev->dev; |
|---|
| 64 | | - struct device_node *np = dev->of_node; |
|---|
| 65 | | - int ret; |
|---|
| 50 | + struct gpio_backlight_platform_data *pdata = dev_get_platdata(dev); |
|---|
| 51 | + struct device_node *of_node = dev->of_node; |
|---|
| 52 | + struct backlight_properties props; |
|---|
| 53 | + struct backlight_device *bl; |
|---|
| 54 | + struct gpio_backlight *gbl; |
|---|
| 55 | + int ret, init_brightness, def_value; |
|---|
| 66 | 56 | |
|---|
| 67 | | - gbl->def_value = of_property_read_bool(np, "default-on"); |
|---|
| 57 | + gbl = devm_kzalloc(dev, sizeof(*gbl), GFP_KERNEL); |
|---|
| 58 | + if (gbl == NULL) |
|---|
| 59 | + return -ENOMEM; |
|---|
| 60 | + |
|---|
| 61 | + if (pdata) |
|---|
| 62 | + gbl->fbdev = pdata->fbdev; |
|---|
| 63 | + |
|---|
| 64 | + def_value = device_property_read_bool(dev, "default-on"); |
|---|
| 68 | 65 | |
|---|
| 69 | 66 | gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); |
|---|
| 70 | 67 | if (IS_ERR(gbl->gpiod)) { |
|---|
| 71 | 68 | ret = PTR_ERR(gbl->gpiod); |
|---|
| 72 | | - |
|---|
| 73 | | - if (ret != -EPROBE_DEFER) { |
|---|
| 69 | + if (ret != -EPROBE_DEFER) |
|---|
| 74 | 70 | dev_err(dev, |
|---|
| 75 | 71 | "Error: The gpios parameter is missing or invalid.\n"); |
|---|
| 76 | | - } |
|---|
| 77 | 72 | return ret; |
|---|
| 78 | | - } |
|---|
| 79 | | - |
|---|
| 80 | | - return 0; |
|---|
| 81 | | -} |
|---|
| 82 | | - |
|---|
| 83 | | -static int gpio_backlight_initial_power_state(struct gpio_backlight *gbl) |
|---|
| 84 | | -{ |
|---|
| 85 | | - struct device_node *node = gbl->dev->of_node; |
|---|
| 86 | | - |
|---|
| 87 | | - /* Not booted with device tree or no phandle link to the node */ |
|---|
| 88 | | - if (!node || !node->phandle) |
|---|
| 89 | | - return gbl->def_value ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; |
|---|
| 90 | | - |
|---|
| 91 | | - /* if the enable GPIO is disabled, do not enable the backlight */ |
|---|
| 92 | | - if (gpiod_get_value_cansleep(gbl->gpiod) == 0) |
|---|
| 93 | | - return FB_BLANK_POWERDOWN; |
|---|
| 94 | | - |
|---|
| 95 | | - return FB_BLANK_UNBLANK; |
|---|
| 96 | | -} |
|---|
| 97 | | - |
|---|
| 98 | | - |
|---|
| 99 | | -static int gpio_backlight_probe(struct platform_device *pdev) |
|---|
| 100 | | -{ |
|---|
| 101 | | - struct gpio_backlight_platform_data *pdata = |
|---|
| 102 | | - dev_get_platdata(&pdev->dev); |
|---|
| 103 | | - struct backlight_properties props; |
|---|
| 104 | | - struct backlight_device *bl; |
|---|
| 105 | | - struct gpio_backlight *gbl; |
|---|
| 106 | | - struct device_node *np = pdev->dev.of_node; |
|---|
| 107 | | - int ret; |
|---|
| 108 | | - |
|---|
| 109 | | - if (!pdata && !np) { |
|---|
| 110 | | - dev_err(&pdev->dev, |
|---|
| 111 | | - "failed to find platform data or device tree node.\n"); |
|---|
| 112 | | - return -ENODEV; |
|---|
| 113 | | - } |
|---|
| 114 | | - |
|---|
| 115 | | - gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); |
|---|
| 116 | | - if (gbl == NULL) |
|---|
| 117 | | - return -ENOMEM; |
|---|
| 118 | | - |
|---|
| 119 | | - gbl->dev = &pdev->dev; |
|---|
| 120 | | - |
|---|
| 121 | | - if (np) { |
|---|
| 122 | | - ret = gpio_backlight_probe_dt(pdev, gbl); |
|---|
| 123 | | - if (ret) |
|---|
| 124 | | - return ret; |
|---|
| 125 | | - } else { |
|---|
| 126 | | - /* |
|---|
| 127 | | - * Legacy platform data GPIO retrieveal. Do not expand |
|---|
| 128 | | - * the use of this code path, currently only used by one |
|---|
| 129 | | - * SH board. |
|---|
| 130 | | - */ |
|---|
| 131 | | - unsigned long flags = GPIOF_DIR_OUT; |
|---|
| 132 | | - |
|---|
| 133 | | - gbl->fbdev = pdata->fbdev; |
|---|
| 134 | | - gbl->def_value = pdata->def_value; |
|---|
| 135 | | - flags |= gbl->def_value ? GPIOF_INIT_HIGH : GPIOF_INIT_LOW; |
|---|
| 136 | | - |
|---|
| 137 | | - ret = devm_gpio_request_one(gbl->dev, pdata->gpio, flags, |
|---|
| 138 | | - pdata ? pdata->name : "backlight"); |
|---|
| 139 | | - if (ret < 0) { |
|---|
| 140 | | - dev_err(&pdev->dev, "unable to request GPIO\n"); |
|---|
| 141 | | - return ret; |
|---|
| 142 | | - } |
|---|
| 143 | | - gbl->gpiod = gpio_to_desc(pdata->gpio); |
|---|
| 144 | | - if (!gbl->gpiod) |
|---|
| 145 | | - return -EINVAL; |
|---|
| 146 | 73 | } |
|---|
| 147 | 74 | |
|---|
| 148 | 75 | memset(&props, 0, sizeof(props)); |
|---|
| 149 | 76 | props.type = BACKLIGHT_RAW; |
|---|
| 150 | 77 | props.max_brightness = 1; |
|---|
| 151 | | - bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), |
|---|
| 152 | | - &pdev->dev, gbl, &gpio_backlight_ops, |
|---|
| 153 | | - &props); |
|---|
| 78 | + bl = devm_backlight_device_register(dev, dev_name(dev), dev, gbl, |
|---|
| 79 | + &gpio_backlight_ops, &props); |
|---|
| 154 | 80 | if (IS_ERR(bl)) { |
|---|
| 155 | | - dev_err(&pdev->dev, "failed to register backlight\n"); |
|---|
| 81 | + dev_err(dev, "failed to register backlight\n"); |
|---|
| 156 | 82 | return PTR_ERR(bl); |
|---|
| 157 | 83 | } |
|---|
| 158 | 84 | |
|---|
| 159 | | - bl->props.power = gpio_backlight_initial_power_state(gbl); |
|---|
| 85 | + /* Set the initial power state */ |
|---|
| 86 | + if (!of_node || !of_node->phandle) |
|---|
| 87 | + /* Not booted with device tree or no phandle link to the node */ |
|---|
| 88 | + bl->props.power = def_value ? FB_BLANK_UNBLANK |
|---|
| 89 | + : FB_BLANK_POWERDOWN; |
|---|
| 90 | + else if (gpiod_get_direction(gbl->gpiod) == 0 && |
|---|
| 91 | + gpiod_get_value_cansleep(gbl->gpiod) == 0) |
|---|
| 92 | + bl->props.power = FB_BLANK_POWERDOWN; |
|---|
| 93 | + else |
|---|
| 94 | + bl->props.power = FB_BLANK_UNBLANK; |
|---|
| 95 | + |
|---|
| 160 | 96 | bl->props.brightness = 1; |
|---|
| 161 | 97 | |
|---|
| 162 | | - backlight_update_status(bl); |
|---|
| 98 | + init_brightness = backlight_get_brightness(bl); |
|---|
| 99 | + ret = gpiod_direction_output(gbl->gpiod, init_brightness); |
|---|
| 100 | + if (ret) { |
|---|
| 101 | + dev_err(dev, "failed to set initial brightness\n"); |
|---|
| 102 | + return ret; |
|---|
| 103 | + } |
|---|
| 163 | 104 | |
|---|
| 164 | 105 | platform_set_drvdata(pdev, bl); |
|---|
| 165 | 106 | return 0; |
|---|
| 166 | 107 | } |
|---|
| 167 | 108 | |
|---|
| 168 | | -#ifdef CONFIG_OF |
|---|
| 169 | 109 | static struct of_device_id gpio_backlight_of_match[] = { |
|---|
| 170 | 110 | { .compatible = "gpio-backlight" }, |
|---|
| 171 | 111 | { /* sentinel */ } |
|---|
| 172 | 112 | }; |
|---|
| 173 | 113 | |
|---|
| 174 | 114 | MODULE_DEVICE_TABLE(of, gpio_backlight_of_match); |
|---|
| 175 | | -#endif |
|---|
| 176 | 115 | |
|---|
| 177 | 116 | static struct platform_driver gpio_backlight_driver = { |
|---|
| 178 | 117 | .driver = { |
|---|
| 179 | 118 | .name = "gpio-backlight", |
|---|
| 180 | | - .of_match_table = of_match_ptr(gpio_backlight_of_match), |
|---|
| 119 | + .of_match_table = gpio_backlight_of_match, |
|---|
| 181 | 120 | }, |
|---|
| 182 | 121 | .probe = gpio_backlight_probe, |
|---|
| 183 | 122 | }; |
|---|