^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Backlight Driver for Dialog DA9052 PMICs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright(c) 2012 Dialog Semiconductor Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author: David Dajun Chen <dchen@diasemi.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/backlight.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/fb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/mfd/da9052/da9052.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/mfd/da9052/reg.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define DA9052_MAX_BRIGHTNESS 0xFF
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) DA9052_WLEDS_OFF,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) DA9052_WLEDS_ON,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) DA9052_TYPE_WLED1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) DA9052_TYPE_WLED2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) DA9052_TYPE_WLED3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) static const unsigned char wled_bank[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) DA9052_LED1_CONF_REG,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) DA9052_LED2_CONF_REG,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) DA9052_LED3_CONF_REG,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) struct da9052_bl {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct da9052 *da9052;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) uint brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) uint state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) uint led_reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static int da9052_adjust_wled_brightness(struct da9052_bl *wleds)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) unsigned char boost_en;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) unsigned char i_sink;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) boost_en = 0x3F;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) i_sink = 0xFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) if (wleds->state == DA9052_WLEDS_OFF) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) boost_en = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) i_sink = 0x00;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) ret = da9052_reg_write(wleds->da9052, DA9052_BOOST_REG, boost_en);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) ret = da9052_reg_write(wleds->da9052, DA9052_LED_CONT_REG, i_sink);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg], 0x0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) usleep_range(10000, 11000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) if (wleds->brightness) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) ret = da9052_reg_write(wleds->da9052, wled_bank[wleds->led_reg],
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) wleds->brightness);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static int da9052_backlight_update_status(struct backlight_device *bl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) int brightness = bl->props.brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct da9052_bl *wleds = bl_get_data(bl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) wleds->brightness = brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) wleds->state = DA9052_WLEDS_ON;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) return da9052_adjust_wled_brightness(wleds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) static int da9052_backlight_get_brightness(struct backlight_device *bl)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) struct da9052_bl *wleds = bl_get_data(bl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return wleds->brightness;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) static const struct backlight_ops da9052_backlight_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) .update_status = da9052_backlight_update_status,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) .get_brightness = da9052_backlight_get_brightness,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) static int da9052_backlight_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) struct backlight_device *bl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) struct backlight_properties props;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) struct da9052_bl *wleds;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) wleds = devm_kzalloc(&pdev->dev, sizeof(struct da9052_bl), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) if (!wleds)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) wleds->da9052 = dev_get_drvdata(pdev->dev.parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) wleds->brightness = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) wleds->led_reg = platform_get_device_id(pdev)->driver_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) wleds->state = DA9052_WLEDS_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) props.type = BACKLIGHT_RAW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) props.max_brightness = DA9052_MAX_BRIGHTNESS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) bl = devm_backlight_device_register(&pdev->dev, pdev->name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) wleds->da9052->dev, wleds,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) &da9052_backlight_ops, &props);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (IS_ERR(bl)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) dev_err(&pdev->dev, "Failed to register backlight\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) return PTR_ERR(bl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) bl->props.max_brightness = DA9052_MAX_BRIGHTNESS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) bl->props.brightness = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) platform_set_drvdata(pdev, bl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) return da9052_adjust_wled_brightness(wleds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) static int da9052_backlight_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) struct backlight_device *bl = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) struct da9052_bl *wleds = bl_get_data(bl);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) wleds->brightness = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) wleds->state = DA9052_WLEDS_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) da9052_adjust_wled_brightness(wleds);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) static const struct platform_device_id da9052_wled_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) .name = "da9052-wled1",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) .driver_data = DA9052_TYPE_WLED1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) .name = "da9052-wled2",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) .driver_data = DA9052_TYPE_WLED2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) .name = "da9052-wled3",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) .driver_data = DA9052_TYPE_WLED3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) { },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) MODULE_DEVICE_TABLE(platform, da9052_wled_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) static struct platform_driver da9052_wled_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) .probe = da9052_backlight_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) .remove = da9052_backlight_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) .id_table = da9052_wled_ids,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) .name = "da9052-wled",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) module_platform_driver(da9052_wled_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) MODULE_DESCRIPTION("Backlight driver for DA9052 PMIC");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) MODULE_LICENSE("GPL");