@ -25,6 +25,7 @@
# include <errno.h>
# include <stdlib.h>
# include <ctype.h>
# include <string.h>
# include <fcntl.h>
# include <unistd.h>
@ -131,6 +132,13 @@ struct drm_fb {
void * map ;
} ;
struct drm_edid {
char eisa_id [ 13 ] ;
char monitor_name [ 13 ] ;
char pnp_id [ 5 ] ;
char serial_number [ 13 ] ;
} ;
struct drm_output {
struct weston_output base ;
@ -139,6 +147,7 @@ struct drm_output {
int pipe ;
uint32_t connector_id ;
drmModeCrtcPtr original_crtc ;
struct drm_edid edid ;
int vblank_pending ;
int page_flip_pending ;
@ -1486,6 +1495,143 @@ drm_output_fini_pixman(struct drm_output *output)
}
}
static void
edid_parse_string ( const uint8_t * data , char text [ ] )
{
int i ;
int replaced = 0 ;
/* this is always 12 bytes, but we can't guarantee it's null
* terminated or not junk . */
strncpy ( text , ( const char * ) data , 12 ) ;
/* remove insane chars */
for ( i = 0 ; text [ i ] ! = ' \0 ' ; i + + ) {
if ( text [ i ] = = ' \n ' | |
text [ i ] = = ' \r ' ) {
text [ i ] = ' \0 ' ;
break ;
}
}
/* ensure string is printable */
for ( i = 0 ; text [ i ] ! = ' \0 ' ; i + + ) {
if ( ! isprint ( text [ i ] ) ) {
text [ i ] = ' - ' ;
replaced + + ;
}
}
/* if the string is random junk, ignore the string */
if ( replaced > 4 )
text [ 0 ] = ' \0 ' ;
}
# define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING 0xfe
# define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME 0xfc
# define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER 0xff
# define EDID_OFFSET_DATA_BLOCKS 0x36
# define EDID_OFFSET_LAST_BLOCK 0x6c
# define EDID_OFFSET_PNPID 0x08
# define EDID_OFFSET_SERIAL 0x0c
static int
edid_parse ( struct drm_edid * edid , const uint8_t * data , size_t length )
{
int i ;
uint32_t serial_number ;
/* check header */
if ( length < 128 )
return - 1 ;
if ( data [ 0 ] ! = 0x00 | | data [ 1 ] ! = 0xff )
return - 1 ;
/* decode the PNP ID from three 5 bit words packed into 2 bytes
* / - - 08 - - \ / - - 09 - - \
* 7654321076543210
* | \ - - - / \ - - - / \ - - - /
* R C1 C2 C3 */
edid - > pnp_id [ 0 ] = ' A ' + ( ( data [ EDID_OFFSET_PNPID + 0 ] & 0x7c ) / 4 ) - 1 ;
edid - > pnp_id [ 1 ] = ' A ' + ( ( data [ EDID_OFFSET_PNPID + 0 ] & 0x3 ) * 8 ) + ( ( data [ EDID_OFFSET_PNPID + 1 ] & 0xe0 ) / 32 ) - 1 ;
edid - > pnp_id [ 2 ] = ' A ' + ( data [ EDID_OFFSET_PNPID + 1 ] & 0x1f ) - 1 ;
edid - > pnp_id [ 3 ] = ' \0 ' ;
/* maybe there isn't a ASCII serial number descriptor, so use this instead */
serial_number = ( uint32_t ) data [ EDID_OFFSET_SERIAL + 0 ] ;
serial_number + = ( uint32_t ) data [ EDID_OFFSET_SERIAL + 1 ] * 0x100 ;
serial_number + = ( uint32_t ) data [ EDID_OFFSET_SERIAL + 2 ] * 0x10000 ;
serial_number + = ( uint32_t ) data [ EDID_OFFSET_SERIAL + 3 ] * 0x1000000 ;
if ( serial_number > 0 )
sprintf ( edid - > serial_number , " %lu " , ( unsigned long ) serial_number ) ;
/* parse EDID data */
for ( i = EDID_OFFSET_DATA_BLOCKS ;
i < = EDID_OFFSET_LAST_BLOCK ;
i + = 18 ) {
/* ignore pixel clock data */
if ( data [ i ] ! = 0 )
continue ;
if ( data [ i + 2 ] ! = 0 )
continue ;
/* any useful blocks? */
if ( data [ i + 3 ] = = EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME ) {
edid_parse_string ( & data [ i + 5 ] ,
edid - > monitor_name ) ;
} else if ( data [ i + 3 ] = = EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER ) {
edid_parse_string ( & data [ i + 5 ] ,
edid - > serial_number ) ;
} else if ( data [ i + 3 ] = = EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING ) {
edid_parse_string ( & data [ i + 5 ] ,
edid - > eisa_id ) ;
}
}
return 0 ;
}
static void
find_and_parse_output_edid ( struct drm_compositor * ec ,
struct drm_output * output ,
drmModeConnector * connector )
{
drmModePropertyBlobPtr edid_blob = NULL ;
drmModePropertyPtr property ;
int i ;
int rc ;
for ( i = 0 ; i < connector - > count_props & & ! edid_blob ; i + + ) {
property = drmModeGetProperty ( ec - > drm . fd , connector - > props [ i ] ) ;
if ( ! property )
continue ;
if ( ( property - > flags & DRM_MODE_PROP_BLOB ) & &
! strcmp ( property - > name , " EDID " ) ) {
edid_blob = drmModeGetPropertyBlob ( ec - > drm . fd ,
connector - > prop_values [ i ] ) ;
}
drmModeFreeProperty ( property ) ;
}
if ( ! edid_blob )
return ;
rc = edid_parse ( & output - > edid ,
edid_blob - > data ,
edid_blob - > length ) ;
if ( ! rc ) {
weston_log ( " EDID data '%s', '%s', '%s' \n " ,
output - > edid . pnp_id ,
output - > edid . monitor_name ,
output - > edid . serial_number ) ;
if ( output - > edid . pnp_id [ 0 ] ! = ' \0 ' )
output - > base . make = output - > edid . pnp_id ;
if ( output - > edid . monitor_name [ 0 ] ! = ' \0 ' )
output - > base . model = output - > edid . monitor_name ;
if ( output - > edid . serial_number [ 0 ] ! = ' \0 ' )
output - > base . serial_number = output - > edid . serial_number ;
}
drmModeFreePropertyBlob ( edid_blob ) ;
}
static int
create_output_for_connector ( struct drm_compositor * ec ,
drmModeRes * resources ,
@ -1517,6 +1663,7 @@ create_output_for_connector(struct drm_compositor *ec,
output - > base . subpixel = drm_subpixel_to_wayland ( connector - > subpixel ) ;
output - > base . make = " unknown " ;
output - > base . model = " unknown " ;
output - > base . serial_number = " unknown " ;
wl_list_init ( & output - > base . mode_list ) ;
if ( connector - > connector_type < ARRAY_LENGTH ( connector_type_names ) )
@ -1642,6 +1789,8 @@ create_output_for_connector(struct drm_compositor *ec,
wl_list_insert ( ec - > base . output_list . prev , & output - > base . link ) ;
find_and_parse_output_edid ( ec , output , connector ) ;
output - > base . origin = output - > base . current ;
output - > base . start_repaint_loop = drm_output_start_repaint_loop ;
output - > base . repaint = drm_output_repaint ;