]>
Commit | Line | Data |
---|---|---|
c296d5f9 TP |
1 | /* |
2 | * Copyright (C) 2013 Noralf Tronnes | |
3 | * | |
4 | * This driver is inspired by: | |
5 | * st7735fb.c, Copyright (C) 2011, Matt Porter | |
6 | * broadsheetfb.c, Copyright (C) 2008, Jaya Kumar | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
c296d5f9 TP |
17 | */ |
18 | ||
19 | #include <linux/module.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/errno.h> | |
22 | #include <linux/string.h> | |
23 | #include <linux/mm.h> | |
24 | #include <linux/vmalloc.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/init.h> | |
27 | #include <linux/fb.h> | |
28 | #include <linux/gpio.h> | |
29 | #include <linux/spi/spi.h> | |
30 | #include <linux/delay.h> | |
31 | #include <linux/uaccess.h> | |
32 | #include <linux/backlight.h> | |
33 | #include <linux/platform_device.h> | |
34 | #include <linux/spinlock.h> | |
c296d5f9 TP |
35 | #include <linux/of.h> |
36 | #include <linux/of_gpio.h> | |
a79cb390 | 37 | #include <video/mipi_display.h> |
c296d5f9 TP |
38 | |
39 | #include "fbtft.h" | |
d9fabbde | 40 | #include "internal.h" |
c296d5f9 TP |
41 | |
42 | static unsigned long debug; | |
ced04842 | 43 | module_param(debug, ulong, 0000); |
c296d5f9 TP |
44 | MODULE_PARM_DESC(debug, "override device debug level"); |
45 | ||
60da7020 HK |
46 | int fbtft_write_buf_dc(struct fbtft_par *par, void *buf, size_t len, int dc) |
47 | { | |
48 | int ret; | |
49 | ||
50 | if (gpio_is_valid(par->gpio.dc)) | |
51 | gpio_set_value(par->gpio.dc, dc); | |
52 | ||
53 | ret = par->fbtftops.write(par, buf, len); | |
54 | if (ret < 0) | |
55 | dev_err(par->info->device, | |
56 | "write() failed and returned %d\n", ret); | |
57 | return ret; | |
58 | } | |
59 | EXPORT_SYMBOL(fbtft_write_buf_dc); | |
60 | ||
c296d5f9 | 61 | void fbtft_dbg_hex(const struct device *dev, int groupsize, |
4da2f4b7 | 62 | void *buf, size_t len, const char *fmt, ...) |
c296d5f9 TP |
63 | { |
64 | va_list args; | |
65 | static char textbuf[512]; | |
66 | char *text = textbuf; | |
67 | size_t text_len; | |
68 | ||
69 | va_start(args, fmt); | |
70 | text_len = vscnprintf(text, sizeof(textbuf), fmt, args); | |
71 | va_end(args); | |
72 | ||
73 | hex_dump_to_buffer(buf, len, 32, groupsize, text + text_len, | |
4da2f4b7 | 74 | 512 - text_len, false); |
c296d5f9 TP |
75 | |
76 | if (len > 32) | |
77 | dev_info(dev, "%s ...\n", text); | |
78 | else | |
79 | dev_info(dev, "%s\n", text); | |
80 | } | |
81 | EXPORT_SYMBOL(fbtft_dbg_hex); | |
82 | ||
ed208436 | 83 | static unsigned long fbtft_request_gpios_match(struct fbtft_par *par, |
4da2f4b7 | 84 | const struct fbtft_gpio *gpio) |
c296d5f9 TP |
85 | { |
86 | int ret; | |
0d0d4d21 | 87 | unsigned int val; |
c296d5f9 TP |
88 | |
89 | fbtft_par_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, "%s('%s')\n", | |
4da2f4b7 | 90 | __func__, gpio->name); |
c296d5f9 TP |
91 | |
92 | if (strcasecmp(gpio->name, "reset") == 0) { | |
93 | par->gpio.reset = gpio->gpio; | |
94 | return GPIOF_OUT_INIT_HIGH; | |
95 | } else if (strcasecmp(gpio->name, "dc") == 0) { | |
96 | par->gpio.dc = gpio->gpio; | |
97 | return GPIOF_OUT_INIT_LOW; | |
98 | } else if (strcasecmp(gpio->name, "cs") == 0) { | |
99 | par->gpio.cs = gpio->gpio; | |
100 | return GPIOF_OUT_INIT_HIGH; | |
101 | } else if (strcasecmp(gpio->name, "wr") == 0) { | |
102 | par->gpio.wr = gpio->gpio; | |
103 | return GPIOF_OUT_INIT_HIGH; | |
104 | } else if (strcasecmp(gpio->name, "rd") == 0) { | |
105 | par->gpio.rd = gpio->gpio; | |
106 | return GPIOF_OUT_INIT_HIGH; | |
107 | } else if (strcasecmp(gpio->name, "latch") == 0) { | |
108 | par->gpio.latch = gpio->gpio; | |
109 | return GPIOF_OUT_INIT_LOW; | |
110 | } else if (gpio->name[0] == 'd' && gpio->name[1] == 'b') { | |
0d0d4d21 | 111 | ret = kstrtouint(&gpio->name[2], 10, &val); |
c296d5f9 TP |
112 | if (ret == 0 && val < 16) { |
113 | par->gpio.db[val] = gpio->gpio; | |
114 | return GPIOF_OUT_INIT_LOW; | |
115 | } | |
116 | } else if (strcasecmp(gpio->name, "led") == 0) { | |
117 | par->gpio.led[0] = gpio->gpio; | |
118 | return GPIOF_OUT_INIT_LOW; | |
119 | } else if (strcasecmp(gpio->name, "led_") == 0) { | |
120 | par->gpio.led[0] = gpio->gpio; | |
121 | return GPIOF_OUT_INIT_HIGH; | |
122 | } | |
123 | ||
124 | return FBTFT_GPIO_NO_MATCH; | |
125 | } | |
126 | ||
ed208436 | 127 | static int fbtft_request_gpios(struct fbtft_par *par) |
c296d5f9 TP |
128 | { |
129 | struct fbtft_platform_data *pdata = par->pdata; | |
130 | const struct fbtft_gpio *gpio; | |
131 | unsigned long flags; | |
132 | int ret; | |
133 | ||
ed20c6fb MR |
134 | if (!(pdata && pdata->gpios)) |
135 | return 0; | |
136 | ||
137 | gpio = pdata->gpios; | |
138 | while (gpio->name[0]) { | |
139 | flags = FBTFT_GPIO_NO_MATCH; | |
140 | /* if driver provides match function, try it first, | |
e4d1bff1 AJ |
141 | * if no match use our own |
142 | */ | |
ed20c6fb MR |
143 | if (par->fbtftops.request_gpios_match) |
144 | flags = par->fbtftops.request_gpios_match(par, gpio); | |
145 | if (flags == FBTFT_GPIO_NO_MATCH) | |
146 | flags = fbtft_request_gpios_match(par, gpio); | |
147 | if (flags != FBTFT_GPIO_NO_MATCH) { | |
148 | ret = devm_gpio_request_one(par->info->device, | |
149 | gpio->gpio, flags, | |
150 | par->info->device->driver->name); | |
151 | if (ret < 0) { | |
152 | dev_err(par->info->device, | |
153 | "%s: gpio_request_one('%s'=%d) failed with %d\n", | |
154 | __func__, gpio->name, | |
155 | gpio->gpio, ret); | |
156 | return ret; | |
c296d5f9 | 157 | } |
ed20c6fb | 158 | fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, |
4da2f4b7 GS |
159 | "%s: '%s' = GPIO%d\n", |
160 | __func__, gpio->name, gpio->gpio); | |
c296d5f9 | 161 | } |
ed20c6fb | 162 | gpio++; |
c296d5f9 TP |
163 | } |
164 | ||
165 | return 0; | |
166 | } | |
167 | ||
168 | #ifdef CONFIG_OF | |
169 | static int fbtft_request_one_gpio(struct fbtft_par *par, | |
170 | const char *name, int index, int *gpiop) | |
171 | { | |
172 | struct device *dev = par->info->device; | |
173 | struct device_node *node = dev->of_node; | |
174 | int gpio, flags, ret = 0; | |
175 | enum of_gpio_flags of_flags; | |
176 | ||
177 | if (of_find_property(node, name, NULL)) { | |
178 | gpio = of_get_named_gpio_flags(node, name, index, &of_flags); | |
179 | if (gpio == -ENOENT) | |
180 | return 0; | |
181 | if (gpio == -EPROBE_DEFER) | |
182 | return gpio; | |
183 | if (gpio < 0) { | |
184 | dev_err(dev, | |
185 | "failed to get '%s' from DT\n", name); | |
186 | return gpio; | |
187 | } | |
188 | ||
189 | /* active low translates to initially low */ | |
190 | flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : | |
191 | GPIOF_OUT_INIT_HIGH; | |
192 | ret = devm_gpio_request_one(dev, gpio, flags, | |
4da2f4b7 | 193 | dev->driver->name); |
c296d5f9 TP |
194 | if (ret) { |
195 | dev_err(dev, | |
196 | "gpio_request_one('%s'=%d) failed with %d\n", | |
197 | name, gpio, ret); | |
198 | return ret; | |
199 | } | |
200 | if (gpiop) | |
201 | *gpiop = gpio; | |
202 | fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n", | |
4da2f4b7 | 203 | __func__, name, gpio); |
c296d5f9 TP |
204 | } |
205 | ||
206 | return ret; | |
207 | } | |
208 | ||
209 | static int fbtft_request_gpios_dt(struct fbtft_par *par) | |
210 | { | |
211 | int i; | |
212 | int ret; | |
213 | ||
214 | if (!par->info->device->of_node) | |
215 | return -EINVAL; | |
216 | ||
217 | ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset); | |
218 | if (ret) | |
219 | return ret; | |
220 | ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc); | |
221 | if (ret) | |
222 | return ret; | |
223 | ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd); | |
224 | if (ret) | |
225 | return ret; | |
226 | ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr); | |
227 | if (ret) | |
228 | return ret; | |
229 | ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs); | |
230 | if (ret) | |
231 | return ret; | |
232 | ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch); | |
233 | if (ret) | |
234 | return ret; | |
235 | for (i = 0; i < 16; i++) { | |
236 | ret = fbtft_request_one_gpio(par, "db-gpios", i, | |
4da2f4b7 | 237 | &par->gpio.db[i]); |
c296d5f9 TP |
238 | if (ret) |
239 | return ret; | |
240 | ret = fbtft_request_one_gpio(par, "led-gpios", i, | |
4da2f4b7 | 241 | &par->gpio.led[i]); |
c296d5f9 TP |
242 | if (ret) |
243 | return ret; | |
244 | ret = fbtft_request_one_gpio(par, "aux-gpios", i, | |
4da2f4b7 | 245 | &par->gpio.aux[i]); |
c296d5f9 TP |
246 | if (ret) |
247 | return ret; | |
248 | } | |
249 | ||
250 | return 0; | |
251 | } | |
252 | #endif | |
253 | ||
254 | #ifdef CONFIG_FB_BACKLIGHT | |
ed208436 | 255 | static int fbtft_backlight_update_status(struct backlight_device *bd) |
c296d5f9 TP |
256 | { |
257 | struct fbtft_par *par = bl_get_data(bd); | |
258 | bool polarity = !!(bd->props.state & BL_CORE_DRIVER1); | |
259 | ||
260 | fbtft_par_dbg(DEBUG_BACKLIGHT, par, | |
261 | "%s: polarity=%d, power=%d, fb_blank=%d\n", | |
262 | __func__, polarity, bd->props.power, bd->props.fb_blank); | |
263 | ||
1007e613 MS |
264 | if ((bd->props.power == FB_BLANK_UNBLANK) && |
265 | (bd->props.fb_blank == FB_BLANK_UNBLANK)) | |
c296d5f9 TP |
266 | gpio_set_value(par->gpio.led[0], polarity); |
267 | else | |
268 | gpio_set_value(par->gpio.led[0], !polarity); | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
ed208436 | 273 | static int fbtft_backlight_get_brightness(struct backlight_device *bd) |
c296d5f9 TP |
274 | { |
275 | return bd->props.brightness; | |
276 | } | |
277 | ||
278 | void fbtft_unregister_backlight(struct fbtft_par *par) | |
279 | { | |
c296d5f9 TP |
280 | if (par->info->bl_dev) { |
281 | par->info->bl_dev->props.power = FB_BLANK_POWERDOWN; | |
282 | backlight_update_status(par->info->bl_dev); | |
c296d5f9 TP |
283 | backlight_device_unregister(par->info->bl_dev); |
284 | par->info->bl_dev = NULL; | |
285 | } | |
286 | } | |
287 | ||
c18d116d MR |
288 | static const struct backlight_ops fbtft_bl_ops = { |
289 | .get_brightness = fbtft_backlight_get_brightness, | |
290 | .update_status = fbtft_backlight_update_status, | |
291 | }; | |
292 | ||
c296d5f9 TP |
293 | void fbtft_register_backlight(struct fbtft_par *par) |
294 | { | |
295 | struct backlight_device *bd; | |
296 | struct backlight_properties bl_props = { 0, }; | |
c296d5f9 | 297 | |
c296d5f9 TP |
298 | if (par->gpio.led[0] == -1) { |
299 | fbtft_par_dbg(DEBUG_BACKLIGHT, par, | |
4da2f4b7 | 300 | "%s(): led pin not set, exiting.\n", __func__); |
c296d5f9 TP |
301 | return; |
302 | } | |
303 | ||
c296d5f9 TP |
304 | bl_props.type = BACKLIGHT_RAW; |
305 | /* Assume backlight is off, get polarity from current state of pin */ | |
306 | bl_props.power = FB_BLANK_POWERDOWN; | |
307 | if (!gpio_get_value(par->gpio.led[0])) | |
308 | bl_props.state |= BL_CORE_DRIVER1; | |
309 | ||
310 | bd = backlight_device_register(dev_driver_string(par->info->device), | |
1007e613 MS |
311 | par->info->device, par, |
312 | &fbtft_bl_ops, &bl_props); | |
c296d5f9 TP |
313 | if (IS_ERR(bd)) { |
314 | dev_err(par->info->device, | |
315 | "cannot register backlight device (%ld)\n", | |
316 | PTR_ERR(bd)); | |
317 | return; | |
318 | } | |
319 | par->info->bl_dev = bd; | |
320 | ||
321 | if (!par->fbtftops.unregister_backlight) | |
322 | par->fbtftops.unregister_backlight = fbtft_unregister_backlight; | |
323 | } | |
324 | #else | |
325 | void fbtft_register_backlight(struct fbtft_par *par) { }; | |
326 | void fbtft_unregister_backlight(struct fbtft_par *par) { }; | |
327 | #endif | |
328 | EXPORT_SYMBOL(fbtft_register_backlight); | |
329 | EXPORT_SYMBOL(fbtft_unregister_backlight); | |
330 | ||
ed208436 KA |
331 | static void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, |
332 | int ye) | |
c296d5f9 | 333 | { |
a79cb390 PL |
334 | write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, |
335 | (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF); | |
c296d5f9 | 336 | |
a79cb390 PL |
337 | write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, |
338 | (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF); | |
c296d5f9 | 339 | |
a79cb390 | 340 | write_reg(par, MIPI_DCS_WRITE_MEMORY_START); |
c296d5f9 TP |
341 | } |
342 | ||
ed208436 | 343 | static void fbtft_reset(struct fbtft_par *par) |
c296d5f9 TP |
344 | { |
345 | if (par->gpio.reset == -1) | |
346 | return; | |
347 | fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__); | |
9ed2bfb3 AS |
348 | gpio_set_value_cansleep(par->gpio.reset, 0); |
349 | usleep_range(20, 40); | |
350 | gpio_set_value_cansleep(par->gpio.reset, 1); | |
351 | msleep(120); | |
c296d5f9 TP |
352 | } |
353 | ||
1c41494a MY |
354 | static void fbtft_update_display(struct fbtft_par *par, unsigned int start_line, |
355 | unsigned int end_line) | |
c296d5f9 TP |
356 | { |
357 | size_t offset, len; | |
367e8560 | 358 | ktime_t ts_start, ts_end; |
c296d5f9 TP |
359 | long fps, throughput; |
360 | bool timeit = false; | |
361 | int ret = 0; | |
362 | ||
1007e613 MS |
363 | if (unlikely(par->debug & (DEBUG_TIME_FIRST_UPDATE | |
364 | DEBUG_TIME_EACH_UPDATE))) { | |
6ba67a5a | 365 | if ((par->debug & DEBUG_TIME_EACH_UPDATE) || |
4da2f4b7 GS |
366 | ((par->debug & DEBUG_TIME_FIRST_UPDATE) && |
367 | !par->first_update_done)) { | |
367e8560 | 368 | ts_start = ktime_get(); |
c296d5f9 TP |
369 | timeit = true; |
370 | } | |
371 | } | |
372 | ||
373 | /* Sanity checks */ | |
374 | if (start_line > end_line) { | |
375 | dev_warn(par->info->device, | |
1007e613 MS |
376 | "%s: start_line=%u is larger than end_line=%u. Shouldn't happen, will do full display update\n", |
377 | __func__, start_line, end_line); | |
c296d5f9 TP |
378 | start_line = 0; |
379 | end_line = par->info->var.yres - 1; | |
380 | } | |
1007e613 MS |
381 | if (start_line > par->info->var.yres - 1 || |
382 | end_line > par->info->var.yres - 1) { | |
c296d5f9 TP |
383 | dev_warn(par->info->device, |
384 | "%s: start_line=%u or end_line=%u is larger than max=%d. Shouldn't happen, will do full display update\n", | |
1007e613 MS |
385 | __func__, start_line, |
386 | end_line, par->info->var.yres - 1); | |
c296d5f9 TP |
387 | start_line = 0; |
388 | end_line = par->info->var.yres - 1; | |
389 | } | |
390 | ||
391 | fbtft_par_dbg(DEBUG_UPDATE_DISPLAY, par, "%s(start_line=%u, end_line=%u)\n", | |
4da2f4b7 | 392 | __func__, start_line, end_line); |
c296d5f9 TP |
393 | |
394 | if (par->fbtftops.set_addr_win) | |
395 | par->fbtftops.set_addr_win(par, 0, start_line, | |
94c0a544 | 396 | par->info->var.xres - 1, end_line); |
c296d5f9 TP |
397 | |
398 | offset = start_line * par->info->fix.line_length; | |
399 | len = (end_line - start_line + 1) * par->info->fix.line_length; | |
400 | ret = par->fbtftops.write_vmem(par, offset, len); | |
401 | if (ret < 0) | |
402 | dev_err(par->info->device, | |
403 | "%s: write_vmem failed to update display buffer\n", | |
404 | __func__); | |
405 | ||
406 | if (unlikely(timeit)) { | |
367e8560 | 407 | ts_end = ktime_get(); |
fc1e2c8e | 408 | if (!ktime_to_ns(par->update_time)) |
367e8560 KS |
409 | par->update_time = ts_start; |
410 | ||
367e8560 | 411 | fps = ktime_us_delta(ts_start, par->update_time); |
fc1e2c8e | 412 | par->update_time = ts_start; |
c296d5f9 TP |
413 | fps = fps ? 1000000 / fps : 0; |
414 | ||
367e8560 | 415 | throughput = ktime_us_delta(ts_end, ts_start); |
c296d5f9 TP |
416 | throughput = throughput ? (len * 1000) / throughput : 0; |
417 | throughput = throughput * 1000 / 1024; | |
418 | ||
419 | dev_info(par->info->device, | |
4da2f4b7 GS |
420 | "Display update: %ld kB/s, fps=%ld\n", |
421 | throughput, fps); | |
c296d5f9 TP |
422 | par->first_update_done = true; |
423 | } | |
424 | } | |
425 | ||
ed208436 | 426 | static void fbtft_mkdirty(struct fb_info *info, int y, int height) |
c296d5f9 TP |
427 | { |
428 | struct fbtft_par *par = info->par; | |
429 | struct fb_deferred_io *fbdefio = info->fbdefio; | |
430 | ||
431 | /* special case, needed ? */ | |
432 | if (y == -1) { | |
433 | y = 0; | |
434 | height = info->var.yres - 1; | |
435 | } | |
436 | ||
437 | /* Mark display lines/area as dirty */ | |
438 | spin_lock(&par->dirty_lock); | |
439 | if (y < par->dirty_lines_start) | |
440 | par->dirty_lines_start = y; | |
441 | if (y + height - 1 > par->dirty_lines_end) | |
442 | par->dirty_lines_end = y + height - 1; | |
443 | spin_unlock(&par->dirty_lock); | |
444 | ||
445 | /* Schedule deferred_io to update display (no-op if already on queue)*/ | |
446 | schedule_delayed_work(&info->deferred_work, fbdefio->delay); | |
447 | } | |
448 | ||
ed208436 | 449 | static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist) |
c296d5f9 TP |
450 | { |
451 | struct fbtft_par *par = info->par; | |
1c41494a | 452 | unsigned int dirty_lines_start, dirty_lines_end; |
c296d5f9 TP |
453 | struct page *page; |
454 | unsigned long index; | |
1c41494a | 455 | unsigned int y_low = 0, y_high = 0; |
c296d5f9 TP |
456 | int count = 0; |
457 | ||
458 | spin_lock(&par->dirty_lock); | |
459 | dirty_lines_start = par->dirty_lines_start; | |
460 | dirty_lines_end = par->dirty_lines_end; | |
461 | /* set display line markers as clean */ | |
462 | par->dirty_lines_start = par->info->var.yres - 1; | |
463 | par->dirty_lines_end = 0; | |
464 | spin_unlock(&par->dirty_lock); | |
465 | ||
466 | /* Mark display lines as dirty */ | |
467 | list_for_each_entry(page, pagelist, lru) { | |
468 | count++; | |
469 | index = page->index << PAGE_SHIFT; | |
470 | y_low = index / info->fix.line_length; | |
471 | y_high = (index + PAGE_SIZE - 1) / info->fix.line_length; | |
b38c760a | 472 | dev_dbg(info->device, |
c296d5f9 TP |
473 | "page->index=%lu y_low=%d y_high=%d\n", |
474 | page->index, y_low, y_high); | |
475 | if (y_high > info->var.yres - 1) | |
476 | y_high = info->var.yres - 1; | |
477 | if (y_low < dirty_lines_start) | |
478 | dirty_lines_start = y_low; | |
479 | if (y_high > dirty_lines_end) | |
480 | dirty_lines_end = y_high; | |
481 | } | |
482 | ||
483 | par->fbtftops.update_display(info->par, | |
484 | dirty_lines_start, dirty_lines_end); | |
485 | } | |
486 | ||
ed208436 KA |
487 | static void fbtft_fb_fillrect(struct fb_info *info, |
488 | const struct fb_fillrect *rect) | |
c296d5f9 TP |
489 | { |
490 | struct fbtft_par *par = info->par; | |
491 | ||
b38c760a | 492 | dev_dbg(info->dev, |
c296d5f9 TP |
493 | "%s: dx=%d, dy=%d, width=%d, height=%d\n", |
494 | __func__, rect->dx, rect->dy, rect->width, rect->height); | |
495 | sys_fillrect(info, rect); | |
496 | ||
497 | par->fbtftops.mkdirty(info, rect->dy, rect->height); | |
498 | } | |
499 | ||
ed208436 KA |
500 | static void fbtft_fb_copyarea(struct fb_info *info, |
501 | const struct fb_copyarea *area) | |
c296d5f9 TP |
502 | { |
503 | struct fbtft_par *par = info->par; | |
504 | ||
b38c760a | 505 | dev_dbg(info->dev, |
c296d5f9 TP |
506 | "%s: dx=%d, dy=%d, width=%d, height=%d\n", |
507 | __func__, area->dx, area->dy, area->width, area->height); | |
508 | sys_copyarea(info, area); | |
509 | ||
510 | par->fbtftops.mkdirty(info, area->dy, area->height); | |
511 | } | |
512 | ||
ed208436 KA |
513 | static void fbtft_fb_imageblit(struct fb_info *info, |
514 | const struct fb_image *image) | |
c296d5f9 TP |
515 | { |
516 | struct fbtft_par *par = info->par; | |
517 | ||
b38c760a | 518 | dev_dbg(info->dev, |
c296d5f9 TP |
519 | "%s: dx=%d, dy=%d, width=%d, height=%d\n", |
520 | __func__, image->dx, image->dy, image->width, image->height); | |
521 | sys_imageblit(info, image); | |
522 | ||
523 | par->fbtftops.mkdirty(info, image->dy, image->height); | |
524 | } | |
525 | ||
ed208436 KA |
526 | static ssize_t fbtft_fb_write(struct fb_info *info, const char __user *buf, |
527 | size_t count, loff_t *ppos) | |
c296d5f9 TP |
528 | { |
529 | struct fbtft_par *par = info->par; | |
530 | ssize_t res; | |
531 | ||
b38c760a | 532 | dev_dbg(info->dev, |
c296d5f9 TP |
533 | "%s: count=%zd, ppos=%llu\n", __func__, count, *ppos); |
534 | res = fb_sys_write(info, buf, count, ppos); | |
535 | ||
e4d1bff1 | 536 | /* TODO: only mark changed area update all for now */ |
c296d5f9 TP |
537 | par->fbtftops.mkdirty(info, -1, 0); |
538 | ||
539 | return res; | |
540 | } | |
541 | ||
542 | /* from pxafb.c */ | |
1c41494a | 543 | static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) |
c296d5f9 TP |
544 | { |
545 | chan &= 0xffff; | |
546 | chan >>= 16 - bf->length; | |
547 | return chan << bf->offset; | |
548 | } | |
549 | ||
1c41494a MY |
550 | static int fbtft_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, |
551 | unsigned int blue, unsigned int transp, | |
ed208436 | 552 | struct fb_info *info) |
c296d5f9 | 553 | { |
1c41494a | 554 | unsigned int val; |
c296d5f9 TP |
555 | int ret = 1; |
556 | ||
b38c760a | 557 | dev_dbg(info->dev, |
c296d5f9 TP |
558 | "%s(regno=%u, red=0x%X, green=0x%X, blue=0x%X, trans=0x%X)\n", |
559 | __func__, regno, red, green, blue, transp); | |
560 | ||
561 | switch (info->fix.visual) { | |
562 | case FB_VISUAL_TRUECOLOR: | |
563 | if (regno < 16) { | |
564 | u32 *pal = info->pseudo_palette; | |
565 | ||
566 | val = chan_to_field(red, &info->var.red); | |
567 | val |= chan_to_field(green, &info->var.green); | |
568 | val |= chan_to_field(blue, &info->var.blue); | |
569 | ||
570 | pal[regno] = val; | |
571 | ret = 0; | |
572 | } | |
573 | break; | |
c296d5f9 TP |
574 | } |
575 | return ret; | |
576 | } | |
577 | ||
ed208436 | 578 | static int fbtft_fb_blank(int blank, struct fb_info *info) |
c296d5f9 TP |
579 | { |
580 | struct fbtft_par *par = info->par; | |
581 | int ret = -EINVAL; | |
582 | ||
b38c760a | 583 | dev_dbg(info->dev, "%s(blank=%d)\n", |
c296d5f9 TP |
584 | __func__, blank); |
585 | ||
586 | if (!par->fbtftops.blank) | |
587 | return ret; | |
588 | ||
589 | switch (blank) { | |
590 | case FB_BLANK_POWERDOWN: | |
591 | case FB_BLANK_VSYNC_SUSPEND: | |
592 | case FB_BLANK_HSYNC_SUSPEND: | |
593 | case FB_BLANK_NORMAL: | |
594 | ret = par->fbtftops.blank(par, true); | |
595 | break; | |
596 | case FB_BLANK_UNBLANK: | |
597 | ret = par->fbtftops.blank(par, false); | |
598 | break; | |
599 | } | |
600 | return ret; | |
601 | } | |
602 | ||
ed208436 | 603 | static void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src) |
c296d5f9 TP |
604 | { |
605 | if (src->write) | |
606 | dst->write = src->write; | |
607 | if (src->read) | |
608 | dst->read = src->read; | |
609 | if (src->write_vmem) | |
610 | dst->write_vmem = src->write_vmem; | |
611 | if (src->write_register) | |
612 | dst->write_register = src->write_register; | |
613 | if (src->set_addr_win) | |
614 | dst->set_addr_win = src->set_addr_win; | |
615 | if (src->reset) | |
616 | dst->reset = src->reset; | |
617 | if (src->mkdirty) | |
618 | dst->mkdirty = src->mkdirty; | |
619 | if (src->update_display) | |
620 | dst->update_display = src->update_display; | |
621 | if (src->init_display) | |
622 | dst->init_display = src->init_display; | |
623 | if (src->blank) | |
624 | dst->blank = src->blank; | |
625 | if (src->request_gpios_match) | |
626 | dst->request_gpios_match = src->request_gpios_match; | |
627 | if (src->request_gpios) | |
628 | dst->request_gpios = src->request_gpios; | |
629 | if (src->verify_gpios) | |
630 | dst->verify_gpios = src->verify_gpios; | |
631 | if (src->register_backlight) | |
632 | dst->register_backlight = src->register_backlight; | |
633 | if (src->unregister_backlight) | |
634 | dst->unregister_backlight = src->unregister_backlight; | |
635 | if (src->set_var) | |
636 | dst->set_var = src->set_var; | |
637 | if (src->set_gamma) | |
638 | dst->set_gamma = src->set_gamma; | |
639 | } | |
640 | ||
641 | /** | |
642 | * fbtft_framebuffer_alloc - creates a new frame buffer info structure | |
643 | * | |
644 | * @display: pointer to structure describing the display | |
645 | * @dev: pointer to the device for this fb, this can be NULL | |
646 | * | |
647 | * Creates a new frame buffer info structure. | |
648 | * | |
649 | * Also creates and populates the following structures: | |
650 | * info->fbops | |
651 | * info->fbdefio | |
652 | * info->pseudo_palette | |
653 | * par->fbtftops | |
654 | * par->txbuf | |
655 | * | |
656 | * Returns the new structure, or NULL if an error occurred. | |
657 | * | |
658 | */ | |
659 | struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, | |
ad6d8812 NT |
660 | struct device *dev, |
661 | struct fbtft_platform_data *pdata) | |
c296d5f9 TP |
662 | { |
663 | struct fb_info *info; | |
664 | struct fbtft_par *par; | |
665 | struct fb_ops *fbops = NULL; | |
666 | struct fb_deferred_io *fbdefio = NULL; | |
c296d5f9 TP |
667 | u8 *vmem = NULL; |
668 | void *txbuf = NULL; | |
669 | void *buf = NULL; | |
1c41494a MY |
670 | unsigned int width; |
671 | unsigned int height; | |
c296d5f9 | 672 | int txbuflen = display->txbuflen; |
1c41494a MY |
673 | unsigned int bpp = display->bpp; |
674 | unsigned int fps = display->fps; | |
c296d5f9 | 675 | int vmem_size, i; |
0a859b31 | 676 | const s16 *init_sequence = display->init_sequence; |
c296d5f9 | 677 | char *gamma = display->gamma; |
22eb36b8 | 678 | u32 *gamma_curves = NULL; |
c296d5f9 TP |
679 | |
680 | /* sanity check */ | |
1007e613 MS |
681 | if (display->gamma_num * display->gamma_len > |
682 | FBTFT_GAMMA_MAX_VALUES_TOTAL) { | |
aed1c72e HM |
683 | dev_err(dev, "FBTFT_GAMMA_MAX_VALUES_TOTAL=%d is exceeded\n", |
684 | FBTFT_GAMMA_MAX_VALUES_TOTAL); | |
c296d5f9 TP |
685 | return NULL; |
686 | } | |
687 | ||
688 | /* defaults */ | |
689 | if (!fps) | |
690 | fps = 20; | |
691 | if (!bpp) | |
692 | bpp = 16; | |
693 | ||
694 | if (!pdata) { | |
695 | dev_err(dev, "platform data is missing\n"); | |
696 | return NULL; | |
697 | } | |
698 | ||
699 | /* override driver values? */ | |
700 | if (pdata->fps) | |
701 | fps = pdata->fps; | |
702 | if (pdata->txbuflen) | |
703 | txbuflen = pdata->txbuflen; | |
704 | if (pdata->display.init_sequence) | |
705 | init_sequence = pdata->display.init_sequence; | |
706 | if (pdata->gamma) | |
707 | gamma = pdata->gamma; | |
708 | if (pdata->display.debug) | |
709 | display->debug = pdata->display.debug; | |
710 | if (pdata->display.backlight) | |
711 | display->backlight = pdata->display.backlight; | |
712 | if (pdata->display.width) | |
713 | display->width = pdata->display.width; | |
714 | if (pdata->display.height) | |
715 | display->height = pdata->display.height; | |
716 | if (pdata->display.buswidth) | |
717 | display->buswidth = pdata->display.buswidth; | |
718 | if (pdata->display.regwidth) | |
719 | display->regwidth = pdata->display.regwidth; | |
720 | ||
721 | display->debug |= debug; | |
722 | fbtft_expand_debug_value(&display->debug); | |
723 | ||
724 | switch (pdata->rotate) { | |
725 | case 90: | |
726 | case 270: | |
727 | width = display->height; | |
728 | height = display->width; | |
729 | break; | |
730 | default: | |
731 | width = display->width; | |
732 | height = display->height; | |
733 | } | |
734 | ||
735 | vmem_size = display->width * display->height * bpp / 8; | |
736 | vmem = vzalloc(vmem_size); | |
737 | if (!vmem) | |
738 | goto alloc_fail; | |
739 | ||
740 | fbops = devm_kzalloc(dev, sizeof(struct fb_ops), GFP_KERNEL); | |
741 | if (!fbops) | |
742 | goto alloc_fail; | |
743 | ||
744 | fbdefio = devm_kzalloc(dev, sizeof(struct fb_deferred_io), GFP_KERNEL); | |
745 | if (!fbdefio) | |
746 | goto alloc_fail; | |
747 | ||
748 | buf = devm_kzalloc(dev, 128, GFP_KERNEL); | |
749 | if (!buf) | |
750 | goto alloc_fail; | |
751 | ||
752 | if (display->gamma_num && display->gamma_len) { | |
0c9ab986 AKC |
753 | gamma_curves = devm_kcalloc(dev, |
754 | display->gamma_num * | |
755 | display->gamma_len, | |
756 | sizeof(gamma_curves[0]), | |
757 | GFP_KERNEL); | |
c296d5f9 TP |
758 | if (!gamma_curves) |
759 | goto alloc_fail; | |
760 | } | |
761 | ||
762 | info = framebuffer_alloc(sizeof(struct fbtft_par), dev); | |
763 | if (!info) | |
764 | goto alloc_fail; | |
765 | ||
4b6dc179 | 766 | info->screen_buffer = vmem; |
c296d5f9 TP |
767 | info->fbops = fbops; |
768 | info->fbdefio = fbdefio; | |
769 | ||
770 | fbops->owner = dev->driver->owner; | |
771 | fbops->fb_read = fb_sys_read; | |
772 | fbops->fb_write = fbtft_fb_write; | |
773 | fbops->fb_fillrect = fbtft_fb_fillrect; | |
774 | fbops->fb_copyarea = fbtft_fb_copyarea; | |
775 | fbops->fb_imageblit = fbtft_fb_imageblit; | |
776 | fbops->fb_setcolreg = fbtft_fb_setcolreg; | |
777 | fbops->fb_blank = fbtft_fb_blank; | |
778 | ||
779 | fbdefio->delay = HZ/fps; | |
780 | fbdefio->deferred_io = fbtft_deferred_io; | |
781 | fb_deferred_io_init(info); | |
782 | ||
7fb0b152 | 783 | snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name); |
c296d5f9 TP |
784 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
785 | info->fix.visual = FB_VISUAL_TRUECOLOR; | |
786 | info->fix.xpanstep = 0; | |
787 | info->fix.ypanstep = 0; | |
788 | info->fix.ywrapstep = 0; | |
94c0a544 | 789 | info->fix.line_length = width * bpp / 8; |
c296d5f9 TP |
790 | info->fix.accel = FB_ACCEL_NONE; |
791 | info->fix.smem_len = vmem_size; | |
792 | ||
793 | info->var.rotate = pdata->rotate; | |
794 | info->var.xres = width; | |
795 | info->var.yres = height; | |
796 | info->var.xres_virtual = info->var.xres; | |
797 | info->var.yres_virtual = info->var.yres; | |
798 | info->var.bits_per_pixel = bpp; | |
799 | info->var.nonstd = 1; | |
800 | ||
801 | /* RGB565 */ | |
802 | info->var.red.offset = 11; | |
803 | info->var.red.length = 5; | |
804 | info->var.green.offset = 5; | |
805 | info->var.green.length = 6; | |
806 | info->var.blue.offset = 0; | |
807 | info->var.blue.length = 5; | |
808 | info->var.transp.offset = 0; | |
809 | info->var.transp.length = 0; | |
810 | ||
811 | info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; | |
812 | ||
813 | par = info->par; | |
814 | par->info = info; | |
ad6d8812 | 815 | par->pdata = pdata; |
c296d5f9 TP |
816 | par->debug = display->debug; |
817 | par->buf = buf; | |
818 | spin_lock_init(&par->dirty_lock); | |
819 | par->bgr = pdata->bgr; | |
820 | par->startbyte = pdata->startbyte; | |
821 | par->init_sequence = init_sequence; | |
822 | par->gamma.curves = gamma_curves; | |
823 | par->gamma.num_curves = display->gamma_num; | |
824 | par->gamma.num_values = display->gamma_len; | |
825 | mutex_init(&par->gamma.lock); | |
826 | info->pseudo_palette = par->pseudo_palette; | |
827 | ||
828 | if (par->gamma.curves && gamma) { | |
829 | if (fbtft_gamma_parse_str(par, | |
830 | par->gamma.curves, gamma, strlen(gamma))) | |
75d8929e | 831 | goto release_framebuf; |
c296d5f9 TP |
832 | } |
833 | ||
834 | /* Transmit buffer */ | |
835 | if (txbuflen == -1) | |
836 | txbuflen = vmem_size + 2; /* add in case startbyte is used */ | |
175f5a41 MS |
837 | if (txbuflen >= vmem_size + 2) |
838 | txbuflen = 0; | |
c296d5f9 TP |
839 | |
840 | #ifdef __LITTLE_ENDIAN | |
841 | if ((!txbuflen) && (bpp > 8)) | |
842 | txbuflen = PAGE_SIZE; /* need buffer for byteswapping */ | |
843 | #endif | |
844 | ||
845 | if (txbuflen > 0) { | |
7c3eaadd | 846 | txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_KERNEL); |
c296d5f9 | 847 | if (!txbuf) |
75d8929e | 848 | goto release_framebuf; |
c296d5f9 TP |
849 | par->txbuf.buf = txbuf; |
850 | par->txbuf.len = txbuflen; | |
851 | } | |
852 | ||
853 | /* Initialize gpios to disabled */ | |
854 | par->gpio.reset = -1; | |
855 | par->gpio.dc = -1; | |
856 | par->gpio.rd = -1; | |
857 | par->gpio.wr = -1; | |
858 | par->gpio.cs = -1; | |
859 | par->gpio.latch = -1; | |
860 | for (i = 0; i < 16; i++) { | |
861 | par->gpio.db[i] = -1; | |
862 | par->gpio.led[i] = -1; | |
863 | par->gpio.aux[i] = -1; | |
864 | } | |
865 | ||
866 | /* default fbtft operations */ | |
867 | par->fbtftops.write = fbtft_write_spi; | |
868 | par->fbtftops.read = fbtft_read_spi; | |
869 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; | |
870 | par->fbtftops.write_register = fbtft_write_reg8_bus8; | |
871 | par->fbtftops.set_addr_win = fbtft_set_addr_win; | |
872 | par->fbtftops.reset = fbtft_reset; | |
873 | par->fbtftops.mkdirty = fbtft_mkdirty; | |
874 | par->fbtftops.update_display = fbtft_update_display; | |
875 | par->fbtftops.request_gpios = fbtft_request_gpios; | |
876 | if (display->backlight) | |
877 | par->fbtftops.register_backlight = fbtft_register_backlight; | |
878 | ||
879 | /* use driver provided functions */ | |
880 | fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops); | |
881 | ||
882 | return info; | |
883 | ||
75d8929e NE |
884 | release_framebuf: |
885 | framebuffer_release(info); | |
886 | ||
c296d5f9 TP |
887 | alloc_fail: |
888 | vfree(vmem); | |
889 | ||
890 | return NULL; | |
891 | } | |
892 | EXPORT_SYMBOL(fbtft_framebuffer_alloc); | |
893 | ||
894 | /** | |
895 | * fbtft_framebuffer_release - frees up all memory used by the framebuffer | |
896 | * | |
897 | * @info: frame buffer info structure | |
898 | * | |
899 | */ | |
900 | void fbtft_framebuffer_release(struct fb_info *info) | |
901 | { | |
902 | fb_deferred_io_cleanup(info); | |
4b6dc179 | 903 | vfree(info->screen_buffer); |
c296d5f9 TP |
904 | framebuffer_release(info); |
905 | } | |
906 | EXPORT_SYMBOL(fbtft_framebuffer_release); | |
907 | ||
908 | /** | |
909 | * fbtft_register_framebuffer - registers a tft frame buffer device | |
910 | * @fb_info: frame buffer info structure | |
911 | * | |
912 | * Sets SPI driverdata if needed | |
913 | * Requests needed gpios. | |
914 | * Initializes display | |
915 | * Updates display. | |
916 | * Registers a frame buffer device @fb_info. | |
917 | * | |
918 | * Returns negative errno on error, or zero for success. | |
919 | * | |
920 | */ | |
921 | int fbtft_register_framebuffer(struct fb_info *fb_info) | |
922 | { | |
923 | int ret; | |
924 | char text1[50] = ""; | |
925 | char text2[50] = ""; | |
926 | struct fbtft_par *par = fb_info->par; | |
927 | struct spi_device *spi = par->spi; | |
928 | ||
929 | /* sanity checks */ | |
930 | if (!par->fbtftops.init_display) { | |
931 | dev_err(fb_info->device, "missing fbtftops.init_display()\n"); | |
932 | return -EINVAL; | |
933 | } | |
934 | ||
935 | if (spi) | |
936 | spi_set_drvdata(spi, fb_info); | |
937 | if (par->pdev) | |
938 | platform_set_drvdata(par->pdev, fb_info); | |
939 | ||
940 | ret = par->fbtftops.request_gpios(par); | |
941 | if (ret < 0) | |
942 | goto reg_fail; | |
943 | ||
944 | if (par->fbtftops.verify_gpios) { | |
945 | ret = par->fbtftops.verify_gpios(par); | |
946 | if (ret < 0) | |
947 | goto reg_fail; | |
948 | } | |
949 | ||
950 | ret = par->fbtftops.init_display(par); | |
951 | if (ret < 0) | |
952 | goto reg_fail; | |
953 | if (par->fbtftops.set_var) { | |
954 | ret = par->fbtftops.set_var(par); | |
955 | if (ret < 0) | |
956 | goto reg_fail; | |
957 | } | |
958 | ||
959 | /* update the entire display */ | |
960 | par->fbtftops.update_display(par, 0, par->info->var.yres - 1); | |
961 | ||
962 | if (par->fbtftops.set_gamma && par->gamma.curves) { | |
963 | ret = par->fbtftops.set_gamma(par, par->gamma.curves); | |
964 | if (ret) | |
965 | goto reg_fail; | |
966 | } | |
967 | ||
968 | if (par->fbtftops.register_backlight) | |
969 | par->fbtftops.register_backlight(par); | |
970 | ||
971 | ret = register_framebuffer(fb_info); | |
972 | if (ret < 0) | |
973 | goto reg_fail; | |
974 | ||
975 | fbtft_sysfs_init(par); | |
976 | ||
18c61421 | 977 | if (par->txbuf.buf && par->txbuf.len >= 1024) |
7c3eaadd | 978 | sprintf(text1, ", %zu KiB buffer memory", par->txbuf.len >> 10); |
c296d5f9 TP |
979 | if (spi) |
980 | sprintf(text2, ", spi%d.%d at %d MHz", spi->master->bus_num, | |
94c0a544 | 981 | spi->chip_select, spi->max_speed_hz / 1000000); |
c296d5f9 | 982 | dev_info(fb_info->dev, |
4da2f4b7 GS |
983 | "%s frame buffer, %dx%d, %d KiB video memory%s, fps=%lu%s\n", |
984 | fb_info->fix.id, fb_info->var.xres, fb_info->var.yres, | |
985 | fb_info->fix.smem_len >> 10, text1, | |
986 | HZ / fb_info->fbdefio->delay, text2); | |
c296d5f9 TP |
987 | |
988 | #ifdef CONFIG_FB_BACKLIGHT | |
989 | /* Turn on backlight if available */ | |
990 | if (fb_info->bl_dev) { | |
991 | fb_info->bl_dev->props.power = FB_BLANK_UNBLANK; | |
992 | fb_info->bl_dev->ops->update_status(fb_info->bl_dev); | |
993 | } | |
994 | #endif | |
995 | ||
996 | return 0; | |
997 | ||
998 | reg_fail: | |
999 | if (par->fbtftops.unregister_backlight) | |
1000 | par->fbtftops.unregister_backlight(par); | |
c296d5f9 TP |
1001 | |
1002 | return ret; | |
1003 | } | |
1004 | EXPORT_SYMBOL(fbtft_register_framebuffer); | |
1005 | ||
1006 | /** | |
1007 | * fbtft_unregister_framebuffer - releases a tft frame buffer device | |
1008 | * @fb_info: frame buffer info structure | |
1009 | * | |
1010 | * Frees SPI driverdata if needed | |
1011 | * Frees gpios. | |
1012 | * Unregisters frame buffer device. | |
1013 | * | |
1014 | */ | |
1015 | int fbtft_unregister_framebuffer(struct fb_info *fb_info) | |
1016 | { | |
1017 | struct fbtft_par *par = fb_info->par; | |
c296d5f9 | 1018 | |
c296d5f9 TP |
1019 | if (par->fbtftops.unregister_backlight) |
1020 | par->fbtftops.unregister_backlight(par); | |
1021 | fbtft_sysfs_exit(par); | |
11107ffe | 1022 | return unregister_framebuffer(fb_info); |
c296d5f9 TP |
1023 | } |
1024 | EXPORT_SYMBOL(fbtft_unregister_framebuffer); | |
1025 | ||
1026 | #ifdef CONFIG_OF | |
1027 | /** | |
1028 | * fbtft_init_display_dt() - Device Tree init_display() function | |
1029 | * @par: Driver data | |
1030 | * | |
1031 | * Return: 0 if successful, negative if error | |
1032 | */ | |
1033 | static int fbtft_init_display_dt(struct fbtft_par *par) | |
1034 | { | |
1035 | struct device_node *node = par->info->device->of_node; | |
1036 | struct property *prop; | |
1037 | const __be32 *p; | |
1038 | u32 val; | |
1039 | int buf[64], i, j; | |
c296d5f9 | 1040 | |
c296d5f9 TP |
1041 | if (!node) |
1042 | return -EINVAL; | |
1043 | ||
1044 | prop = of_find_property(node, "init", NULL); | |
1045 | p = of_prop_next_u32(prop, NULL, &val); | |
1046 | if (!p) | |
1047 | return -EINVAL; | |
dc4b2068 NT |
1048 | |
1049 | par->fbtftops.reset(par); | |
1050 | if (par->gpio.cs != -1) | |
1051 | gpio_set_value(par->gpio.cs, 0); /* Activate chip */ | |
1052 | ||
c296d5f9 TP |
1053 | while (p) { |
1054 | if (val & FBTFT_OF_INIT_CMD) { | |
1055 | val &= 0xFFFF; | |
1056 | i = 0; | |
1057 | while (p && !(val & 0xFFFF0000)) { | |
1058 | if (i > 63) { | |
1059 | dev_err(par->info->device, | |
1060 | "%s: Maximum register values exceeded\n", | |
1061 | __func__); | |
1062 | return -EINVAL; | |
1063 | } | |
1064 | buf[i++] = val; | |
1065 | p = of_prop_next_u32(prop, p, &val); | |
1066 | } | |
1067 | /* make debug message */ | |
c296d5f9 | 1068 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, |
4da2f4b7 | 1069 | "init: write_register:\n"); |
e6ffd1ba SM |
1070 | for (j = 0; j < i; j++) |
1071 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, | |
1072 | "buf[%d] = %02X\n", j, buf[j]); | |
c296d5f9 TP |
1073 | |
1074 | par->fbtftops.write_register(par, i, | |
1075 | buf[0], buf[1], buf[2], buf[3], | |
1076 | buf[4], buf[5], buf[6], buf[7], | |
1077 | buf[8], buf[9], buf[10], buf[11], | |
1078 | buf[12], buf[13], buf[14], buf[15], | |
1079 | buf[16], buf[17], buf[18], buf[19], | |
1080 | buf[20], buf[21], buf[22], buf[23], | |
1081 | buf[24], buf[25], buf[26], buf[27], | |
1082 | buf[28], buf[29], buf[30], buf[31], | |
1083 | buf[32], buf[33], buf[34], buf[35], | |
1084 | buf[36], buf[37], buf[38], buf[39], | |
1085 | buf[40], buf[41], buf[42], buf[43], | |
1086 | buf[44], buf[45], buf[46], buf[47], | |
1087 | buf[48], buf[49], buf[50], buf[51], | |
1088 | buf[52], buf[53], buf[54], buf[55], | |
1089 | buf[56], buf[57], buf[58], buf[59], | |
1090 | buf[60], buf[61], buf[62], buf[63]); | |
1091 | } else if (val & FBTFT_OF_INIT_DELAY) { | |
1092 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, | |
4da2f4b7 | 1093 | "init: msleep(%u)\n", val & 0xFFFF); |
c296d5f9 TP |
1094 | msleep(val & 0xFFFF); |
1095 | p = of_prop_next_u32(prop, p, &val); | |
1096 | } else { | |
1097 | dev_err(par->info->device, "illegal init value 0x%X\n", | |
4da2f4b7 | 1098 | val); |
c296d5f9 TP |
1099 | return -EINVAL; |
1100 | } | |
1101 | } | |
1102 | ||
1103 | return 0; | |
1104 | } | |
1105 | #endif | |
1106 | ||
1107 | /** | |
1108 | * fbtft_init_display() - Generic init_display() function | |
1109 | * @par: Driver data | |
1110 | * | |
1111 | * Uses par->init_sequence to do the initialization | |
1112 | * | |
1113 | * Return: 0 if successful, negative if error | |
1114 | */ | |
1115 | int fbtft_init_display(struct fbtft_par *par) | |
1116 | { | |
1117 | int buf[64]; | |
1118 | char msg[128]; | |
1119 | char str[16]; | |
1120 | int i = 0; | |
1121 | int j; | |
1122 | ||
c296d5f9 TP |
1123 | /* sanity check */ |
1124 | if (!par->init_sequence) { | |
1125 | dev_err(par->info->device, | |
1126 | "error: init_sequence is not set\n"); | |
1127 | return -EINVAL; | |
1128 | } | |
1129 | ||
1130 | /* make sure stop marker exists */ | |
1131 | for (i = 0; i < FBTFT_MAX_INIT_SEQUENCE; i++) | |
1132 | if (par->init_sequence[i] == -3) | |
1133 | break; | |
1134 | if (i == FBTFT_MAX_INIT_SEQUENCE) { | |
1135 | dev_err(par->info->device, | |
1136 | "missing stop marker at end of init sequence\n"); | |
1137 | return -EINVAL; | |
1138 | } | |
1139 | ||
1140 | par->fbtftops.reset(par); | |
1141 | if (par->gpio.cs != -1) | |
1142 | gpio_set_value(par->gpio.cs, 0); /* Activate chip */ | |
1143 | ||
1144 | i = 0; | |
1145 | while (i < FBTFT_MAX_INIT_SEQUENCE) { | |
1146 | if (par->init_sequence[i] == -3) { | |
1147 | /* done */ | |
1148 | return 0; | |
1149 | } | |
1150 | if (par->init_sequence[i] >= 0) { | |
1151 | dev_err(par->info->device, | |
1152 | "missing delimiter at position %d\n", i); | |
1153 | return -EINVAL; | |
1154 | } | |
94c0a544 | 1155 | if (par->init_sequence[i + 1] < 0) { |
c296d5f9 TP |
1156 | dev_err(par->info->device, |
1157 | "missing value after delimiter %d at position %d\n", | |
1158 | par->init_sequence[i], i); | |
1159 | return -EINVAL; | |
1160 | } | |
1161 | switch (par->init_sequence[i]) { | |
1162 | case -1: | |
1163 | i++; | |
1164 | /* make debug message */ | |
1165 | strcpy(msg, ""); | |
1166 | j = i + 1; | |
1167 | while (par->init_sequence[j] >= 0) { | |
1168 | sprintf(str, "0x%02X ", par->init_sequence[j]); | |
1169 | strcat(msg, str); | |
1170 | j++; | |
1171 | } | |
1172 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, | |
4da2f4b7 GS |
1173 | "init: write(0x%02X) %s\n", |
1174 | par->init_sequence[i], msg); | |
c296d5f9 TP |
1175 | |
1176 | /* Write */ | |
1177 | j = 0; | |
1178 | while (par->init_sequence[i] >= 0) { | |
1179 | if (j > 63) { | |
1180 | dev_err(par->info->device, | |
1181 | "%s: Maximum register values exceeded\n", | |
1182 | __func__); | |
1183 | return -EINVAL; | |
1184 | } | |
1185 | buf[j++] = par->init_sequence[i++]; | |
1186 | } | |
1187 | par->fbtftops.write_register(par, j, | |
1188 | buf[0], buf[1], buf[2], buf[3], | |
1189 | buf[4], buf[5], buf[6], buf[7], | |
1190 | buf[8], buf[9], buf[10], buf[11], | |
1191 | buf[12], buf[13], buf[14], buf[15], | |
1192 | buf[16], buf[17], buf[18], buf[19], | |
1193 | buf[20], buf[21], buf[22], buf[23], | |
1194 | buf[24], buf[25], buf[26], buf[27], | |
1195 | buf[28], buf[29], buf[30], buf[31], | |
1196 | buf[32], buf[33], buf[34], buf[35], | |
1197 | buf[36], buf[37], buf[38], buf[39], | |
1198 | buf[40], buf[41], buf[42], buf[43], | |
1199 | buf[44], buf[45], buf[46], buf[47], | |
1200 | buf[48], buf[49], buf[50], buf[51], | |
1201 | buf[52], buf[53], buf[54], buf[55], | |
1202 | buf[56], buf[57], buf[58], buf[59], | |
1203 | buf[60], buf[61], buf[62], buf[63]); | |
1204 | break; | |
1205 | case -2: | |
1206 | i++; | |
1207 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, | |
1208 | "init: mdelay(%d)\n", par->init_sequence[i]); | |
1209 | mdelay(par->init_sequence[i++]); | |
1210 | break; | |
1211 | default: | |
1212 | dev_err(par->info->device, | |
1213 | "unknown delimiter %d at position %d\n", | |
1214 | par->init_sequence[i], i); | |
1215 | return -EINVAL; | |
1216 | } | |
1217 | } | |
1218 | ||
1219 | dev_err(par->info->device, | |
1220 | "%s: something is wrong. Shouldn't get here.\n", __func__); | |
1221 | return -EINVAL; | |
1222 | } | |
1223 | EXPORT_SYMBOL(fbtft_init_display); | |
1224 | ||
1225 | /** | |
1226 | * fbtft_verify_gpios() - Generic verify_gpios() function | |
1227 | * @par: Driver data | |
1228 | * | |
1229 | * Uses @spi, @pdev and @buswidth to determine which GPIOs is needed | |
1230 | * | |
1231 | * Return: 0 if successful, negative if error | |
1232 | */ | |
ed208436 | 1233 | static int fbtft_verify_gpios(struct fbtft_par *par) |
c296d5f9 | 1234 | { |
ad6d8812 | 1235 | struct fbtft_platform_data *pdata = par->pdata; |
c296d5f9 TP |
1236 | int i; |
1237 | ||
1238 | fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); | |
1239 | ||
6ba67a5a | 1240 | if (pdata->display.buswidth != 9 && par->startbyte == 0 && |
c296d5f9 TP |
1241 | par->gpio.dc < 0) { |
1242 | dev_err(par->info->device, | |
1243 | "Missing info about 'dc' gpio. Aborting.\n"); | |
1244 | return -EINVAL; | |
1245 | } | |
1246 | ||
1247 | if (!par->pdev) | |
1248 | return 0; | |
1249 | ||
1250 | if (par->gpio.wr < 0) { | |
1251 | dev_err(par->info->device, "Missing 'wr' gpio. Aborting.\n"); | |
1252 | return -EINVAL; | |
1253 | } | |
1254 | for (i = 0; i < pdata->display.buswidth; i++) { | |
1255 | if (par->gpio.db[i] < 0) { | |
1256 | dev_err(par->info->device, | |
1257 | "Missing 'db%02d' gpio. Aborting.\n", i); | |
1258 | return -EINVAL; | |
1259 | } | |
1260 | } | |
1261 | ||
1262 | return 0; | |
1263 | } | |
1264 | ||
1265 | #ifdef CONFIG_OF | |
1266 | /* returns 0 if the property is not present */ | |
1267 | static u32 fbtft_of_value(struct device_node *node, const char *propname) | |
1268 | { | |
1269 | int ret; | |
1270 | u32 val = 0; | |
1271 | ||
1272 | ret = of_property_read_u32(node, propname, &val); | |
1273 | if (ret == 0) | |
1274 | pr_info("%s: %s = %u\n", __func__, propname, val); | |
1275 | ||
1276 | return val; | |
1277 | } | |
1278 | ||
1279 | static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) | |
1280 | { | |
1281 | struct device_node *node = dev->of_node; | |
1282 | struct fbtft_platform_data *pdata; | |
1283 | ||
1284 | if (!node) { | |
1285 | dev_err(dev, "Missing platform data or DT\n"); | |
1286 | return ERR_PTR(-EINVAL); | |
1287 | } | |
1288 | ||
1289 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | |
1290 | if (!pdata) | |
1291 | return ERR_PTR(-ENOMEM); | |
1292 | ||
1293 | pdata->display.width = fbtft_of_value(node, "width"); | |
1294 | pdata->display.height = fbtft_of_value(node, "height"); | |
1295 | pdata->display.regwidth = fbtft_of_value(node, "regwidth"); | |
1296 | pdata->display.buswidth = fbtft_of_value(node, "buswidth"); | |
1297 | pdata->display.backlight = fbtft_of_value(node, "backlight"); | |
1298 | pdata->display.bpp = fbtft_of_value(node, "bpp"); | |
1299 | pdata->display.debug = fbtft_of_value(node, "debug"); | |
1300 | pdata->rotate = fbtft_of_value(node, "rotate"); | |
1301 | pdata->bgr = of_property_read_bool(node, "bgr"); | |
1302 | pdata->fps = fbtft_of_value(node, "fps"); | |
1303 | pdata->txbuflen = fbtft_of_value(node, "txbuflen"); | |
1304 | pdata->startbyte = fbtft_of_value(node, "startbyte"); | |
1305 | of_property_read_string(node, "gamma", (const char **)&pdata->gamma); | |
1306 | ||
1307 | if (of_find_property(node, "led-gpios", NULL)) | |
1308 | pdata->display.backlight = 1; | |
1309 | if (of_find_property(node, "init", NULL)) | |
1310 | pdata->display.fbtftops.init_display = fbtft_init_display_dt; | |
1311 | pdata->display.fbtftops.request_gpios = fbtft_request_gpios_dt; | |
1312 | ||
1313 | return pdata; | |
1314 | } | |
1315 | #else | |
1316 | static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) | |
1317 | { | |
1318 | dev_err(dev, "Missing platform data\n"); | |
1319 | return ERR_PTR(-EINVAL); | |
1320 | } | |
1321 | #endif | |
1322 | ||
1323 | /** | |
1324 | * fbtft_probe_common() - Generic device probe() helper function | |
1325 | * @display: Display properties | |
1326 | * @sdev: SPI device | |
1327 | * @pdev: Platform device | |
1328 | * | |
1329 | * Allocates, initializes and registers a framebuffer | |
1330 | * | |
1331 | * Either @sdev or @pdev should be NULL | |
1332 | * | |
1333 | * Return: 0 if successful, negative if error | |
1334 | */ | |
1335 | int fbtft_probe_common(struct fbtft_display *display, | |
1336 | struct spi_device *sdev, struct platform_device *pdev) | |
1337 | { | |
1338 | struct device *dev; | |
1339 | struct fb_info *info; | |
1340 | struct fbtft_par *par; | |
1341 | struct fbtft_platform_data *pdata; | |
1342 | int ret; | |
1343 | ||
1344 | if (sdev) | |
1345 | dev = &sdev->dev; | |
1346 | else | |
1347 | dev = &pdev->dev; | |
1348 | ||
1349 | if (unlikely(display->debug & DEBUG_DRIVER_INIT_FUNCTIONS)) | |
1350 | dev_info(dev, "%s()\n", __func__); | |
1351 | ||
1352 | pdata = dev->platform_data; | |
1353 | if (!pdata) { | |
1354 | pdata = fbtft_probe_dt(dev); | |
1355 | if (IS_ERR(pdata)) | |
1356 | return PTR_ERR(pdata); | |
c296d5f9 TP |
1357 | } |
1358 | ||
ad6d8812 | 1359 | info = fbtft_framebuffer_alloc(display, dev, pdata); |
c296d5f9 TP |
1360 | if (!info) |
1361 | return -ENOMEM; | |
1362 | ||
1363 | par = info->par; | |
1364 | par->spi = sdev; | |
1365 | par->pdev = pdev; | |
1366 | ||
1367 | if (display->buswidth == 0) { | |
1368 | dev_err(dev, "buswidth is not set\n"); | |
1369 | return -EINVAL; | |
1370 | } | |
1371 | ||
1372 | /* write register functions */ | |
1373 | if (display->regwidth == 8 && display->buswidth == 8) { | |
1374 | par->fbtftops.write_register = fbtft_write_reg8_bus8; | |
1375 | } else | |
1376 | if (display->regwidth == 8 && display->buswidth == 9 && par->spi) { | |
1377 | par->fbtftops.write_register = fbtft_write_reg8_bus9; | |
1378 | } else if (display->regwidth == 16 && display->buswidth == 8) { | |
1379 | par->fbtftops.write_register = fbtft_write_reg16_bus8; | |
1380 | } else if (display->regwidth == 16 && display->buswidth == 16) { | |
1381 | par->fbtftops.write_register = fbtft_write_reg16_bus16; | |
1382 | } else { | |
1383 | dev_warn(dev, | |
1384 | "no default functions for regwidth=%d and buswidth=%d\n", | |
1385 | display->regwidth, display->buswidth); | |
1386 | } | |
1387 | ||
1388 | /* write_vmem() functions */ | |
1389 | if (display->buswidth == 8) | |
1390 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; | |
1391 | else if (display->buswidth == 9) | |
1392 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus9; | |
1393 | else if (display->buswidth == 16) | |
1394 | par->fbtftops.write_vmem = fbtft_write_vmem16_bus16; | |
1395 | ||
1396 | /* GPIO write() functions */ | |
1397 | if (par->pdev) { | |
1398 | if (display->buswidth == 8) | |
1399 | par->fbtftops.write = fbtft_write_gpio8_wr; | |
1400 | else if (display->buswidth == 16) | |
1401 | par->fbtftops.write = fbtft_write_gpio16_wr; | |
1402 | } | |
1403 | ||
1404 | /* 9-bit SPI setup */ | |
1405 | if (par->spi && display->buswidth == 9) { | |
cabb5b2a SW |
1406 | if (par->spi->master->bits_per_word_mask & SPI_BPW_MASK(9)) { |
1407 | par->spi->bits_per_word = 9; | |
1408 | } else { | |
c296d5f9 TP |
1409 | dev_warn(&par->spi->dev, |
1410 | "9-bit SPI not available, emulating using 8-bit.\n"); | |
c296d5f9 TP |
1411 | /* allocate buffer with room for dc bits */ |
1412 | par->extra = devm_kzalloc(par->info->device, | |
1413 | par->txbuf.len + (par->txbuf.len / 8) + 8, | |
1414 | GFP_KERNEL); | |
1415 | if (!par->extra) { | |
1416 | ret = -ENOMEM; | |
1417 | goto out_release; | |
1418 | } | |
1419 | par->fbtftops.write = fbtft_write_spi_emulate_9; | |
1420 | } | |
1421 | } | |
1422 | ||
1423 | if (!par->fbtftops.verify_gpios) | |
1424 | par->fbtftops.verify_gpios = fbtft_verify_gpios; | |
1425 | ||
1426 | /* make sure we still use the driver provided functions */ | |
1427 | fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops); | |
1428 | ||
1429 | /* use init_sequence if provided */ | |
1430 | if (par->init_sequence) | |
1431 | par->fbtftops.init_display = fbtft_init_display; | |
1432 | ||
1433 | /* use platform_data provided functions above all */ | |
1434 | fbtft_merge_fbtftops(&par->fbtftops, &pdata->display.fbtftops); | |
1435 | ||
1436 | ret = fbtft_register_framebuffer(info); | |
1437 | if (ret < 0) | |
1438 | goto out_release; | |
1439 | ||
1440 | return 0; | |
1441 | ||
1442 | out_release: | |
1443 | fbtft_framebuffer_release(info); | |
1444 | ||
1445 | return ret; | |
1446 | } | |
1447 | EXPORT_SYMBOL(fbtft_probe_common); | |
1448 | ||
1449 | /** | |
1450 | * fbtft_remove_common() - Generic device remove() helper function | |
1451 | * @dev: Device | |
1452 | * @info: Framebuffer | |
1453 | * | |
1454 | * Unregisters and releases the framebuffer | |
1455 | * | |
1456 | * Return: 0 if successful, negative if error | |
1457 | */ | |
1458 | int fbtft_remove_common(struct device *dev, struct fb_info *info) | |
1459 | { | |
1460 | struct fbtft_par *par; | |
1461 | ||
1462 | if (!info) | |
1463 | return -EINVAL; | |
1464 | par = info->par; | |
1465 | if (par) | |
1466 | fbtft_par_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, par, | |
4da2f4b7 | 1467 | "%s()\n", __func__); |
c296d5f9 TP |
1468 | fbtft_unregister_framebuffer(info); |
1469 | fbtft_framebuffer_release(info); | |
1470 | ||
1471 | return 0; | |
1472 | } | |
1473 | EXPORT_SYMBOL(fbtft_remove_common); | |
1474 | ||
1475 | MODULE_LICENSE("GPL"); |