1 | /** |
---|
2 | * @file |
---|
3 | * |
---|
4 | * @ingroup raspberrypi |
---|
5 | * |
---|
6 | * @brief framebuffer support. |
---|
7 | */ |
---|
8 | |
---|
9 | /* |
---|
10 | * Copyright (c) 2015 Yang Qiao |
---|
11 | * |
---|
12 | * The license and distribution terms for this file may be |
---|
13 | * found in the file LICENSE in this distribution or at |
---|
14 | * |
---|
15 | * http://www.rtems.org/license/LICENSE |
---|
16 | * |
---|
17 | */ |
---|
18 | |
---|
19 | #include <stdlib.h> |
---|
20 | #include <string.h> |
---|
21 | #include <stdio.h> |
---|
22 | #include <errno.h> |
---|
23 | #include <sys/types.h> |
---|
24 | |
---|
25 | #include <bsp.h> |
---|
26 | #include <bsp/raspberrypi.h> |
---|
27 | #include <bsp/mailbox.h> |
---|
28 | #include <bsp/vc.h> |
---|
29 | #include <bsp/rpi-fb.h> |
---|
30 | |
---|
31 | #include <libcpu/arm-cp15.h> |
---|
32 | |
---|
33 | #include <rtems.h> |
---|
34 | #include <rtems/libio.h> |
---|
35 | #include <rtems/fb.h> |
---|
36 | #include <rtems/framebuffer.h> |
---|
37 | #include <rtems/score/atomic.h> |
---|
38 | #include <rtems/bspIo.h> |
---|
39 | |
---|
40 | #define SCREEN_WIDTH 1024 |
---|
41 | #define SCREEN_HEIGHT 768 |
---|
42 | #define BPP 32 |
---|
43 | |
---|
44 | /* flag to limit driver to protect against multiple opens */ |
---|
45 | static Atomic_Flag driver_mutex; |
---|
46 | |
---|
47 | /* |
---|
48 | * screen information for the driver (fb0). |
---|
49 | */ |
---|
50 | |
---|
51 | static struct fb_var_screeninfo fb_var_info = { |
---|
52 | .xres = SCREEN_WIDTH, |
---|
53 | .yres = SCREEN_HEIGHT, |
---|
54 | .bits_per_pixel = BPP |
---|
55 | }; |
---|
56 | |
---|
57 | static struct fb_fix_screeninfo fb_fix_info = { |
---|
58 | .smem_start = (void *) NULL, |
---|
59 | .smem_len = 0, |
---|
60 | .type = FB_TYPE_PACKED_PIXELS, |
---|
61 | .visual = FB_VISUAL_TRUECOLOR, |
---|
62 | .line_length = 0 |
---|
63 | }; |
---|
64 | |
---|
65 | typedef enum { |
---|
66 | NO_SUITABLE_MODE = -1, |
---|
67 | BAD_FORMAT = -2, |
---|
68 | AUTO_SELECT = -3, |
---|
69 | DONT_INIT = -4, |
---|
70 | NO_MODE_REQ = -5, |
---|
71 | } mode_err_ret_val; |
---|
72 | |
---|
73 | int rpi_get_fix_screen_info( struct fb_fix_screeninfo *info ) |
---|
74 | { |
---|
75 | *info = fb_fix_info; |
---|
76 | return 0; |
---|
77 | } |
---|
78 | |
---|
79 | int rpi_get_var_screen_info( struct fb_var_screeninfo *info ) |
---|
80 | { |
---|
81 | *info = fb_var_info; |
---|
82 | return 0; |
---|
83 | } |
---|
84 | |
---|
85 | /** |
---|
86 | * @brief Find mode given in string format. |
---|
87 | * |
---|
88 | * expected format |
---|
89 | * <resX>x<resY>[-<bpp>] |
---|
90 | * numbers <resX>, <resY> and <bpp> are decadic |
---|
91 | * |
---|
92 | * @param[out] fb_var_ptr pointer to variable mode part filled by function |
---|
93 | * @param[in] video_string string to be parsed |
---|
94 | * @retval video mode number to be set |
---|
95 | * @retval -1 no suitable mode found |
---|
96 | * @retval -2 bad format of the video_string |
---|
97 | * @retval -3 automatic mode selection requested |
---|
98 | * @retval -4 request to not initialize graphics |
---|
99 | * @retval -5 no mode requested/empty video string |
---|
100 | */ |
---|
101 | |
---|
102 | static int parse_mode_from_string( |
---|
103 | struct fb_var_screeninfo *fb_var_ptr, |
---|
104 | const char *video_string |
---|
105 | ) |
---|
106 | { |
---|
107 | const char *opt; |
---|
108 | char *endptr; |
---|
109 | uint32_t width; |
---|
110 | uint32_t height; |
---|
111 | uint32_t bpp = 0; |
---|
112 | |
---|
113 | opt = video_string; |
---|
114 | |
---|
115 | if ( opt == NULL ) |
---|
116 | return NO_MODE_REQ; |
---|
117 | |
---|
118 | if ( strncmp( opt, "auto", 4 ) == 0 ) |
---|
119 | return AUTO_SELECT; |
---|
120 | |
---|
121 | if ( strncmp( opt, "none", 4 ) == 0 || |
---|
122 | strncmp( opt, "off", 3 ) == 0 ) |
---|
123 | return DONT_INIT; |
---|
124 | |
---|
125 | width = strtol( opt, &endptr, 10 ); |
---|
126 | |
---|
127 | if ( *endptr != 'x' ) { |
---|
128 | return BAD_FORMAT; |
---|
129 | } |
---|
130 | |
---|
131 | opt = endptr + 1; |
---|
132 | height = strtol( opt, &endptr, 10 ); |
---|
133 | |
---|
134 | switch ( *endptr ) { |
---|
135 | case '-': |
---|
136 | opt = endptr + 1; |
---|
137 | endptr = NULL; |
---|
138 | bpp = strtol( opt, &endptr, 10 ); |
---|
139 | |
---|
140 | if ( ( endptr == opt ) || ( endptr == NULL ) ) |
---|
141 | return BAD_FORMAT; |
---|
142 | |
---|
143 | if ( *endptr && ( *endptr != ' ' ) ) |
---|
144 | return BAD_FORMAT; |
---|
145 | |
---|
146 | break; |
---|
147 | case ' ': |
---|
148 | case 0: |
---|
149 | break; |
---|
150 | default: |
---|
151 | return BAD_FORMAT; |
---|
152 | } |
---|
153 | |
---|
154 | fb_var_ptr->xres = width; |
---|
155 | fb_var_ptr->yres = height; |
---|
156 | |
---|
157 | if ( bpp != 0 ) |
---|
158 | fb_var_ptr->bits_per_pixel = bpp; |
---|
159 | |
---|
160 | return 0; |
---|
161 | } |
---|
162 | |
---|
163 | static int find_mode_from_vc( void ) |
---|
164 | { |
---|
165 | int res; |
---|
166 | unsigned int width; |
---|
167 | unsigned int height; |
---|
168 | bcm2835_get_display_size_entries entries; |
---|
169 | |
---|
170 | res = bcm2835_mailbox_get_display_size( &entries ); |
---|
171 | |
---|
172 | width = entries.width; |
---|
173 | height = entries.height; |
---|
174 | |
---|
175 | if ( width == 0 || height == 0 ) { |
---|
176 | fb_var_info.xres = SCREEN_WIDTH; |
---|
177 | fb_var_info.yres = SCREEN_HEIGHT; |
---|
178 | } else { |
---|
179 | fb_var_info.xres = width; |
---|
180 | fb_var_info.yres = height; |
---|
181 | } |
---|
182 | printk("find_mode_from_vc %u x %u, res %d\n", width, height, res); |
---|
183 | |
---|
184 | return res; |
---|
185 | } |
---|
186 | |
---|
187 | bool rpi_fb_hdmi_is_present( void ) |
---|
188 | { |
---|
189 | bcm2835_get_display_size_entries entries; |
---|
190 | |
---|
191 | memset( &entries, 0, sizeof( entries ) ); |
---|
192 | bcm2835_mailbox_get_display_size( &entries ); |
---|
193 | |
---|
194 | /* Impossible display dimension */ |
---|
195 | if ( ( entries.width < 10 ) || ( entries.height < 10 ) ) |
---|
196 | return false; |
---|
197 | |
---|
198 | /* Know default values reported when monitor is not present */ |
---|
199 | if ( ( entries.width == 0x290 ) && ( entries.height == 0x1A0 ) ) |
---|
200 | return false; |
---|
201 | |
---|
202 | return true; |
---|
203 | } |
---|
204 | |
---|
205 | int rpi_fb_init( void ) |
---|
206 | { |
---|
207 | int res; |
---|
208 | int mode_from_cmdline; |
---|
209 | bcm2835_init_frame_buffer_entries init_frame_buffer_entries; |
---|
210 | |
---|
211 | if ( fb_fix_info.smem_start != NULL ) { |
---|
212 | return RPI_FB_INIT_ALREADY_INITIALIZED; |
---|
213 | } |
---|
214 | |
---|
215 | if ( rpi_fb_hdmi_is_present() == false ) { |
---|
216 | return RPI_FB_INIT_NO_DISPLAY; |
---|
217 | } |
---|
218 | |
---|
219 | mode_from_cmdline = parse_mode_from_string( &fb_var_info, |
---|
220 | rpi_cmdline_get_arg( "--video=" ) ); |
---|
221 | |
---|
222 | switch ( mode_from_cmdline ) { |
---|
223 | case BAD_FORMAT: |
---|
224 | return RPI_FB_INIT_CMDLINE_BAD_FORMAT; |
---|
225 | case AUTO_SELECT: |
---|
226 | break; |
---|
227 | case DONT_INIT: |
---|
228 | return RPI_FB_INIT_CMDLINE_DONT_INIT; |
---|
229 | case NO_MODE_REQ: |
---|
230 | return RPI_FB_INIT_CMDLINE_NO_MODE_REQ; |
---|
231 | } |
---|
232 | |
---|
233 | if ( mode_from_cmdline ) { |
---|
234 | if ( find_mode_from_vc() ) |
---|
235 | return RPI_FB_INIT_MODE_PROBE_ERROR; |
---|
236 | } |
---|
237 | |
---|
238 | memset( &init_frame_buffer_entries, 0, sizeof( init_frame_buffer_entries ) ); |
---|
239 | init_frame_buffer_entries.xres = fb_var_info.xres; |
---|
240 | init_frame_buffer_entries.yres = fb_var_info.yres; |
---|
241 | init_frame_buffer_entries.xvirt = fb_var_info.xres; |
---|
242 | init_frame_buffer_entries.yvirt = fb_var_info.yres; |
---|
243 | init_frame_buffer_entries.depth = fb_var_info.bits_per_pixel; |
---|
244 | init_frame_buffer_entries.pixel_order = bcm2835_mailbox_pixel_order_rgb; |
---|
245 | init_frame_buffer_entries.alpha_mode = bcm2835_mailbox_alpha_mode_0_opaque; |
---|
246 | init_frame_buffer_entries.voffset_x = 0; |
---|
247 | init_frame_buffer_entries.voffset_y = 0; |
---|
248 | init_frame_buffer_entries.overscan_left = 0; |
---|
249 | init_frame_buffer_entries.overscan_right = 0; |
---|
250 | init_frame_buffer_entries.overscan_top = 0; |
---|
251 | init_frame_buffer_entries.overscan_bottom = 0; |
---|
252 | printk("bcm2835_mailbox_init_frame_buffer ...\n"); |
---|
253 | res = bcm2835_mailbox_init_frame_buffer( &init_frame_buffer_entries ); |
---|
254 | printk("bcm2835_mailbox_init_frame_buffer returned %d\n", res); |
---|
255 | if (res != 0) { |
---|
256 | printk("bcm2835_mailbox_init_frame_buffer retry ...\n"); |
---|
257 | res = bcm2835_mailbox_init_frame_buffer( &init_frame_buffer_entries ); |
---|
258 | printk("bcm2835_mailbox_init_frame_buffer returned %d\n", res); |
---|
259 | if (res != 0) |
---|
260 | return RPI_FB_INIT_SETUP_FAILED; |
---|
261 | } |
---|
262 | |
---|
263 | bcm2835_get_pitch_entries get_pitch_entries; |
---|
264 | bcm2835_mailbox_get_pitch( &get_pitch_entries ); |
---|
265 | |
---|
266 | fb_var_info.xres = init_frame_buffer_entries.xres; |
---|
267 | fb_var_info.yres = init_frame_buffer_entries.yres; |
---|
268 | fb_var_info.bits_per_pixel = init_frame_buffer_entries.depth; |
---|
269 | fb_fix_info.smem_start = (void *) init_frame_buffer_entries.base; |
---|
270 | fb_fix_info.smem_len = init_frame_buffer_entries.size; |
---|
271 | fb_fix_info.line_length = get_pitch_entries.pitch; |
---|
272 | |
---|
273 | if ( fb_fix_info.smem_start == NULL ) |
---|
274 | return RPI_FB_INIT_START_ADDR_UNKNOWN; |
---|
275 | |
---|
276 | printk("fb_fix_info.smem_start %p\n", fb_fix_info.smem_start); |
---|
277 | |
---|
278 | arm_cp15_set_translation_table_entries( (void *) fb_fix_info.smem_start, |
---|
279 | (void *) fb_fix_info.smem_start + |
---|
280 | fb_fix_info.smem_len, |
---|
281 | ARMV7_MMU_DATA_READ_WRITE_CACHED ); |
---|
282 | |
---|
283 | return RPI_FB_INIT_OK; |
---|
284 | } |
---|
285 | |
---|
286 | /* |
---|
287 | * fbds device driver initialize entry point. |
---|
288 | */ |
---|
289 | |
---|
290 | rtems_device_driver frame_buffer_initialize( |
---|
291 | rtems_device_major_number major, |
---|
292 | rtems_device_minor_number minor, |
---|
293 | void *arg |
---|
294 | ) |
---|
295 | { |
---|
296 | rtems_status_code status; |
---|
297 | |
---|
298 | /* register the devices */ |
---|
299 | status = rtems_io_register_name( FRAMEBUFFER_DEVICE_0_NAME, major, 0 ); |
---|
300 | |
---|
301 | if ( status != RTEMS_SUCCESSFUL ) { |
---|
302 | printk( "[!] error registering framebuffer\n" ); |
---|
303 | rtems_fatal_error_occurred( status ); |
---|
304 | } |
---|
305 | |
---|
306 | _Atomic_Flag_clear( &driver_mutex, ATOMIC_ORDER_RELEASE ); |
---|
307 | return RTEMS_SUCCESSFUL; |
---|
308 | } |
---|
309 | |
---|
310 | /* |
---|
311 | * fbds device driver open operation. |
---|
312 | */ |
---|
313 | |
---|
314 | rtems_device_driver frame_buffer_open( |
---|
315 | rtems_device_major_number major, |
---|
316 | rtems_device_minor_number minor, |
---|
317 | void *arg |
---|
318 | ) |
---|
319 | { |
---|
320 | if ( _Atomic_Flag_test_and_set( &driver_mutex, |
---|
321 | ATOMIC_ORDER_ACQUIRE ) != 0 ) { |
---|
322 | printk( "RaspberryPi framebuffer could not lock driver_mutex\n" ); |
---|
323 | return RTEMS_UNSATISFIED; |
---|
324 | } |
---|
325 | |
---|
326 | if ( fb_fix_info.smem_start == NULL ) { |
---|
327 | int res; |
---|
328 | res = rpi_fb_init(); |
---|
329 | if ( (res < RPI_FB_INIT_OK) || (fb_fix_info.smem_start == NULL) ) { |
---|
330 | _Atomic_Flag_clear( &driver_mutex, ATOMIC_ORDER_RELEASE ); |
---|
331 | printk( "RaspberryPi framebuffer initialization failed\n" ); |
---|
332 | return RTEMS_UNSATISFIED; |
---|
333 | } |
---|
334 | } |
---|
335 | |
---|
336 | memset( (void *) fb_fix_info.smem_start, 0, fb_fix_info.smem_len ); |
---|
337 | return RTEMS_SUCCESSFUL; |
---|
338 | } |
---|
339 | |
---|
340 | /* |
---|
341 | * fbds device driver close operation. |
---|
342 | */ |
---|
343 | |
---|
344 | rtems_device_driver frame_buffer_close( |
---|
345 | rtems_device_major_number major, |
---|
346 | rtems_device_minor_number minor, |
---|
347 | void *arg |
---|
348 | ) |
---|
349 | { |
---|
350 | /* restore previous state. for VGA this means return to text mode. |
---|
351 | * leave out if graphics hardware has been initialized in |
---|
352 | * frame_buffer_initialize() */ |
---|
353 | memset( (void *) fb_fix_info.smem_start, 0, fb_fix_info.smem_len ); |
---|
354 | _Atomic_Flag_clear( &driver_mutex, ATOMIC_ORDER_RELEASE ); |
---|
355 | return RTEMS_SUCCESSFUL; |
---|
356 | } |
---|
357 | |
---|
358 | /* |
---|
359 | * fbds device driver read operation. |
---|
360 | */ |
---|
361 | |
---|
362 | rtems_device_driver frame_buffer_read( |
---|
363 | rtems_device_major_number major, |
---|
364 | rtems_device_minor_number minor, |
---|
365 | void *arg |
---|
366 | ) |
---|
367 | { |
---|
368 | rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg; |
---|
369 | |
---|
370 | rw_args->bytes_moved = |
---|
371 | ( ( rw_args->offset + rw_args->count ) > fb_fix_info.smem_len ) ? |
---|
372 | ( fb_fix_info.smem_len - rw_args->offset ) : rw_args->count; |
---|
373 | memcpy( rw_args->buffer, |
---|
374 | (const void *) ( fb_fix_info.smem_start + rw_args->offset ), |
---|
375 | rw_args->bytes_moved ); |
---|
376 | return RTEMS_SUCCESSFUL; |
---|
377 | } |
---|
378 | |
---|
379 | /* |
---|
380 | * fbds device driver write operation. |
---|
381 | */ |
---|
382 | |
---|
383 | rtems_device_driver frame_buffer_write( |
---|
384 | rtems_device_major_number major, |
---|
385 | rtems_device_minor_number minor, |
---|
386 | void *arg |
---|
387 | ) |
---|
388 | { |
---|
389 | rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg; |
---|
390 | |
---|
391 | rw_args->bytes_moved = |
---|
392 | ( ( rw_args->offset + rw_args->count ) > fb_fix_info.smem_len ) ? |
---|
393 | ( fb_fix_info.smem_len - rw_args->offset ) : rw_args->count; |
---|
394 | memcpy( (void *) ( fb_fix_info.smem_start + rw_args->offset ), |
---|
395 | rw_args->buffer, |
---|
396 | rw_args->bytes_moved ); |
---|
397 | return RTEMS_SUCCESSFUL; |
---|
398 | } |
---|
399 | |
---|
400 | /* |
---|
401 | * ioctl entry point. |
---|
402 | */ |
---|
403 | |
---|
404 | rtems_device_driver frame_buffer_control( |
---|
405 | rtems_device_major_number major, |
---|
406 | rtems_device_minor_number minor, |
---|
407 | void *arg |
---|
408 | ) |
---|
409 | { |
---|
410 | rtems_libio_ioctl_args_t *args = arg; |
---|
411 | |
---|
412 | /* XXX check minor */ |
---|
413 | |
---|
414 | switch ( args->command ) { |
---|
415 | case FBIOGET_VSCREENINFO: |
---|
416 | memcpy( args->buffer, &fb_var_info, sizeof( fb_var_info ) ); |
---|
417 | args->ioctl_return = 0; |
---|
418 | break; |
---|
419 | case FBIOGET_FSCREENINFO: |
---|
420 | memcpy( args->buffer, &fb_fix_info, sizeof( fb_fix_info ) ); |
---|
421 | args->ioctl_return = 0; |
---|
422 | break; |
---|
423 | case FBIOGETCMAP: |
---|
424 | /* no palette - truecolor mode */ |
---|
425 | args->ioctl_return = -1; |
---|
426 | return RTEMS_UNSATISFIED; |
---|
427 | case FBIOPUTCMAP: |
---|
428 | /* no palette - truecolor mode */ |
---|
429 | args->ioctl_return = -1; |
---|
430 | return RTEMS_UNSATISFIED; |
---|
431 | default: |
---|
432 | args->ioctl_return = -1; |
---|
433 | return RTEMS_UNSATISFIED; |
---|
434 | } |
---|
435 | |
---|
436 | return RTEMS_SUCCESSFUL; |
---|
437 | } |
---|