1
2
3
4
5
6
7
8#include <drm/drm_mipi_dbi.h>
9#include <drm/drm_modes.h>
10#include <drm/drm_panel.h>
11
12#include <linux/delay.h>
13#include <linux/gpio/consumer.h>
14#include <linux/init.h>
15#include <linux/kernel.h>
16#include <linux/media-bus-format.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/regulator/consumer.h>
20#include <linux/spi/spi.h>
21
22#include <video/mipi_display.h>
23
24#define DB7430_ACCESS_PROT_OFF 0xb0
25#define DB7430_UNKNOWN_B4 0xb4
26#define DB7430_USER_SELECT 0xb5
27#define DB7430_UNKNOWN_B7 0xb7
28#define DB7430_UNKNOWN_B8 0xb8
29#define DB7430_PANEL_DRIVING 0xc0
30#define DB7430_SOURCE_CONTROL 0xc1
31#define DB7430_GATE_INTERFACE 0xc4
32#define DB7430_DISPLAY_H_TIMING 0xc5
33#define DB7430_RGB_SYNC_OPTION 0xc6
34#define DB7430_GAMMA_SET_RED 0xc8
35#define DB7430_GAMMA_SET_GREEN 0xc9
36#define DB7430_GAMMA_SET_BLUE 0xca
37#define DB7430_BIAS_CURRENT_CTRL 0xd1
38#define DB7430_DDV_CTRL 0xd2
39#define DB7430_GAMMA_CTRL_REF 0xd3
40#define DB7430_UNKNOWN_D4 0xd4
41#define DB7430_DCDC_CTRL 0xd5
42#define DB7430_VCL_CTRL 0xd6
43#define DB7430_UNKNOWN_F8 0xf8
44#define DB7430_UNKNOWN_FC 0xfc
45
46#define DATA_MASK 0x100
47
48
49
50
51
52struct db7430 {
53
54 struct device *dev;
55
56 struct mipi_dbi dbi;
57
58 struct drm_panel panel;
59
60 u32 width;
61
62 u32 height;
63
64 struct gpio_desc *reset;
65
66 struct regulator_bulk_data regulators[2];
67};
68
69static const struct drm_display_mode db7430_480_800_mode = {
70
71
72
73
74 .clock = 32258,
75 .hdisplay = 480,
76 .hsync_start = 480 + 10,
77 .hsync_end = 480 + 10 + 4,
78 .htotal = 480 + 10 + 4 + 40,
79 .vdisplay = 800,
80 .vsync_start = 800 + 6,
81 .vsync_end = 800 + 6 + 1,
82 .vtotal = 800 + 6 + 1 + 7,
83 .width_mm = 53,
84 .height_mm = 87,
85 .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
86};
87
88static inline struct db7430 *to_db7430(struct drm_panel *panel)
89{
90 return container_of(panel, struct db7430, panel);
91}
92
93static int db7430_power_on(struct db7430 *db)
94{
95 struct mipi_dbi *dbi = &db->dbi;
96 int ret;
97
98
99 ret = regulator_bulk_enable(ARRAY_SIZE(db->regulators),
100 db->regulators);
101 if (ret) {
102 dev_err(db->dev, "failed to enable regulators: %d\n", ret);
103 return ret;
104 }
105 msleep(50);
106
107
108 gpiod_set_value_cansleep(db->reset, 1);
109 usleep_range(1000, 5000);
110
111 gpiod_set_value_cansleep(db->reset, 0);
112
113 msleep(10);
114 dev_dbg(db->dev, "de-asserted RESET\n");
115
116
117
118
119
120
121
122 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
123 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
124 mipi_dbi_command(dbi, DB7430_ACCESS_PROT_OFF, 0x00);
125 mipi_dbi_command(dbi, DB7430_PANEL_DRIVING, 0x28, 0x08);
126 mipi_dbi_command(dbi, DB7430_SOURCE_CONTROL,
127 0x01, 0x30, 0x15, 0x05, 0x22);
128 mipi_dbi_command(dbi, DB7430_GATE_INTERFACE,
129 0x10, 0x01, 0x00);
130 mipi_dbi_command(dbi, DB7430_DISPLAY_H_TIMING,
131 0x06, 0x55, 0x03, 0x07, 0x0b,
132 0x33, 0x00, 0x01, 0x03);
133
134
135
136
137 mipi_dbi_command(dbi, DB7430_RGB_SYNC_OPTION, 0x01);
138 mipi_dbi_command(dbi, DB7430_GAMMA_SET_RED,
139 0x00,
140 0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62,
141 0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08,
142 0x00,
143 0x0A, 0x31, 0x3B, 0x4E, 0x58, 0x59, 0x5B, 0x58, 0x5E, 0x62,
144 0x60, 0x61, 0x5E, 0x62, 0x55, 0x55, 0x7F, 0x08);
145 mipi_dbi_command(dbi, DB7430_GAMMA_SET_GREEN,
146 0x00,
147 0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59,
148 0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A,
149 0x00,
150 0x25, 0x15, 0x28, 0x3D, 0x4A, 0x48, 0x4C, 0x4A, 0x52, 0x59,
151 0x59, 0x5B, 0x56, 0x60, 0x5D, 0x55, 0x7F, 0x0A);
152 mipi_dbi_command(dbi, DB7430_GAMMA_SET_BLUE,
153 0x00,
154 0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D,
155 0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C,
156 0x00,
157 0x48, 0x10, 0x1F, 0x2F, 0x35, 0x38, 0x3D, 0x3C, 0x45, 0x4D,
158 0x4E, 0x52, 0x51, 0x60, 0x7F, 0x7E, 0x7F, 0x0C);
159 mipi_dbi_command(dbi, DB7430_BIAS_CURRENT_CTRL, 0x33, 0x13);
160 mipi_dbi_command(dbi, DB7430_DDV_CTRL, 0x11, 0x00, 0x00);
161 mipi_dbi_command(dbi, DB7430_GAMMA_CTRL_REF, 0x50, 0x50);
162 mipi_dbi_command(dbi, DB7430_DCDC_CTRL, 0x2f, 0x11, 0x1e, 0x46);
163 mipi_dbi_command(dbi, DB7430_VCL_CTRL, 0x11, 0x0a);
164
165 return 0;
166}
167
168static int db7430_power_off(struct db7430 *db)
169{
170
171 gpiod_set_value_cansleep(db->reset, 1);
172 return regulator_bulk_disable(ARRAY_SIZE(db->regulators),
173 db->regulators);
174}
175
176static int db7430_unprepare(struct drm_panel *panel)
177{
178 return db7430_power_off(to_db7430(panel));
179}
180
181static int db7430_disable(struct drm_panel *panel)
182{
183 struct db7430 *db = to_db7430(panel);
184 struct mipi_dbi *dbi = &db->dbi;
185
186 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF);
187 msleep(25);
188 mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE);
189 msleep(120);
190
191 return 0;
192}
193
194static int db7430_prepare(struct drm_panel *panel)
195{
196 return db7430_power_on(to_db7430(panel));
197}
198
199static int db7430_enable(struct drm_panel *panel)
200{
201 struct db7430 *db = to_db7430(panel);
202 struct mipi_dbi *dbi = &db->dbi;
203
204
205 mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
206 msleep(20);
207
208
209 mipi_dbi_command(dbi, DB7430_UNKNOWN_D4, 0x52, 0x5e);
210 mipi_dbi_command(dbi, DB7430_UNKNOWN_F8, 0x01, 0xf5, 0xf2, 0x71, 0x44);
211 mipi_dbi_command(dbi, DB7430_UNKNOWN_FC, 0x00, 0x08);
212 msleep(150);
213
214
215 mipi_dbi_command(dbi, DB7430_UNKNOWN_B4, 0x0f, 0x00, 0x50);
216 mipi_dbi_command(dbi, DB7430_USER_SELECT, 0x80);
217 mipi_dbi_command(dbi, DB7430_UNKNOWN_B7, 0x24);
218 mipi_dbi_command(dbi, DB7430_UNKNOWN_B8, 0x01);
219
220
221 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
222
223 return 0;
224}
225
226
227
228
229
230
231static int db7430_get_modes(struct drm_panel *panel,
232 struct drm_connector *connector)
233{
234 struct db7430 *db = to_db7430(panel);
235 struct drm_display_mode *mode;
236 static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
237
238 mode = drm_mode_duplicate(connector->dev, &db7430_480_800_mode);
239 if (!mode) {
240 dev_err(db->dev, "failed to add mode\n");
241 return -ENOMEM;
242 }
243
244 connector->display_info.bpc = 8;
245 connector->display_info.width_mm = mode->width_mm;
246 connector->display_info.height_mm = mode->height_mm;
247 connector->display_info.bus_flags =
248 DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
249 drm_display_info_set_bus_formats(&connector->display_info,
250 &bus_format, 1);
251
252 drm_mode_set_name(mode);
253 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
254
255 drm_mode_probed_add(connector, mode);
256
257 return 1;
258}
259
260static const struct drm_panel_funcs db7430_drm_funcs = {
261 .disable = db7430_disable,
262 .unprepare = db7430_unprepare,
263 .prepare = db7430_prepare,
264 .enable = db7430_enable,
265 .get_modes = db7430_get_modes,
266};
267
268static int db7430_probe(struct spi_device *spi)
269{
270 struct device *dev = &spi->dev;
271 struct db7430 *db;
272 int ret;
273
274 db = devm_kzalloc(dev, sizeof(*db), GFP_KERNEL);
275 if (!db)
276 return -ENOMEM;
277 db->dev = dev;
278
279
280
281
282
283 db->regulators[0].supply = "vci";
284 db->regulators[1].supply = "vccio";
285 ret = devm_regulator_bulk_get(dev,
286 ARRAY_SIZE(db->regulators),
287 db->regulators);
288 if (ret)
289 return dev_err_probe(dev, ret, "failed to get regulators\n");
290
291 db->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
292 if (IS_ERR(db->reset)) {
293 ret = PTR_ERR(db->reset);
294 return dev_err_probe(dev, ret, "no RESET GPIO\n");
295 }
296
297 ret = mipi_dbi_spi_init(spi, &db->dbi, NULL);
298 if (ret)
299 return dev_err_probe(dev, ret, "MIPI DBI init failed\n");
300
301 drm_panel_init(&db->panel, dev, &db7430_drm_funcs,
302 DRM_MODE_CONNECTOR_DPI);
303
304
305 ret = drm_panel_of_backlight(&db->panel);
306 if (ret)
307 return dev_err_probe(dev, ret, "failed to add backlight\n");
308
309 spi_set_drvdata(spi, db);
310
311 drm_panel_add(&db->panel);
312 dev_dbg(dev, "added panel\n");
313
314 return 0;
315}
316
317static void db7430_remove(struct spi_device *spi)
318{
319 struct db7430 *db = spi_get_drvdata(spi);
320
321 drm_panel_remove(&db->panel);
322}
323
324
325
326
327
328static const struct of_device_id db7430_match[] = {
329 { .compatible = "samsung,lms397kf04", },
330 {},
331};
332MODULE_DEVICE_TABLE(of, db7430_match);
333
334static struct spi_driver db7430_driver = {
335 .probe = db7430_probe,
336 .remove = db7430_remove,
337 .driver = {
338 .name = "db7430-panel",
339 .of_match_table = db7430_match,
340 },
341};
342module_spi_driver(db7430_driver);
343
344MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
345MODULE_DESCRIPTION("Samsung DB7430 panel driver");
346MODULE_LICENSE("GPL v2");
347