^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * LEDs driver for Dialog Semiconductor DA9030/DA9034
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2008 Compulab, Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Mike Rapoport <mike@compulab.co.il>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright (C) 2006-2008 Marvell International Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Eric Miao <eric.miao@marvell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/kernel.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) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/mfd/da903x.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define DA9030_LED1_CONTROL 0x20
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define DA9030_LED2_CONTROL 0x21
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define DA9030_LED3_CONTROL 0x22
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define DA9030_LED4_CONTROL 0x23
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define DA9030_LEDPC_CONTROL 0x24
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define DA9030_MISC_CONTROL_A 0x26 /* Vibrator Control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define DA9034_LED1_CONTROL 0x35
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define DA9034_LED2_CONTROL 0x36
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define DA9034_VIBRA 0x40
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) struct da903x_led {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct led_classdev cdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct device *master;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) int id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) int flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) #define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) static int da903x_led_set(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) enum led_brightness value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) struct da903x_led *led =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) container_of(led_cdev, struct da903x_led, cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) uint8_t val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) int offset, ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) switch (led->id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) case DA9030_ID_LED_1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) case DA9030_ID_LED_2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) case DA9030_ID_LED_3:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) case DA9030_ID_LED_4:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) case DA9030_ID_LED_PC:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) offset = DA9030_LED_OFFSET(led->id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) val = led->flags & ~0x87;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) val |= value ? 0x80 : 0; /* EN bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) val |= (0x7 - (value >> 5)) & 0x7; /* PWM<2:0> */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) ret = da903x_write(led->master, DA9030_LED1_CONTROL + offset,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) case DA9030_ID_VIBRA:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) val = led->flags & ~0x80;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) val |= value ? 0x80 : 0; /* EN bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) ret = da903x_write(led->master, DA9030_MISC_CONTROL_A, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) case DA9034_ID_LED_1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) case DA9034_ID_LED_2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) offset = DA9034_LED_OFFSET(led->id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) val = (value * 0x5f / LED_FULL) & 0x7f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) ret = da903x_write(led->master, DA9034_LED1_CONTROL + offset,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) case DA9034_ID_VIBRA:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) val = value & 0xfe;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) ret = da903x_write(led->master, DA9034_VIBRA, val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) static int da903x_led_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct led_info *pdata = dev_get_platdata(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) struct da903x_led *led;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) int id, ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) if (pdata == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) id = pdev->id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (!((id >= DA9030_ID_LED_1 && id <= DA9030_ID_VIBRA) ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) (id >= DA9034_ID_LED_1 && id <= DA9034_ID_VIBRA))) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) dev_err(&pdev->dev, "invalid LED ID (%d) specified\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return -EINVAL;
^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) led = devm_kzalloc(&pdev->dev, sizeof(struct da903x_led), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (!led)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) led->cdev.name = pdata->name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) led->cdev.default_trigger = pdata->default_trigger;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) led->cdev.brightness_set_blocking = da903x_led_set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) led->cdev.brightness = LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) led->id = id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) led->flags = pdata->flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) led->master = pdev->dev.parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) ret = led_classdev_register(led->master, &led->cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) dev_err(&pdev->dev, "failed to register LED %d\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) platform_set_drvdata(pdev, led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) static int da903x_led_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) struct da903x_led *led = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) led_classdev_unregister(&led->cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static struct platform_driver da903x_led_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .name = "da903x-led",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .probe = da903x_led_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) .remove = da903x_led_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) module_platform_driver(da903x_led_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) MODULE_DESCRIPTION("LEDs driver for Dialog Semiconductor DA9030/DA9034");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) MODULE_ALIAS("platform:da903x-led");