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 /* Return the address corresponding to the start of frame buffer. */
143 DEBUGASSERT(ppv != NULL);
144 *ppv = fb->fbmem;
145 ret = OK;
146 }
147 break;
148
149 case FBIOGET_VIDEOINFO:
150 { /* Get color plane info */
151 struct fb_videoinfo_s *vinfo = (struct fb_videoinfo_s *)((uintptr_t)arg);
152 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getvideoinfo != NULL);
153 ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo);
154 }
155 break;
156
157 case FBIOGET_PLANEINFO:
158 { /* Get video plane info */
159 struct fb_planeinfo_s *pinfo = (struct fb_planeinfo_s *)((uintptr_t)arg);
160 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getplaneinfo != NULL);
161 ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo);
162 }
163 break;
164
165 #ifdef CONFIG_FB_CMAP
166 case FBIOGET_CMAP:
167 { /* Get RGB color mapping */
168 struct fb_cmap_s *cmap = (struct fb_cmap_s *)((uintptr_t)arg);
169 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getcmap != NULL);
170 ret = fb->vtable->getcmap(fb->vtable, &cmap);
171 }
172 break;
173
174 case FBIOPUT_CMAP:
175 { /* Put RGB color mapping */
176 struct fb_cmap_s *cmap = (struct fb_cmap_s *)((uintptr_t)arg);
177 DEBUGASSERT(fb->vtable != NULL && fb->vtable->putcmap != NULL);
178 ret = fb->vtable->putcmap(fb->vtable, &cmap);
179 }
180 break;
181 #endif
182 #ifdef CONFIG_FB_HWCURSOR
183 case FBIOGET_CURSOR:
184 { /* Get cursor attributes */
185 struct fb_cursorattrib_s *attrib = (struct fb_cursorattrib_s *)((uintptr_t)arg);
186 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getcursor != NULL);
187 ret = fb->vtable->getcursor(fb->vtable, &attrib);
188 }
189 break;
190
191 case FBIOPUT_CURSOR:
192 { /* Set cursor attributes */
193 struct fb_setcursor_s *cursor = (struct fb_setcursor_s *)((uintptr_t)arg);
194 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setcursor != NULL);
195 ret = fb->vtable->setcursor(fb->vtable, &cursor);
196 }
197 break;
198 #endif
199
200 #ifdef CONFIG_FB_UPDATE
201 case FBIO_UPDATE:
202 { /* Update the modified framebuffer data */
203 struct fb_area_s *area = (struct fb_area_s *)((uintptr_t)arg);
204 DEBUGASSERT(fb->vtable != NULL && fb->vtable->updatearea != NULL);
205 ret = fb->vtable->updatearea(fb->vtable, area);
206 }
207 break;
208 #endif
209
210 #ifdef CONFIG_FB_SYNC
211 case FBIO_WAITFORVSYNC:
212 { /* Wait upon vertical sync */
213 ret = fb->vtable->waitforvsync(fb->vtable);
214 }
215 break;
216 #endif
217
218 #ifdef CONFIG_FB_OVERLAY
219 case FBIO_SELECT_OVERLAY:
220 { /* Select video overlay */
221 struct fb_overlayinfo_s oinfo;
222 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getoverlayinfo != NULL);
223 ret = fb->vtable->getoverlayinfo(fb->vtable, arg, &oinfo);
224 if (ret == OK) {
225 fb->fbmem = oinfo.fbmem;
226 fb->fblen = oinfo.fblen;
227 fb->bpp = oinfo.bpp;
228 }
229 }
230 break;
231
232 case FBIOGET_OVERLAYINFO:
233 { /* Get video overlay info */
234 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
235 DEBUGASSERT(fb->vtable != NULL && fb->vtable->getoverlayinfo != NULL);
236 ret = fb->vtable->getoverlayinfo(fb->vtable, oinfo->overlay, &oinfo);
237 }
238 break;
239
240 case FBIOSET_TRANSP:
241 { /* Set video overlay transparency */
242 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
243 DEBUGASSERT(fb->vtable != NULL && fb->vtable->settransp != NULL);
244 ret = fb->vtable->settransp(fb->vtable, &oinfo);
245 }
246 break;
247
248 case FBIOSET_CHROMAKEY:
249 { /* Set video overlay chroma key */
250 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
251 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setchromakey != NULL);
252 ret = fb->vtable->setchromakey(fb->vtable, &oinfo);
253 }
254 break;
255
256 case FBIOSET_COLOR:
257 { /* Set video overlay color */
258 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
259 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setcolor != NULL);
260 ret = fb->vtable->setcolor(fb->vtable, &oinfo);
261 }
262 break;
263
264 case FBIOSET_BLANK:
265 { /* Blank or unblank video overlay */
266 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
267 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setblank != NULL);
268 ret = fb->vtable->setblank(fb->vtable, &oinfo);
269 }
270 break;
271
272 case FBIOSET_AREA:
273 { /* Set active video overlay area */
274 struct fb_overlayinfo_s *oinfo = (struct fb_overlayinfo_s *)((uintptr_t)arg);
275 DEBUGASSERT(fb->vtable != NULL && fb->vtable->setarea != NULL);
276 ret = fb->vtable->setarea(fb->vtable, &oinfo);
277 }
278 break;
279
280 #ifdef CONFIG_FB_OVERLAY_BLIT
281 case FBIOSET_BLIT:
282 { /* Blit operation between video overlays */
283 struct fb_overlayblit_s *blit = (struct fb_overlayblit_s *)((uintptr_t)arg);
284 DEBUGASSERT(fb->vtable != NULL && fb->vtable->blit != NULL);
285 ret = fb->vtable->blit(fb->vtable, &blit);
286 }
287 break;
288
289 case FBIOSET_BLEND:
290 { /* Blend operation between video overlays */
291 struct fb_overlayblend_s *blend = (struct fb_overlayblend_s *)((uintptr_t)arg);
292 DEBUGASSERT(fb->vtable != NULL && fb->vtable->blend != NULL);
293 ret = fb->vtable->blend(fb->vtable, &blend);
294 }
295 break;
296 #endif
297 #endif /* CONFIG_FB_OVERLAY */
298
299 default:
300 DEBUGASSERT(fb->vtable != NULL && fb->vtable->fb_ioctl != NULL);
301 ret = fb->vtable->fb_ioctl(fb->vtable, cmd, arg);
302 break;
303 }
304
305 return ret;
306 }
307
getplaneinfo(struct fb_mem * fbmem,struct fb_planeinfo_s ** result)308 int getplaneinfo(struct fb_mem *fbmem, struct fb_planeinfo_s **result)
309 {
310 int ret;
311 struct fb_chardev_s *fb;
312
313 fb = (struct fb_chardev_s *)fbmem->data;
314
315 struct fb_planeinfo_s pinfo;
316
317 ret = fb->vtable->getplaneinfo(fb->vtable, fb->plane, &pinfo);
318 if (ret == 0) {
319 *result = &pinfo;
320 }
321
322 return 0;
323 }
324
325 /****************************************************************************
326 * Name: fb_register
327 *
328 * Description:
329 * Register the framebuffer character device at /dev/fbN where N is the
330 * display number if the devices supports only a single plane. If the
331 * hardware supports multiple color planes, then the device will be
332 * registered at /dev/fbN.M where N is the again display number but M
333 * is the display plane.
334 *
335 * Input Parameters:
336 * display - The display number for the case of boards supporting multiple
337 * displays or for hardware that supports multiple
338 * layers (each layer is consider a display). Typically zero.
339 * plane - Identifies the color plane on hardware that supports separate
340 * framebuffer "planes" for each color component.
341 *
342 * Returned Value:
343 * Zero (OK) is returned success; a negated errno value is returned on any
344 * failure.
345 *
346 ****************************************************************************/
347
fb_register(int display,int plane)348 int fb_register(int display, int plane)
349 {
350 struct fb_chardev_s *fb = NULL;
351 struct fb_videoinfo_s vinfo;
352 struct fb_planeinfo_s pinfo;
353 #ifdef CONFIG_FB_OVERLAY
354 struct fb_overlayinfo_s oinfo;
355 #endif
356 char devname[16];
357 int nplanes;
358 int ret;
359
360 if (display < 0 || display >= FB_DEV_MAXNUM) return -EINVAL;
361
362 /* Allocate a framebuffer state instance */
363 fb = (struct fb_chardev_s *)malloc(sizeof(struct fb_chardev_s));
364 if (fb == NULL) {
365 return -ENOMEM;
366 }
367
368 /* Initialize the frame buffer device. */
369 ret = up_fbinitialize(display);
370 if (ret < 0) {
371 gerr("ERROR: up_fbinitialize() failed for display %d: %d\n", display, ret);
372 goto errout_with_fb;
373 }
374
375 DEBUGASSERT((unsigned)plane <= UINT8_MAX);
376 fb->plane = plane;
377
378 fb->vtable = up_fbgetvplane(display, plane);
379 if (fb->vtable == NULL) {
380 gerr("ERROR: up_fbgetvplane() failed, vplane=%d\n", plane);
381 goto errout_with_fb;
382 }
383
384 /* Initialize the frame buffer instance. */
385 DEBUGASSERT(fb->vtable->getvideoinfo != NULL);
386 ret = fb->vtable->getvideoinfo(fb->vtable, &vinfo);
387 if (ret < 0) {
388 gerr("ERROR: getvideoinfo() failed: %d\n", ret);
389 goto errout_with_fb;
390 }
391
392 nplanes = vinfo.nplanes;
393 DEBUGASSERT(vinfo.nplanes > 0 && (unsigned)plane < vinfo.nplanes);
394
395 DEBUGASSERT(fb->vtable->getplaneinfo != NULL);
396 ret = fb->vtable->getplaneinfo(fb->vtable, plane, &pinfo);
397 if (ret < 0) {
398 gerr("ERROR: getplaneinfo() failed: %d\n", ret);
399 goto errout_with_fb;
400 }
401
402 fb->fbmem = pinfo.fbmem;
403 fb->fblen = pinfo.fblen;
404 fb->bpp = pinfo.bpp;
405
406 /* Clear the framebuffer memory */
407 memset(pinfo.fbmem, 0, pinfo.fblen);
408
409 #ifdef CONFIG_FB_OVERLAY
410 /* Initialize first overlay but do not select */
411 DEBUGASSERT(fb->vtable->getoverlayinfo != NULL);
412 ret = fb->vtable->getoverlayinfo(fb->vtable, 0, &oinfo);
413 if (ret < 0) {
414 gerr("ERROR: getoverlayinfo() failed: %d\n", ret);
415 goto errout_with_fb;
416 }
417
418 /* Clear the overlay memory. Necessary when plane 0 and overlay 0
419 * different.
420 */
421
422 memset(oinfo.fbmem, 0, oinfo.fblen);
423 #endif
424
425 /* Register the framebuffer device */
426 if (nplanes < 2) {
427 (void)sprintf_s(devname, 16, "/dev/fb%d", display);
428 } else {
429 (void)sprintf_s(devname, 16, "/dev/fb%d.%d", display, plane);
430 }
431
432 ret = register_driver(devname, (void *)fb);
433 if (ret < 0) {
434 gerr("ERROR: register_driver() failed: %d\n", ret);
435 goto errout_with_fb;
436 }
437
438 g_fb_dev[display] = fb;
439
440 return OK;
441
442 errout_with_fb:
443 free(fb);
444 return ret;
445 }
446
fb_unregister(int display)447 int fb_unregister(int display)
448 {
449 struct fb_chardev_s *fb = NULL;
450 char devname[16];
451
452 if (display < 0 || display >= FB_DEV_MAXNUM) return -EINVAL;
453
454 (void)sprintf_s(devname, 16, "/dev/fb%d", display);
455 unregister_driver(devname);
456
457 up_fbuninitialize(display);
458
459 fb = g_fb_dev[display];
460 free(fb);
461 g_fb_dev[display] = NULL;
462
463 return 0;
464 }