1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Backlight driver for Dialog Semiconductor DA9030/DA9034 |
4 | * |
5 | * Copyright (C) 2008 Compulab, Ltd. |
6 | * Mike Rapoport <mike@compulab.co.il> |
7 | * |
8 | * Copyright (C) 2006-2008 Marvell International Ltd. |
9 | * Eric Miao <eric.miao@marvell.com> |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/init.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/fb.h> |
16 | #include <linux/backlight.h> |
17 | #include <linux/mfd/da903x.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/module.h> |
20 | |
21 | #define DA9030_WLED_CONTROL 0x25 |
22 | #define DA9030_WLED_CP_EN (1 << 6) |
23 | #define DA9030_WLED_TRIM(x) ((x) & 0x7) |
24 | |
25 | #define DA9034_WLED_CONTROL1 0x3C |
26 | #define DA9034_WLED_CONTROL2 0x3D |
27 | #define DA9034_WLED_ISET(x) ((x) & 0x1f) |
28 | |
29 | #define DA9034_WLED_BOOST_EN (1 << 5) |
30 | |
31 | #define DA9030_MAX_BRIGHTNESS 7 |
32 | #define DA9034_MAX_BRIGHTNESS 0x7f |
33 | |
34 | struct da903x_backlight_data { |
35 | struct device *da903x_dev; |
36 | int id; |
37 | int current_brightness; |
38 | }; |
39 | |
40 | static int da903x_backlight_set(struct backlight_device *bl, int brightness) |
41 | { |
42 | struct da903x_backlight_data *data = bl_get_data(bl_dev: bl); |
43 | struct device *dev = data->da903x_dev; |
44 | uint8_t val; |
45 | int ret = 0; |
46 | |
47 | switch (data->id) { |
48 | case DA9034_ID_WLED: |
49 | ret = da903x_update(dev, DA9034_WLED_CONTROL1, |
50 | val: brightness, mask: 0x7f); |
51 | if (ret) |
52 | return ret; |
53 | |
54 | if (data->current_brightness && brightness == 0) |
55 | ret = da903x_clr_bits(dev, |
56 | DA9034_WLED_CONTROL2, |
57 | DA9034_WLED_BOOST_EN); |
58 | |
59 | if (data->current_brightness == 0 && brightness) |
60 | ret = da903x_set_bits(dev, |
61 | DA9034_WLED_CONTROL2, |
62 | DA9034_WLED_BOOST_EN); |
63 | break; |
64 | case DA9030_ID_WLED: |
65 | val = DA9030_WLED_TRIM(brightness); |
66 | val |= brightness ? DA9030_WLED_CP_EN : 0; |
67 | ret = da903x_write(dev, DA9030_WLED_CONTROL, val); |
68 | break; |
69 | } |
70 | |
71 | if (ret) |
72 | return ret; |
73 | |
74 | data->current_brightness = brightness; |
75 | return 0; |
76 | } |
77 | |
78 | static int da903x_backlight_update_status(struct backlight_device *bl) |
79 | { |
80 | return da903x_backlight_set(bl, brightness: backlight_get_brightness(bd: bl)); |
81 | } |
82 | |
83 | static int da903x_backlight_get_brightness(struct backlight_device *bl) |
84 | { |
85 | struct da903x_backlight_data *data = bl_get_data(bl_dev: bl); |
86 | |
87 | return data->current_brightness; |
88 | } |
89 | |
90 | static const struct backlight_ops da903x_backlight_ops = { |
91 | .options = BL_CORE_SUSPENDRESUME, |
92 | .update_status = da903x_backlight_update_status, |
93 | .get_brightness = da903x_backlight_get_brightness, |
94 | }; |
95 | |
96 | static int da903x_backlight_probe(struct platform_device *pdev) |
97 | { |
98 | struct da9034_backlight_pdata *pdata = dev_get_platdata(dev: &pdev->dev); |
99 | struct da903x_backlight_data *data; |
100 | struct backlight_device *bl; |
101 | struct backlight_properties props; |
102 | int max_brightness; |
103 | |
104 | data = devm_kzalloc(dev: &pdev->dev, size: sizeof(*data), GFP_KERNEL); |
105 | if (data == NULL) |
106 | return -ENOMEM; |
107 | |
108 | switch (pdev->id) { |
109 | case DA9030_ID_WLED: |
110 | max_brightness = DA9030_MAX_BRIGHTNESS; |
111 | break; |
112 | case DA9034_ID_WLED: |
113 | max_brightness = DA9034_MAX_BRIGHTNESS; |
114 | break; |
115 | default: |
116 | dev_err(&pdev->dev, "invalid backlight device ID(%d)\n" , |
117 | pdev->id); |
118 | return -EINVAL; |
119 | } |
120 | |
121 | data->id = pdev->id; |
122 | data->da903x_dev = pdev->dev.parent; |
123 | data->current_brightness = 0; |
124 | |
125 | /* adjust the WLED output current */ |
126 | if (pdata) |
127 | da903x_write(dev: data->da903x_dev, DA9034_WLED_CONTROL2, |
128 | DA9034_WLED_ISET(pdata->output_current)); |
129 | |
130 | memset(&props, 0, sizeof(props)); |
131 | props.type = BACKLIGHT_RAW; |
132 | props.max_brightness = max_brightness; |
133 | bl = devm_backlight_device_register(dev: &pdev->dev, name: pdev->name, |
134 | parent: data->da903x_dev, devdata: data, |
135 | ops: &da903x_backlight_ops, props: &props); |
136 | if (IS_ERR(ptr: bl)) { |
137 | dev_err(&pdev->dev, "failed to register backlight\n" ); |
138 | return PTR_ERR(ptr: bl); |
139 | } |
140 | |
141 | bl->props.brightness = max_brightness; |
142 | |
143 | platform_set_drvdata(pdev, data: bl); |
144 | backlight_update_status(bd: bl); |
145 | return 0; |
146 | } |
147 | |
148 | static struct platform_driver da903x_backlight_driver = { |
149 | .driver = { |
150 | .name = "da903x-backlight" , |
151 | }, |
152 | .probe = da903x_backlight_probe, |
153 | }; |
154 | |
155 | module_platform_driver(da903x_backlight_driver); |
156 | |
157 | MODULE_DESCRIPTION("Backlight Driver for Dialog Semiconductor DA9030/DA9034" ); |
158 | MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>" ); |
159 | MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>" ); |
160 | MODULE_LICENSE("GPL" ); |
161 | MODULE_ALIAS("platform:da903x-backlight" ); |
162 | |