1 /****************************************************************************
2 * video/fb.c
3 * Framebuffer character driver
4 *
5 * Copyright (C) 2017 Gregory Nutt. All rights reserved.
6 * Author: Gregory Nutt <gnutt@nuttx.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * 3. Neither the name NuttX nor the names of its contributors may be
19 * used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 ****************************************************************************/
36
37 /****************************************************************************
38 * Included Files
39 ****************************************************************************/
40
41 #include "fb.h"
42
43 #include "errno.h"
44 #include "string.h"
45
46 #define gerr PRINT_ERR
47 #define DEBUGASSERT LOS_ASSERT
48
49 /****************************************************************************
50 * Private Types
51 ****************************************************************************/
52
53 /* This structure defines one framebuffer device. Note that which is
54 * everything in this structure is constant data set up and initialization
55 * time. Therefore, no there is requirement for serialized access to this
56 * structure.
57 */
58
59 struct fb_chardev_s {
60 struct fb_vtable_s *vtable; /* Framebuffer interface */
61 void *fbmem; /* Start of frame buffer memory */
62 size_t fblen; /* Size of the framebuffer */
63 uint8_t plane; /* Video plan number */
64 uint8_t bpp; /* Bits per pixel */
65 };
66
67 #define FB_DEV_MAXNUM 32
68 static struct fb_chardev_s *g_fb_dev[FB_DEV_MAXNUM] = {NULL};
69
70 /****************************************************************************
71 * Public Functions
72 ****************************************************************************/
fb_open(const char * key,struct fb_mem ** result)73 int fb_open(const char *key, struct fb_mem **result)
74 {
75 struct fb_mem *fbmem = NULL;
76 struct fb_chardev_s *fb;
77 struct fb_vtable_s *vtable;
78 int ret = -EINVAL;
79
80 if (key == NULL || strlen(key) >= PATH_MAX) {
81 return -EINVAL;
82 }
83 FbMemHold();
84 ret = FbMemLookup(key, &fbmem, 0);
85 FbMemDrop();
86 if (ret == 0) {
87 fb = (struct fb_chardev_s *)fbmem->data;
88 if (fb == NULL) {
89 return -ENODEV;
90 }
91
92 vtable = fb->vtable;
93 if (vtable == NULL) {
94 return -EINVAL;
95 }
96
97 if (vtable->fb_open) {
98 ret = vtable->fb_open(vtable);
99 if (ret == 0) {
100 *result = fbmem;
101 }
102 }
103 }
104 return ret;
105 }
106
fb_close(struct fb_mem * fbmem)107 int fb_close(struct fb_mem *fbmem)
108 {
109 struct fb_chardev_s *fb;
110 struct fb_vtable_s *vtable;
111 int ret = -EINVAL;
112
113 fb = (struct fb_chardev_s *)fbmem->data;
114 if (fb == NULL) {
115 return -ENODEV;
116 }
117
118 vtable = fb->vtable;
119 if (vtable == NULL) {
120 return -EINVAL;
121 }
122
123 if (vtable->fb_release) {
124 ret = vtable->fb_release(vtable);
125 }
126 return ret;
127 }
128
fb_ioctl(struct fb_mem * fbMem,int cmd,unsigned long arg)129 int fb_ioctl(struct fb_mem *fbMem, int cmd, unsigned long arg)
130 {
131 struct fb_chardev_s *fb = NULL;
132 int ret;
133
134 /* Get the framebuffer instance */
135 fb = (struct fb_chardev_s *)fbMem->data;
136 /* Process the IOCTL command */
137
138 switch (cmd) {
139 case FIOC_MMAP:
140 { /* Get color plane info */
141 void **ppv = (void **)((uintptr_t)arg);
142 uintptr_t fbmem = (uintptr_t)fb->fbmem;
143 /* Return the address corresponding to the start of frame buffer. */
144 DEBUGASSERT(ppv != NULL);
145 *ppv = fb->fbmem;
146 ret = OK;
147 }
148 break;
149
150 case FBIOGET_VIDEOINFO:
151 { /* Get color plane info */
152 struct fb_videoinfo_s *vinfo = (struct fb_videoinfo_s *)((uintptr_t)arg);
153 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getvideoinfo != NULL);
154 ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo);
155 }
156 break;
157
158 case FBIOGET_PLANEINFO:
159 { /* Get video plane info */
160 struct fb_planeinfo_s *pinfo = (struct fb_planeinfo_s *)((uintptr_t)arg);
161 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getplaneinfo != NULL);
162 ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo);
163 }
164 break;
165
166 #ifdef CONFIG_FB_CMAP
167 case FBIOGET_CMAP:
168 { /* Get RGB color mapping */
169 struct fb_cmap_s *cmap = (struct fb_cmap_s *)((uintptr_t)arg);
170 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getcmap != NULL);
171 ret = fb->vtable->getcmap(fb->vtable, &cmap);
172 }
173 break;
174
175 case FBIOPUT_CMAP:
176 { /* Put RGB color mapping */
177 struct fb_cmap_s *cmap = (struct fb_cmap_s *)((uintptr_t)arg);
178 DEBUGASSERT(fb->vtable != NULL && fb->vtable->putcmap != NULL);
179 ret = fb->vtable->putcmap(fb->vtable, &cmap);
180 }
181 break;
182 #endif
183 #ifdef CONFIG_FB_HWCURSOR
184 case FBIOGET_CURSOR:
185 { /* Get cursor attributes */
186 struct fb_cursorattrib_s *attrib = (struct fb_cursorattrib_s *)((uintptr_t)arg);
187 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getcursor != NULL);
188 ret = fb->vtable->getcursor(fb->vtable, &attrib);
189 }
190 break;
191
192 case FBIOPUT_CURSOR:
193 { /* Set cursor attibutes */
194 struct fb_setcursor_s *cursor = (struct fb_setcursor_s *)((uintptr_t)arg);
195 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setcursor != NULL);
196 ret = fb->vtable->setcursor(fb->vtable, &cursor);
197 }
198 break;
199 #endif
200
201 #ifdef CONFIG_FB_UPDATE
202 case FBIO_UPDATE:
203 { /* Update the modified framebuffer data */
204 struct fb_area_s *area = (struct fb_area_s *)((uintptr_t)arg);
205 DEBUGASSERT(fb->vtable != NULL && fb->vtable->updatearea != NULL);
206 ret = fb->vtable->updatearea(fb->vtable, area);
207 }
208 break;
209 #endif
210
211 #ifdef CONFIG_FB_SYNC
212 case FBIO_WAITFORVSYNC:
213 { /* Wait upon vertical sync */
214 ret = fb->vtable->waitforvsync(fb->vtable);
215 }
216 break;
217 #endif
218
219 #ifdef CONFIG_FB_OVERLAY
220 case FBIO_SELECT_OVERLAY:
221 { /* Select video overlay */
222 struct fb_overlayinfo_s oinfo;
223 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getoverlayinfo != NULL);
224 ret = fb->vtable->getoverlayinfo(fb->vtable, arg, &oinfo);
225 if (ret == OK) {
226 fb->fbmem = oinfo.fbmem;
227 fb->fblen = oinfo.fblen;
228 fb->bpp = oinfo.bpp;
229 }
230 }
231 break;
232
233 case FBIOGET_OVERLAYINFO:
234 { /* Get video overlay info */
235 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
236 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getoverlayinfo != NULL);
237 ret = fb->vtable->getoverlayinfo(fb->vtable, oinfo->overlay, &oinfo);
238 }
239 break;
240
241 case FBIOSET_TRANSP:
242 { /* Set video overlay transparency */
243 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
244 DEBUGASSERT(fb->vtable != NULL && fb->vtable->settransp != NULL);
245 ret = fb->vtable->settransp(fb->vtable, &oinfo);
246 }
247 break;
248
249 case FBIOSET_CHROMAKEY:
250 { /* Set video overlay chroma key */
251 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
252 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setchromakey != NULL);
253 ret = fb->vtable->setchromakey(fb->vtable, &oinfo);
254 }
255 break;
256
257 case FBIOSET_COLOR:
258 { /* Set video overlay color */
259 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
260 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setcolor != NULL);
261 ret = fb->vtable->setcolor(fb->vtable, &oinfo);
262 }
263 break;
264
265 case FBIOSET_BLANK:
266 { /* Blank or unblank video overlay */
267 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
268 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setblank != NULL);
269 ret = fb->vtable->setblank(fb->vtable, &oinfo);
270 }
271 break;
272
273 case FBIOSET_AREA:
274 { /* Set active video overlay area */
275 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
276 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setarea != NULL);
277 ret = fb->vtable->setarea(fb->vtable, &oinfo);
278 }
279 break;
280
281 #ifdef CONFIG_FB_OVERLAY_BLIT
282 case FBIOSET_BLIT:
283 { /* Blit operation between video overlays */
284 struct fb_overlayblit_s *blit = (struct fb_overlayblit_s *)((uintptr_t)arg);
285 DEBUGASSERT(fb->vtable != NULL && fb->vtable->blit != NULL);
286 ret = fb->vtable->blit(fb->vtable, &blit);
287 }
288 break;
289
290 case FBIOSET_BLEND:
291 { /* Blend operation between video overlays */
292 struct fb_overlayblend_s *blend = (struct fb_overlayblend_s *)((uintptr_t)arg);
293 DEBUGASSERT(fb->vtable != NULL && fb->vtable->blend != NULL);
294 ret = fb->vtable->blend(fb->vtable, &blend);
295 }
296 break;
297 #endif
298 #endif /* CONFIG_FB_OVERLAY */
299
300 default:
301 DEBUGASSERT(fb->vtable != NULL && fb->vtable->fb_ioctl != NULL);
302 ret = fb->vtable->fb_ioctl(fb->vtable, cmd, arg);
303 break;
304 }
305
306 return ret;
307 }
308
getplaneinfo(struct fb_mem * fbmem,struct fb_planeinfo_s ** result)309 int getplaneinfo(struct fb_mem *fbmem, struct fb_planeinfo_s **result)
310 {
311 int ret;
312 struct fb_chardev_s *fb;
313
314 fb = (struct fb_chardev_s *)fbmem->data;
315
316 struct fb_planeinfo_s pinfo;
317
318 ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo);
319 if (ret == 0) {
320 *result = &pinfo;
321 }
322
323 return 0;
324 }
325
326 /****************************************************************************
327 * Name: fb_register
328 *
329 * Description:
330 * Register the framebuffer character device at /dev/fbN where N is the
331 * display number if the devices supports only a single plane. If the
332 * hardware supports multiple color planes, then the device will be
333 * registered at /dev/fbN.M where N is the again display number but M
334 * is the display plane.
335 *
336 * Input Parameters:
337 * display - The display number for the case of boards supporting multiple
338 * displays or for hardware that supports multiple
339 * layers (each layer is consider a display). Typically zero.
340 * plane - Identifies the color plane on hardware that supports separate
341 * framebuffer "planes" for each color component.
342 *
343 * Returned Value:
344 * Zero (OK) is returned success; a negated errno value is returned on any
345 * failure.
346 *
347 ****************************************************************************/
348
fb_register(int display,int plane)349 int fb_register(int display, int plane)
350 {
351 struct fb_chardev_s *fb = NULL;
352 struct fb_videoinfo_s vinfo;
353 struct fb_planeinfo_s pinfo;
354 #ifdef CONFIG_FB_OVERLAY
355 struct fb_overlayinfo_s oinfo;
356 #endif
357 char devname[16];
358 int nplanes;
359 int ret;
360
361 if (display < 0 || display >= FB_DEV_MAXNUM) return -EINVAL;
362
363 /* Allocate a framebuffer state instance */
364 fb = (struct fb_chardev_s *)malloc(sizeof(struct fb_chardev_s));
365 if (fb == NULL) {
366 return -ENOMEM;
367 }
368
369 /* Initialize the frame buffer device. */
370 ret = up_fbinitialize(display);
371 if (ret < 0) {
372 gerr("ERROR: up_fbinitialize() failed for display %d: %d\n", display, ret);
373 goto errout_with_fb;
374 }
375
376 DEBUGASSERT((unsigned)plane <= UINT8_MAX);
377 fb->plane = plane;
378
379 fb->vtable = up_fbgetvplane(display, plane);
380 if (fb->vtable == NULL) {
381 gerr("ERROR: up_fbgetvplane() failed, vplane=%d\n", plane);
382 goto errout_with_fb;
383 }
384
385 /* Initialize the frame buffer instance. */
386 DEBUGASSERT(fb->vtable->getvideoinfo != NULL);
387 ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo);
388 if (ret < 0) {
389 gerr("ERROR: getvideoinfo() failed: %d\n", ret);
390 goto errout_with_fb;
391 }
392
393 nplanes = vinfo.nplanes;
394 DEBUGASSERT(vinfo.nplanes > 0 && (unsigned)plane < vinfo.nplanes);
395
396 DEBUGASSERT(fb->vtable->getplaneinfo != NULL);
397 ret = fb->vtable->getplaneinfo(fb->vtable, plane, &pinfo);
398 if (ret < 0) {
399 gerr("ERROR: getplaneinfo() failed: %d\n", ret);
400 goto errout_with_fb;
401 }
402
403 fb->fbmem = pinfo.fbmem;
404 fb->fblen = pinfo.fblen;
405 fb->bpp = pinfo.bpp;
406
407 /* Clear the framebuffer memory */
408 memset(pinfo.fbmem, 0, pinfo.fblen);
409
410 #ifdef CONFIG_FB_OVERLAY
411 /* Initialize first overlay but do not select */
412 DEBUGASSERT(fb->vtable->getoverlayinfo != NULL);
413 ret = fb->vtable->getoverlayinfo(fb->vtable, 0, &oinfo);
414 if (ret < 0) {
415 gerr("ERROR: getoverlayinfo() failed: %d\n", ret);
416 goto errout_with_fb;
417 }
418
419 /* Clear the overlay memory. Necessary when plane 0 and overlay 0
420 * different.
421 */
422
423 memset(oinfo.fbmem, 0, oinfo.fblen);
424 #endif
425
426 /* Register the framebuffer device */
427 if (nplanes < 2) {
428 (void)sprintf_s(devname, 16, "/dev/fb%d", display);
429 } else {
430 (void)sprintf_s(devname, 16, "/dev/fb%d.%d", display, plane);
431 }
432
433 ret = register_driver(devname, (void *)fb);
434 if (ret < 0) {
435 gerr("ERROR: register_driver() failed: %d\n", ret);
436 goto errout_with_fb;
437 }
438
439 g_fb_dev[display] = fb;
440
441 return OK;
442
443 errout_with_fb:
444 free(fb);
445 return ret;
446 }
447
fb_unregister(int display)448 int fb_unregister(int display)
449 {
450 struct fb_chardev_s *fb = NULL;
451 char devname[16];
452
453 if (display < 0 || display >= FB_DEV_MAXNUM) return -EINVAL;
454
455 (void)sprintf_s(devname, 16, "/dev/fb%d", display);
456 unregister_driver(devname);
457
458 up_fbuninitialize(display);
459
460 fb = g_fb_dev[display];
461 free(fb);
462 g_fb_dev[display] = NULL;
463
464 return 0;
465 }