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
205
206
207
208
|
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2024 Waveshare International Limited
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
/* I2C registers of the microcontroller. */
#define REG_TP 0x94
#define REG_LCD 0x95
#define REG_PWM 0x96
#define REG_SIZE 0x97
#define REG_ID 0x98
#define REG_VERSION 0x99
enum {
GPIO_AVDD = 0,
GPIO_PANEL_RESET = 1,
GPIO_BL_ENABLE = 2,
GPIO_IOVCC = 4,
GPIO_VCC = 8,
GPIO_TS_RESET = 9,
};
#define NUM_GPIO 16
struct waveshare_gpio {
struct mutex dir_lock;
struct mutex pwr_lock;
struct regmap *regmap;
u16 poweron_state;
struct gpio_chip gc;
};
static const struct regmap_config waveshare_gpio_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = REG_VERSION,
};
static int waveshare_gpio_get(struct waveshare_gpio *state, unsigned int offset)
{
u16 pwr_state;
guard(mutex)(&state->pwr_lock);
pwr_state = state->poweron_state & BIT(offset);
return !!pwr_state;
}
static int waveshare_gpio_set(struct waveshare_gpio *state, unsigned int offset, int value)
{
u16 last_val;
int err;
guard(mutex)(&state->pwr_lock);
last_val = state->poweron_state;
if (value)
last_val |= BIT(offset);
else
last_val &= ~BIT(offset);
state->poweron_state = last_val;
err = regmap_write(state->regmap, REG_TP, last_val >> 8);
if (!err)
err = regmap_write(state->regmap, REG_LCD, last_val & 0xff);
return err;
}
static int waveshare_gpio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
}
static int waveshare_gpio_gpio_get(struct gpio_chip *gc, unsigned int offset)
{
struct waveshare_gpio *state = gpiochip_get_data(gc);
return waveshare_gpio_get(state, offset);
}
static int waveshare_gpio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct waveshare_gpio *state = gpiochip_get_data(gc);
return waveshare_gpio_set(state, offset, value);
}
static int waveshare_gpio_update_status(struct backlight_device *bl)
{
struct waveshare_gpio *state = bl_get_data(bl);
int brightness = backlight_get_brightness(bl);
waveshare_gpio_set(state, GPIO_BL_ENABLE, brightness);
return regmap_write(state->regmap, REG_PWM, brightness);
}
static const struct backlight_ops waveshare_gpio_bl = {
.update_status = waveshare_gpio_update_status,
};
static int waveshare_gpio_probe(struct i2c_client *i2c)
{
struct backlight_properties props = {};
struct waveshare_gpio *state;
struct device *dev = &i2c->dev;
struct backlight_device *bl;
struct regmap *regmap;
unsigned int data;
int ret;
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
ret = devm_mutex_init(dev, &state->dir_lock);
if (ret)
return ret;
ret = devm_mutex_init(dev, &state->pwr_lock);
if (ret)
return ret;
regmap = devm_regmap_init_i2c(i2c, &waveshare_gpio_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
state->regmap = regmap;
i2c_set_clientdata(i2c, state);
ret = regmap_read(regmap, REG_ID, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel hw id = 0x%x\n", data);
ret = regmap_read(regmap, REG_SIZE, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel size = %d\n", data);
ret = regmap_read(regmap, REG_VERSION, &data);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read register\n");
dev_dbg(dev, "waveshare panel mcu version = 0x%x\n", data);
ret = waveshare_gpio_set(state, GPIO_TS_RESET, 1);
if (ret)
return dev_err_probe(dev, ret, "Failed to program GPIOs\n");
msleep(20);
state->gc.parent = dev;
state->gc.label = i2c->name;
state->gc.owner = THIS_MODULE;
state->gc.base = -1;
state->gc.ngpio = NUM_GPIO;
/* it is output only */
state->gc.get = waveshare_gpio_gpio_get;
state->gc.set = waveshare_gpio_gpio_set;
state->gc.get_direction = waveshare_gpio_gpio_get_direction;
state->gc.can_sleep = true;
ret = devm_gpiochip_add_data(dev, &state->gc, state);
if (ret)
return dev_err_probe(dev, ret, "Failed to create gpiochip\n");
props.type = BACKLIGHT_RAW;
props.max_brightness = 255;
props.brightness = 255;
bl = devm_backlight_device_register(dev, dev_name(dev), dev, state,
&waveshare_gpio_bl, &props);
return PTR_ERR_OR_ZERO(bl);
}
static const struct of_device_id waveshare_gpio_dt_ids[] = {
{ .compatible = "waveshare,dsi-touch-gpio" },
{},
};
MODULE_DEVICE_TABLE(of, waveshare_gpio_dt_ids);
static struct i2c_driver waveshare_gpio_regulator_driver = {
.driver = {
.name = "waveshare-regulator",
.of_match_table = of_match_ptr(waveshare_gpio_dt_ids),
},
.probe = waveshare_gpio_probe,
};
module_i2c_driver(waveshare_gpio_regulator_driver);
MODULE_DESCRIPTION("GPIO controller driver for Waveshare DSI touch panels");
MODULE_LICENSE("GPL");
|