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