1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* ROHM Semiconductor BD6107 LED Driver
*
* Copyright (C) 2013 Ideas on board SPRL
*
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
*/
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_data/bd6107.h>
#include <linux/slab.h>
#define BD6107_PSCNT1 0x00
#define BD6107_PSCNT1_PSCNTREG2 (1 << 2)
#define BD6107_PSCNT1_PSCNTREG1 (1 << 0)
#define BD6107_REGVSET 0x02
#define BD6107_REGVSET_REG1VSET_2_85V (1 << 2)
#define BD6107_REGVSET_REG1VSET_2_80V (0 << 2)
#define BD6107_LEDCNT1 0x03
#define BD6107_LEDCNT1_LEDONOFF2 (1 << 1)
#define BD6107_LEDCNT1_LEDONOFF1 (1 << 0)
#define BD6107_PORTSEL 0x04
#define BD6107_PORTSEL_LEDM(n) (1 << (n))
#define BD6107_RGB1CNT1 0x05
#define BD6107_RGB1CNT2 0x06
#define BD6107_RGB1CNT3 0x07
#define BD6107_RGB1CNT4 0x08
#define BD6107_RGB1CNT5 0x09
#define BD6107_RGB1FLM 0x0a
#define BD6107_RGB2CNT1 0x0b
#define BD6107_RGB2CNT2 0x0c
#define BD6107_RGB2CNT3 0x0d
#define BD6107_RGB2CNT4 0x0e
#define BD6107_RGB2CNT5 0x0f
#define BD6107_RGB2FLM 0x10
#define BD6107_PSCONT3 0x11
#define BD6107_SMMONCNT 0x12
#define BD6107_DCDCCNT 0x13
#define BD6107_IOSEL 0x14
#define BD6107_OUT1 0x15
#define BD6107_OUT2 0x16
#define BD6107_MASK1 0x17
#define BD6107_MASK2 0x18
#define BD6107_FACTOR1 0x19
#define BD6107_FACTOR2 0x1a
#define BD6107_CLRFACT1 0x1b
#define BD6107_CLRFACT2 0x1c
#define BD6107_STATE1 0x1d
#define BD6107_LSIVER 0x1e
#define BD6107_GRPSEL 0x1f
#define BD6107_LEDCNT2 0x20
#define BD6107_LEDCNT3 0x21
#define BD6107_MCURRENT 0x22
#define BD6107_MAINCNT1 0x23
#define BD6107_MAINCNT2 0x24
#define BD6107_SLOPECNT 0x25
#define BD6107_MSLOPE 0x26
#define BD6107_RGBSLOPE 0x27
#define BD6107_TEST 0x29
#define BD6107_SFTRST 0x2a
#define BD6107_SFTRSTGD 0x2b
struct bd6107 {
struct i2c_client *client;
struct backlight_device *backlight;
struct bd6107_platform_data *pdata;
struct gpio_desc *reset;
};
static int bd6107_write(struct bd6107 *bd, u8 reg, u8 data)
{
return i2c_smbus_write_byte_data(bd->client, reg, data);
}
static int bd6107_backlight_update_status(struct backlight_device *backlight)
{
struct bd6107 *bd = bl_get_data(backlight);
int brightness = backlight_get_brightness(backlight);
if (brightness) {
bd6107_write(bd, BD6107_PORTSEL, BD6107_PORTSEL_LEDM(2) |
BD6107_PORTSEL_LEDM(1) | BD6107_PORTSEL_LEDM(0));
bd6107_write(bd, BD6107_MAINCNT1, brightness);
bd6107_write(bd, BD6107_LEDCNT1, BD6107_LEDCNT1_LEDONOFF1);
} else {
/* Assert the reset line (gpiolib will handle active low) */
gpiod_set_value(bd->reset, 1);
msleep(24);
gpiod_set_value(bd->reset, 0);
}
return 0;
}
static int bd6107_backlight_check_fb(struct backlight_device *backlight,
struct fb_info *info)
{
struct bd6107 *bd = bl_get_data(backlight);
return !bd->pdata->dev || bd->pdata->dev == info->device;
}
static const struct backlight_ops bd6107_backlight_ops = {
.options = BL_CORE_SUSPENDRESUME,
.update_status = bd6107_backlight_update_status,
.check_fb = bd6107_backlight_check_fb,
};
static int bd6107_probe(struct i2c_client *client)
{
struct bd6107_platform_data *pdata = dev_get_platdata(&client->dev);
struct backlight_device *backlight;
struct backlight_properties props;
struct bd6107 *bd;
int ret;
if (pdata == NULL) {
dev_err(&client->dev, "No platform data\n");
return -EINVAL;
}
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_warn(&client->dev,
"I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
return -EIO;
}
bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL);
if (!bd)
return -ENOMEM;
bd->client = client;
bd->pdata = pdata;
/*
* Request the reset GPIO line with GPIOD_OUT_HIGH meaning asserted,
* so in the machine descriptor table (or other hardware description),
* the line should be flagged as active low so this will assert
* the reset.
*/
bd->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(bd->reset)) {
dev_err(&client->dev, "unable to request reset GPIO\n");
ret = PTR_ERR(bd->reset);
return ret;
}
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
props.max_brightness = 128;
props.brightness = clamp_t(unsigned int, pdata->def_value, 0,
props.max_brightness);
backlight = devm_backlight_device_register(&client->dev,
dev_name(&client->dev),
&bd->client->dev, bd,
&bd6107_backlight_ops, &props);
if (IS_ERR(backlight)) {
dev_err(&client->dev, "failed to register backlight\n");
return PTR_ERR(backlight);
}
backlight_update_status(backlight);
i2c_set_clientdata(client, backlight);
return 0;
}
static void bd6107_remove(struct i2c_client *client)
{
struct backlight_device *backlight = i2c_get_clientdata(client);
backlight->props.brightness = 0;
backlight_update_status(backlight);
}
static const struct i2c_device_id bd6107_ids[] = {
{ "bd6107", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, bd6107_ids);
static struct i2c_driver bd6107_driver = {
.driver = {
.name = "bd6107",
},
.probe = bd6107_probe,
.remove = bd6107_remove,
.id_table = bd6107_ids,
};
module_i2c_driver(bd6107_driver);
MODULE_DESCRIPTION("Rohm BD6107 Backlight Driver");
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_LICENSE("GPL");
|