aboutsummaryrefslogtreecommitdiffstats
path: root/boards/base/Linux-Framebuffer/board_framebuffer.h
blob: fa631dbc739bedbb27add8c61bf66a4e079f15ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*
 * This file is subject to the terms of the GFX License. If a copy of
 * the license was not distributed with this file, you can obtain one at:
 *
 *              http://ugfx.io/license.html
 */

// Set this to your frame buffer pixel format. Note Linux frame buffer only supports RGB modes (no BGR modes).
#ifndef GDISP_LLD_PIXELFORMAT
	#define GDISP_LLD_PIXELFORMAT		GDISP_PIXELFORMAT_RGB888
#endif

#ifdef GDISP_DRIVER_VMT

	#define FBDEV_PATH1		"/dev/fb0"
	#define FBDEV_PATH2		"/dev/fb/0"			// Optional - comment this out to only try the one device
	#define USE_SET_MODE						// Optional - comment this out to not to try to set the color mode we want
	//#define VTDEV_PATH	"/dev/tty0"			// Optional - if defined use this tty to switch from text to graphics mode

	#define _GNU_SOURCE 1
	#include <fcntl.h>
	#include <limits.h>
	#include <linux/fb.h>
	#include <linux/kd.h>
	#include <linux/vt.h>
	#include <stdarg.h>
	#include <stdio.h>
	#include <stdlib.h>
	#include <string.h>
	#include <sys/ioctl.h>
	#include <sys/mman.h>
	#include <sys/stat.h>
	#include <sys/time.h>
	#include <sys/types.h>
	#include <unistd.h>

	#if VTDEV_PATH
		static void board_revert2textmode(void) {
			int tty;

			// Go back to text mode
			if ((tty = open(VTDEV_PATH, O_RDWR)) >= 0) {
				ioctl(tty, KDSETMODE, KD_TEXT);
				close(tty);
			}
		}
		static void board_switch2graphicsmode(void) {
			int tty;

			// Open the tty
			if ((tty = open (VTDEV_PATH, O_RDWR)) < 0) {
				fprintf(stderr, "GDISP Framebuffer: Cannot open tty %s\n", VTDEV_PATH);
				exit(-1);
			}
			if (ioctl (tty, KDSETMODE, KD_GRAPHICS) == -1) {
				fprintf(stderr, "GDISP Framebuffer: Cannot set to graphics mode\n");
				exit(-1);
			}
			close(tty);

			// Make sure we clean up properly
			atexit(board_revert2textmode)
		}
	#endif

	static void board_init(GDisplay *g, fbInfo *fbi) {
		int							fb;
		char *						env;
		size_t						fblen;
		struct fb_fix_screeninfo	fb_fix;
		struct fb_var_screeninfo	fb_var;

		// Open the frame buffer device
		if((env = getenv("FRAMEBUFFER")) != 0)
			fb = open(env, O_RDWR);
		else {
			fb = open(FBDEV_PATH1, O_RDWR);
			#ifdef FBDEV_PATH2
				if (fb < 0) fb = open(FBDEV_PATH2, O_RDWR);
			#endif
		}
		if(fb < 0) {
			fprintf(stderr, "GDISP Framebuffer: Error opening the framebuffer device\n");
			exit(-1);
		}

		// Get screen info
		if (ioctl(fb, FBIOGET_FSCREENINFO, &fb_fix) == -1 || ioctl(fb, FBIOGET_VSCREENINFO, &fb_var) == -1) {
			fprintf(stderr, "GDISP Framebuffer: Error getting screen info\n");
			exit(-1);
		}

		#ifdef USE_SET_MODE
			fb_var.reserved[0] = 0;
			fb_var.reserved[1] = 0;
			fb_var.reserved[2] = 0;
			fb_var.xoffset = 0;
			fb_var.yoffset = 0;
			#if LLDCOLOR_BITS == 15
				fb_var.bits_per_pixel = LLDCOLOR_BITS;				// Handle RGB555 & BGR555
			#else
				fb_var.bits_per_pixel = sizeof(LLDCOLOR_TYPE)*8;
			#endif
			fb_var.grayscale = 0;
			fb_var.activate = FB_ACTIVATE_NOW;
			if (ioctl(fb, FBIOPUT_VSCREENINFO, &fb_var) == -1 || ioctl (fb, FBIOGET_VSCREENINFO, &fb_var) == -1) {
				fprintf(stderr, "GDISP Framebuffer: Failed to set video mode\n");
				exit(-1);
			}
		#endif

		// Check things are as they should be
		if (fb_fix.type != FB_TYPE_PACKED_PIXELS) {
			fprintf(stderr, "GDISP Framebuffer: The display is not in a single plane graphics mode\n");
			exit(-1);
		}
		if (fb_fix.visual != FB_VISUAL_TRUECOLOR) {
			fprintf(stderr, "GDISP Framebuffer: The display is not in TRUECOLOR mode\n");
			exit(-1);
		}
		if (fb_var.bits_per_pixel != sizeof(LLDCOLOR_TYPE)*8) {
			fprintf(stderr, "GDISP Framebuffer: The display is %u not %u bits per pixel\n", fb_var.bits_per_pixel, LLDCOLOR_TYPE_BITS);
			exit(-1);
		}
		if (fb_var.red.length != LLDCOLOR_BITS_R || fb_var.green.length != LLDCOLOR_BITS_G || fb_var.blue.length != LLDCOLOR_BITS_B) {
			fprintf(stderr, "GDISP Framebuffer: The display pixel format is not %d%d%d\n", LLDCOLOR_BITS_R, LLDCOLOR_BITS_G, LLDCOLOR_BITS_B);
			exit(-1);
		}
		if (fb_var.red.offset != LLDCOLOR_SHIFT_R || fb_var.green.offset != LLDCOLOR_SHIFT_G || fb_var.blue.offset != LLDCOLOR_SHIFT_B) {
			#if LLDCOLOR_SHIFT_B == 0
				fprintf(stderr, "GDISP Framebuffer: The display pixel format is not RGB\n");
			#else
				fprintf(stderr, "GDISP Framebuffer: The display pixel format is not BGR\n");
			#endif
			exit(-1);
		}

		// Ensure we are at the origin of the virtual display area
		if (fb_var.xoffset || fb_var.yoffset) {
			fb_var.xoffset = 0;
			fb_var.yoffset = 0;
			ioctl(fb, FBIOPAN_DISPLAY, &fb_var);
		}

		// Switch to graphics mode (if required)
		#ifdef VTDEV_PATH
			board_switch2graphicsmode();
		#endif

		// Calculate the frame buffer length
		fblen = fb_var.yres * fb_fix.line_length;

		// Different systems need mapping in slightly different ways - Yuck!
		#ifdef ARCH_LINUX_SPARC
			#define CG3_MMAP_OFFSET 0x4000000
			#define CG6_RAM    		0x70016000
			#define TCX_RAM8BIT		0x00000000
			#define TCX_RAM24BIT	0x01000000
			switch (fb_fix.accel) {
			case FB_ACCEL_SUN_CGTHREE:
				fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, CG3_MMAP_OFFSET);
				break;
			case FB_ACCEL_SUN_CGSIX:
				fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, CG6_RAM);
				break;
			case FB_ACCEL_SUN_TCX:
				fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, TCX_RAM24BIT);
				break;
			default:
				fprintf(stderr, "GDISP Framebuffer: Don't know how to mmap with accel %d\n", fb_fix.accel);
				exit(-1);
			}
		#elif defined(BLACKFIN)
			fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FILE, fb, 0);
		#elif defined(__uClinux__)
			fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, 0, fb, 0);
		#else
			fbi->pixels = mmap(0, fblen, PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0);
		#endif
		if(!fbi->pixels || fbi->pixels == (void *)-1) {
			fprintf(stderr, "GDISP Framebuffer: mmap of display buffer failed\n");
			exit(-1);
		}

		// If this program gets children they should not inherit this file descriptor
		fcntl(fb, F_SETFD, FD_CLOEXEC);

		// We are finished with the file descriptor
		close(fb);

		// Set the rest of the details of the frame buffer
		g->g.Width = fb_var.xres;
		g->g.Height = fb_var.yres;
		g->g.Backlight = 100;
		g->g.Contrast = 50;
		fbi->linelen = fb_fix.line_length;
	}

	#if GDISP_HARDWARE_FLUSH
		static void board_flush(GDisplay *g) {
			(void) g;
		}
	#endif

	#if GDISP_NEED_CONTROL
		static void board_backlight(GDisplay *g, gU8 percent) {
			(void) g;
			(void) percent;
		}

		static void board_contrast(GDisplay *g, gU8 percent) {
			(void) g;
			(void) percent;
		}

		static void board_power(GDisplay *g, gPowermode pwr) {
			(void) g;
			(void) pwr;
		}
	#endif

#endif /* GDISP_LLD_BOARD_IMPLEMENTATION */