Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:04

0001 /**
0002  * @file
0003  *
0004  * @ingroup raspberrypi
0005  *
0006  * @brief framebuffer support.
0007  */
0008 
0009 /*
0010  * Copyright (c) 2015 Yang Qiao
0011  *
0012  *  The license and distribution terms for this file may be
0013  *  found in the file LICENSE in this distribution or at
0014  *
0015  *  http://www.rtems.org/license/LICENSE
0016  *
0017  */
0018 
0019 #include <stdlib.h>
0020 #include <string.h>
0021 #include <stdio.h>
0022 #include <errno.h>
0023 #include <sys/types.h>
0024 
0025 #include <bsp.h>
0026 #include <bsp/raspberrypi.h>
0027 #include <bsp/mailbox.h>
0028 #include <bsp/vc.h>
0029 #include <bsp/rpi-fb.h>
0030 
0031 #include <libcpu/arm-cp15.h>
0032 
0033 #include <rtems.h>
0034 #include <rtems/libio.h>
0035 #include <rtems/fb.h>
0036 #include <rtems/framebuffer.h>
0037 #include <rtems/score/atomic.h>
0038 #include <rtems/bspIo.h>
0039 
0040 #define SCREEN_WIDTH 1024
0041 #define SCREEN_HEIGHT 768
0042 #define BPP 32
0043 
0044 /* flag to limit driver to protect against multiple opens */
0045 static Atomic_Flag driver_mutex;
0046 
0047 /*
0048  * screen information for the driver (fb0).
0049  */
0050 
0051 static struct fb_var_screeninfo fb_var_info = {
0052   .xres                = SCREEN_WIDTH,
0053   .yres                = SCREEN_HEIGHT,
0054   .bits_per_pixel      = BPP
0055 };
0056 
0057 static struct fb_fix_screeninfo fb_fix_info = {
0058   .smem_start          = (void *) NULL,
0059   .smem_len            = 0,
0060   .type                = FB_TYPE_PACKED_PIXELS,
0061   .visual              = FB_VISUAL_TRUECOLOR,
0062   .line_length         = 0
0063 };
0064 
0065 typedef enum {
0066   NO_SUITABLE_MODE     = -1,
0067   BAD_FORMAT           = -2,
0068   AUTO_SELECT          = -3,
0069   DONT_INIT            = -4,
0070   NO_MODE_REQ          = -5,
0071 } mode_err_ret_val;
0072 
0073 int rpi_get_fix_screen_info( struct fb_fix_screeninfo *info )
0074 {
0075   *info = fb_fix_info;
0076   return 0;
0077 }
0078 
0079 int rpi_get_var_screen_info( struct fb_var_screeninfo *info )
0080 {
0081   *info = fb_var_info;
0082   return 0;
0083 }
0084 
0085 /**
0086  * @brief Find mode given in string format.
0087  *
0088  *  expected format
0089  *  <resX>x<resY>[-<bpp>]
0090  *  numbers <resX>, <resY> and <bpp> are decadic
0091  *
0092  * @param[out] fb_var_ptr pointer to variable mode part filled by function
0093  * @param[in] video_string string to be parsed
0094  * @retval video mode number to be set
0095  * @retval -1 no suitable mode found
0096  * @retval -2 bad format of the video_string
0097  * @retval -3 automatic mode selection requested
0098  * @retval -4 request to not initialize graphics
0099  * @retval -5 no mode requested/empty video string
0100  */
0101 
0102 static int parse_mode_from_string(
0103   struct fb_var_screeninfo *fb_var_ptr,
0104   const char               *video_string
0105 )
0106 {
0107   const char *opt;
0108   char       *endptr;
0109   uint32_t    width;
0110   uint32_t    height;
0111   uint32_t    bpp = 0;
0112 
0113   opt = video_string;
0114 
0115   if ( opt == NULL )
0116     return NO_MODE_REQ;
0117 
0118   if ( strncmp( opt, "auto", 4 ) == 0 )
0119     return AUTO_SELECT;
0120 
0121   if ( strncmp( opt, "none", 4 ) == 0 ||
0122        strncmp( opt, "off", 3 ) == 0 )
0123     return DONT_INIT;
0124 
0125   width = strtol( opt, &endptr, 10 );
0126 
0127   if ( *endptr != 'x' ) {
0128     return BAD_FORMAT;
0129   }
0130 
0131   opt = endptr + 1;
0132   height = strtol( opt, &endptr, 10 );
0133 
0134   switch ( *endptr ) {
0135     case '-':
0136       opt = endptr + 1;
0137       endptr = NULL;
0138       bpp = strtol( opt, &endptr, 10 );
0139 
0140       if ( ( endptr == opt ) || ( endptr == NULL ) )
0141         return BAD_FORMAT;
0142 
0143       if ( *endptr && ( *endptr != ' ' ) )
0144         return BAD_FORMAT;
0145 
0146       break;
0147     case ' ':
0148     case 0:
0149       break;
0150     default:
0151       return BAD_FORMAT;
0152   }
0153 
0154   fb_var_ptr->xres = width;
0155   fb_var_ptr->yres = height;
0156 
0157   if ( bpp != 0 )
0158     fb_var_ptr->bits_per_pixel = bpp;
0159 
0160   return 0;
0161 }
0162 
0163 static int find_mode_from_vc( void )
0164 {
0165   int res;
0166   unsigned int width;
0167   unsigned int height;
0168   bcm2835_get_display_size_entries entries;
0169 
0170   res = bcm2835_mailbox_get_display_size( &entries );
0171 
0172   width = entries.width;
0173   height = entries.height;
0174 
0175   if ( width == 0 || height == 0 ) {
0176     fb_var_info.xres = SCREEN_WIDTH;
0177     fb_var_info.yres = SCREEN_HEIGHT;
0178   } else {
0179     fb_var_info.xres = width;
0180     fb_var_info.yres = height;
0181   }
0182   printk("find_mode_from_vc %u x %u, res %d\n", width, height, res);
0183 
0184   return res;
0185 }
0186 
0187 bool rpi_fb_hdmi_is_present( void )
0188 {
0189   bcm2835_get_display_size_entries entries;
0190 
0191   memset( &entries, 0, sizeof( entries ) );
0192   bcm2835_mailbox_get_display_size( &entries );
0193 
0194   /* Impossible display dimension */
0195   if ( ( entries.width < 10 ) || ( entries.height < 10 ) )
0196     return false;
0197 
0198   /* Know default values reported when monitor is not present */
0199   if ( ( entries.width == 0x290 ) && ( entries.height == 0x1A0 ) )
0200     return false;
0201 
0202   return true;
0203 }
0204 
0205 int rpi_fb_init( void )
0206 {
0207   int res;
0208   int                               mode_from_cmdline;
0209   bcm2835_init_frame_buffer_entries init_frame_buffer_entries;
0210 
0211   if ( fb_fix_info.smem_start != NULL ) {
0212     return RPI_FB_INIT_ALREADY_INITIALIZED;
0213   }
0214 
0215   if ( rpi_fb_hdmi_is_present() == false ) {
0216     return RPI_FB_INIT_NO_DISPLAY;
0217   }
0218 
0219   mode_from_cmdline = parse_mode_from_string( &fb_var_info,
0220     rpi_cmdline_get_arg( "--video=" ) );
0221 
0222   switch ( mode_from_cmdline ) {
0223     case BAD_FORMAT:
0224       return RPI_FB_INIT_CMDLINE_BAD_FORMAT;
0225     case AUTO_SELECT:
0226       break;
0227     case DONT_INIT:
0228       return RPI_FB_INIT_CMDLINE_DONT_INIT;
0229     case NO_MODE_REQ:
0230       return RPI_FB_INIT_CMDLINE_NO_MODE_REQ;
0231   }
0232 
0233   if ( mode_from_cmdline ) {
0234     if ( find_mode_from_vc() )
0235       return RPI_FB_INIT_MODE_PROBE_ERROR;
0236   }
0237 
0238   memset( &init_frame_buffer_entries, 0, sizeof( init_frame_buffer_entries ) );
0239   init_frame_buffer_entries.xres = fb_var_info.xres;
0240   init_frame_buffer_entries.yres = fb_var_info.yres;
0241   init_frame_buffer_entries.xvirt = fb_var_info.xres;
0242   init_frame_buffer_entries.yvirt = fb_var_info.yres;
0243   init_frame_buffer_entries.depth = fb_var_info.bits_per_pixel;
0244   init_frame_buffer_entries.pixel_order = bcm2835_mailbox_pixel_order_rgb;
0245   init_frame_buffer_entries.alpha_mode = bcm2835_mailbox_alpha_mode_0_opaque;
0246   init_frame_buffer_entries.voffset_x = 0;
0247   init_frame_buffer_entries.voffset_y = 0;
0248   init_frame_buffer_entries.overscan_left = 0;
0249   init_frame_buffer_entries.overscan_right = 0;
0250   init_frame_buffer_entries.overscan_top = 0;
0251   init_frame_buffer_entries.overscan_bottom = 0;
0252   printk("bcm2835_mailbox_init_frame_buffer ...\n");
0253   res = bcm2835_mailbox_init_frame_buffer( &init_frame_buffer_entries );
0254   printk("bcm2835_mailbox_init_frame_buffer returned %d\n", res);
0255   if (res != 0) {
0256     printk("bcm2835_mailbox_init_frame_buffer retry ...\n");
0257     res = bcm2835_mailbox_init_frame_buffer( &init_frame_buffer_entries );
0258     printk("bcm2835_mailbox_init_frame_buffer returned %d\n", res);
0259     if (res != 0)
0260       return RPI_FB_INIT_SETUP_FAILED;
0261   }
0262 
0263   bcm2835_get_pitch_entries get_pitch_entries;
0264   bcm2835_mailbox_get_pitch( &get_pitch_entries );
0265 
0266   fb_var_info.xres = init_frame_buffer_entries.xres;
0267   fb_var_info.yres = init_frame_buffer_entries.yres;
0268   fb_var_info.bits_per_pixel = init_frame_buffer_entries.depth;
0269   fb_fix_info.smem_start = (void *) init_frame_buffer_entries.base;
0270   fb_fix_info.smem_len = init_frame_buffer_entries.size;
0271   fb_fix_info.line_length = get_pitch_entries.pitch;
0272 
0273   if ( fb_fix_info.smem_start == NULL )
0274     return RPI_FB_INIT_START_ADDR_UNKNOWN;
0275 
0276   printk("fb_fix_info.smem_start %p\n", fb_fix_info.smem_start);
0277 
0278   arm_cp15_set_translation_table_entries( (void *) fb_fix_info.smem_start,
0279     (void *) fb_fix_info.smem_start +
0280     fb_fix_info.smem_len,
0281     ARMV7_MMU_DATA_READ_WRITE_CACHED );
0282 
0283   return RPI_FB_INIT_OK;
0284 }
0285 
0286 /*
0287  * fbds device driver initialize entry point.
0288  */
0289 
0290 rtems_device_driver frame_buffer_initialize(
0291   rtems_device_major_number major,
0292   rtems_device_minor_number minor,
0293   void                     *arg
0294 )
0295 {
0296   rtems_status_code status;
0297 
0298   /* register the devices */
0299   status = rtems_io_register_name( FRAMEBUFFER_DEVICE_0_NAME, major, 0 );
0300 
0301   if ( status != RTEMS_SUCCESSFUL ) {
0302     printk( "[!] error registering framebuffer\n" );
0303     rtems_fatal_error_occurred( status );
0304   }
0305 
0306   _Atomic_Flag_clear( &driver_mutex, ATOMIC_ORDER_RELEASE );
0307   return RTEMS_SUCCESSFUL;
0308 }
0309 
0310 /*
0311  * fbds device driver open operation.
0312  */
0313 
0314 rtems_device_driver frame_buffer_open(
0315   rtems_device_major_number major,
0316   rtems_device_minor_number minor,
0317   void                     *arg
0318 )
0319 {
0320   if ( _Atomic_Flag_test_and_set( &driver_mutex,
0321          ATOMIC_ORDER_ACQUIRE ) != 0 ) {
0322     printk( "RaspberryPi framebuffer could not lock driver_mutex\n" );
0323     return RTEMS_UNSATISFIED;
0324   }
0325 
0326   if ( fb_fix_info.smem_start == NULL ) {
0327     int res;
0328     res = rpi_fb_init();
0329     if ( (res < RPI_FB_INIT_OK) || (fb_fix_info.smem_start == NULL) ) {
0330       _Atomic_Flag_clear( &driver_mutex, ATOMIC_ORDER_RELEASE );
0331       printk( "RaspberryPi framebuffer initialization failed\n" );
0332       return RTEMS_UNSATISFIED;
0333     }
0334   }
0335 
0336   memset( (void *) fb_fix_info.smem_start, 0, fb_fix_info.smem_len );
0337   return RTEMS_SUCCESSFUL;
0338 }
0339 
0340 /*
0341  * fbds device driver close operation.
0342  */
0343 
0344 rtems_device_driver frame_buffer_close(
0345   rtems_device_major_number major,
0346   rtems_device_minor_number minor,
0347   void                     *arg
0348 )
0349 {
0350   /* restore previous state.  for VGA this means return to text mode.
0351    * leave out if graphics hardware has been initialized in
0352    * frame_buffer_initialize() */
0353   memset( (void *) fb_fix_info.smem_start, 0, fb_fix_info.smem_len );
0354   _Atomic_Flag_clear( &driver_mutex, ATOMIC_ORDER_RELEASE );
0355   return RTEMS_SUCCESSFUL;
0356 }
0357 
0358 /*
0359  * fbds device driver read operation.
0360  */
0361 
0362 rtems_device_driver frame_buffer_read(
0363   rtems_device_major_number major,
0364   rtems_device_minor_number minor,
0365   void                     *arg
0366 )
0367 {
0368   rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg;
0369 
0370   rw_args->bytes_moved =
0371     ( ( rw_args->offset + rw_args->count ) > fb_fix_info.smem_len ) ?
0372     ( fb_fix_info.smem_len - rw_args->offset ) : rw_args->count;
0373   memcpy( rw_args->buffer,
0374     (const void *) ( fb_fix_info.smem_start + rw_args->offset ),
0375     rw_args->bytes_moved );
0376   return RTEMS_SUCCESSFUL;
0377 }
0378 
0379 /*
0380  * fbds device driver write operation.
0381  */
0382 
0383 rtems_device_driver frame_buffer_write(
0384   rtems_device_major_number major,
0385   rtems_device_minor_number minor,
0386   void                     *arg
0387 )
0388 {
0389   rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t *) arg;
0390 
0391   rw_args->bytes_moved =
0392     ( ( rw_args->offset + rw_args->count ) > fb_fix_info.smem_len ) ?
0393     ( fb_fix_info.smem_len - rw_args->offset ) : rw_args->count;
0394   memcpy( (void *) ( fb_fix_info.smem_start + rw_args->offset ),
0395     rw_args->buffer,
0396     rw_args->bytes_moved );
0397   return RTEMS_SUCCESSFUL;
0398 }
0399 
0400 /*
0401  * ioctl entry point.
0402  */
0403 
0404 rtems_device_driver frame_buffer_control(
0405   rtems_device_major_number major,
0406   rtems_device_minor_number minor,
0407   void                     *arg
0408 )
0409 {
0410   rtems_libio_ioctl_args_t *args = arg;
0411 
0412   /* XXX check minor */
0413 
0414   switch ( args->command ) {
0415     case FBIOGET_VSCREENINFO:
0416       memcpy( args->buffer, &fb_var_info, sizeof( fb_var_info ) );
0417       args->ioctl_return = 0;
0418       break;
0419     case FBIOGET_FSCREENINFO:
0420       memcpy( args->buffer, &fb_fix_info, sizeof( fb_fix_info ) );
0421       args->ioctl_return = 0;
0422       break;
0423     case FBIOGETCMAP:
0424       /* no palette - truecolor mode */
0425       args->ioctl_return = -1;
0426       return RTEMS_UNSATISFIED;
0427     case FBIOPUTCMAP:
0428       /* no palette - truecolor mode */
0429       args->ioctl_return = -1;
0430       return RTEMS_UNSATISFIED;
0431     default:
0432       args->ioctl_return = -1;
0433       return RTEMS_UNSATISFIED;
0434   }
0435 
0436   return RTEMS_SUCCESSFUL;
0437 }