2019-05-23 11:14:39 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2012-03-18 22:37:33 +01:00
/*
drm_edid_load . c : use a built - in EDID data set or load it via the firmware
interface
Copyright ( C ) 2012 Carsten Emde < C . Emde @ osadl . org >
*/
# include <linux/firmware.h>
2019-05-06 12:52:48 +03:00
# include <linux/module.h>
# include <linux/platform_device.h>
2022-10-24 15:33:41 +03:00
# include <drm/drm_connector.h>
2019-05-06 12:52:48 +03:00
# include <drm/drm_drv.h>
2012-10-02 18:01:07 +01:00
# include <drm/drm_edid.h>
2019-05-06 12:52:48 +03:00
# include <drm/drm_print.h>
2012-03-18 22:37:33 +01:00
2022-10-24 15:33:41 +03:00
# include "drm_crtc_internal.h"
2012-03-18 22:37:33 +01:00
static char edid_firmware [ PATH_MAX ] ;
module_param_string ( edid_firmware , edid_firmware , sizeof ( edid_firmware ) , 0644 ) ;
MODULE_PARM_DESC ( edid_firmware , " Do not probe monitor, use specified EDID blob "
" from built-in data or /lib/firmware instead. " ) ;
2017-09-18 21:20:03 +03:00
/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
int __drm_set_edid_firmware_path ( const char * path )
{
scnprintf ( edid_firmware , sizeof ( edid_firmware ) , " %s " , path ) ;
return 0 ;
}
EXPORT_SYMBOL ( __drm_set_edid_firmware_path ) ;
/* Use only for backward compatibility with drm_kms_helper.edid_firmware */
int __drm_get_edid_firmware_path ( char * buf , size_t bufsize )
{
return scnprintf ( buf , bufsize , " %s " , edid_firmware ) ;
}
EXPORT_SYMBOL ( __drm_get_edid_firmware_path ) ;
2014-05-23 16:01:43 +01:00
# define GENERIC_EDIDS 6
2015-08-31 15:09:25 +03:00
static const char * const generic_edid_name [ GENERIC_EDIDS ] = {
2014-05-23 16:01:43 +01:00
" edid/800x600.bin " ,
2012-03-18 22:37:33 +01:00
" edid/1024x768.bin " ,
" edid/1280x1024.bin " ,
2013-04-06 16:01:34 +00:00
" edid/1600x1200.bin " ,
2012-03-18 22:37:33 +01:00
" edid/1680x1050.bin " ,
" edid/1920x1080.bin " ,
} ;
2013-10-02 11:12:53 +01:00
static const u8 generic_edid [ GENERIC_EDIDS ] [ 128 ] = {
2014-05-23 16:01:43 +01:00
{
0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 ,
0x31 , 0xd8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x05 , 0x16 , 0x01 , 0x03 , 0x6d , 0x1b , 0x14 , 0x78 ,
0xea , 0x5e , 0xc0 , 0xa4 , 0x59 , 0x4a , 0x98 , 0x25 ,
0x20 , 0x50 , 0x54 , 0x01 , 0x00 , 0x00 , 0x45 , 0x40 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0xa0 , 0x0f ,
0x20 , 0x00 , 0x31 , 0x58 , 0x1c , 0x20 , 0x28 , 0x80 ,
0x14 , 0x00 , 0x15 , 0xd0 , 0x10 , 0x00 , 0x00 , 0x1e ,
0x00 , 0x00 , 0x00 , 0xff , 0x00 , 0x4c , 0x69 , 0x6e ,
0x75 , 0x78 , 0x20 , 0x23 , 0x30 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfd , 0x00 , 0x3b ,
0x3d , 0x24 , 0x26 , 0x05 , 0x00 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfc ,
0x00 , 0x4c , 0x69 , 0x6e , 0x75 , 0x78 , 0x20 , 0x53 ,
0x56 , 0x47 , 0x41 , 0x0a , 0x20 , 0x20 , 0x00 , 0xc2 ,
} ,
2012-03-18 22:37:33 +01:00
{
0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 ,
0x31 , 0xd8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x05 , 0x16 , 0x01 , 0x03 , 0x6d , 0x23 , 0x1a , 0x78 ,
0xea , 0x5e , 0xc0 , 0xa4 , 0x59 , 0x4a , 0x98 , 0x25 ,
0x20 , 0x50 , 0x54 , 0x00 , 0x08 , 0x00 , 0x61 , 0x40 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x64 , 0x19 ,
0x00 , 0x40 , 0x41 , 0x00 , 0x26 , 0x30 , 0x08 , 0x90 ,
0x36 , 0x00 , 0x63 , 0x0a , 0x11 , 0x00 , 0x00 , 0x18 ,
0x00 , 0x00 , 0x00 , 0xff , 0x00 , 0x4c , 0x69 , 0x6e ,
0x75 , 0x78 , 0x20 , 0x23 , 0x30 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfd , 0x00 , 0x3b ,
0x3d , 0x2f , 0x31 , 0x07 , 0x00 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfc ,
0x00 , 0x4c , 0x69 , 0x6e , 0x75 , 0x78 , 0x20 , 0x58 ,
0x47 , 0x41 , 0x0a , 0x20 , 0x20 , 0x20 , 0x00 , 0x55 ,
} ,
{
0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 ,
0x31 , 0xd8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x05 , 0x16 , 0x01 , 0x03 , 0x6d , 0x2c , 0x23 , 0x78 ,
0xea , 0x5e , 0xc0 , 0xa4 , 0x59 , 0x4a , 0x98 , 0x25 ,
0x20 , 0x50 , 0x54 , 0x00 , 0x00 , 0x00 , 0x81 , 0x80 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x30 , 0x2a ,
0x00 , 0x98 , 0x51 , 0x00 , 0x2a , 0x40 , 0x30 , 0x70 ,
0x13 , 0x00 , 0xbc , 0x63 , 0x11 , 0x00 , 0x00 , 0x1e ,
0x00 , 0x00 , 0x00 , 0xff , 0x00 , 0x4c , 0x69 , 0x6e ,
0x75 , 0x78 , 0x20 , 0x23 , 0x30 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfd , 0x00 , 0x3b ,
0x3d , 0x3e , 0x40 , 0x0b , 0x00 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfc ,
0x00 , 0x4c , 0x69 , 0x6e , 0x75 , 0x78 , 0x20 , 0x53 ,
0x58 , 0x47 , 0x41 , 0x0a , 0x20 , 0x20 , 0x00 , 0xa0 ,
} ,
{
0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 ,
0x31 , 0xd8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
2013-04-06 16:01:34 +00:00
0x05 , 0x16 , 0x01 , 0x03 , 0x6d , 0x37 , 0x29 , 0x78 ,
0xea , 0x5e , 0xc0 , 0xa4 , 0x59 , 0x4a , 0x98 , 0x25 ,
0x20 , 0x50 , 0x54 , 0x00 , 0x00 , 0x00 , 0xa9 , 0x40 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x48 , 0x3f ,
0x40 , 0x30 , 0x62 , 0xb0 , 0x32 , 0x40 , 0x40 , 0xc0 ,
0x13 , 0x00 , 0x2b , 0xa0 , 0x21 , 0x00 , 0x00 , 0x1e ,
0x00 , 0x00 , 0x00 , 0xff , 0x00 , 0x4c , 0x69 , 0x6e ,
0x75 , 0x78 , 0x20 , 0x23 , 0x30 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfd , 0x00 , 0x3b ,
0x3d , 0x4a , 0x4c , 0x11 , 0x00 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfc ,
0x00 , 0x4c , 0x69 , 0x6e , 0x75 , 0x78 , 0x20 , 0x55 ,
0x58 , 0x47 , 0x41 , 0x0a , 0x20 , 0x20 , 0x00 , 0x9d ,
} ,
{
0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 ,
0x31 , 0xd8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
2012-03-18 22:37:33 +01:00
0x05 , 0x16 , 0x01 , 0x03 , 0x6d , 0x2b , 0x1b , 0x78 ,
0xea , 0x5e , 0xc0 , 0xa4 , 0x59 , 0x4a , 0x98 , 0x25 ,
0x20 , 0x50 , 0x54 , 0x00 , 0x00 , 0x00 , 0xb3 , 0x00 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x21 , 0x39 ,
0x90 , 0x30 , 0x62 , 0x1a , 0x27 , 0x40 , 0x68 , 0xb0 ,
0x36 , 0x00 , 0xb5 , 0x11 , 0x11 , 0x00 , 0x00 , 0x1e ,
0x00 , 0x00 , 0x00 , 0xff , 0x00 , 0x4c , 0x69 , 0x6e ,
0x75 , 0x78 , 0x20 , 0x23 , 0x30 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfd , 0x00 , 0x3b ,
0x3d , 0x40 , 0x42 , 0x0f , 0x00 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfc ,
0x00 , 0x4c , 0x69 , 0x6e , 0x75 , 0x78 , 0x20 , 0x57 ,
0x53 , 0x58 , 0x47 , 0x41 , 0x0a , 0x20 , 0x00 , 0x26 ,
} ,
{
0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 ,
0x31 , 0xd8 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x05 , 0x16 , 0x01 , 0x03 , 0x6d , 0x32 , 0x1c , 0x78 ,
0xea , 0x5e , 0xc0 , 0xa4 , 0x59 , 0x4a , 0x98 , 0x25 ,
0x20 , 0x50 , 0x54 , 0x00 , 0x00 , 0x00 , 0xd1 , 0xc0 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 ,
0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x01 , 0x02 , 0x3a ,
0x80 , 0x18 , 0x71 , 0x38 , 0x2d , 0x40 , 0x58 , 0x2c ,
0x45 , 0x00 , 0xf4 , 0x19 , 0x11 , 0x00 , 0x00 , 0x1e ,
0x00 , 0x00 , 0x00 , 0xff , 0x00 , 0x4c , 0x69 , 0x6e ,
0x75 , 0x78 , 0x20 , 0x23 , 0x30 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfd , 0x00 , 0x3b ,
0x3d , 0x42 , 0x44 , 0x0f , 0x00 , 0x0a , 0x20 , 0x20 ,
0x20 , 0x20 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0xfc ,
0x00 , 0x4c , 0x69 , 0x6e , 0x75 , 0x78 , 0x20 , 0x46 ,
0x48 , 0x44 , 0x0a , 0x20 , 0x20 , 0x20 , 0x00 , 0x05 ,
} ,
} ;
2022-10-24 15:33:40 +03:00
static const struct drm_edid * edid_load ( struct drm_connector * connector , const char * name )
2012-03-18 22:37:33 +01:00
{
2013-10-02 11:12:53 +01:00
const struct firmware * fw = NULL ;
const u8 * fwdata ;
2022-10-24 15:33:40 +03:00
const struct drm_edid * drm_edid ;
2013-10-02 11:12:53 +01:00
int fwsize , builtin ;
2012-03-18 22:37:33 +01:00
2016-03-17 14:22:23 -07:00
builtin = match_string ( generic_edid_name , GENERIC_EDIDS , name ) ;
if ( builtin > = 0 ) {
fwdata = generic_edid [ builtin ] ;
fwsize = sizeof ( generic_edid [ builtin ] ) ;
} else {
2013-10-02 11:12:53 +01:00
int err ;
2012-03-18 22:37:33 +01:00
2022-11-14 13:17:09 +02:00
err = request_firmware ( & fw , name , connector - > dev - > dev ) ;
2013-10-02 11:12:53 +01:00
if ( err ) {
2022-10-24 15:33:42 +03:00
drm_err ( connector - > dev ,
" [CONNECTOR:%d:%s] Requesting EDID firmware \" %s \" failed (err=%d) \n " ,
connector - > base . id , connector - > name ,
name , err ) ;
2013-10-02 11:12:53 +01:00
return ERR_PTR ( err ) ;
}
2012-03-18 22:37:33 +01:00
2013-10-02 11:12:53 +01:00
fwdata = fw - > data ;
2012-03-18 22:37:33 +01:00
fwsize = fw - > size ;
}
2022-10-24 15:33:40 +03:00
drm_dbg_kms ( connector - > dev , " [CONNECTOR:%d:%s] Loaded %s firmware EDID \" %s \" \n " ,
connector - > base . id , connector - > name ,
builtin > = 0 ? " built-in " : " external " , name ) ;
2013-10-02 11:12:53 +01:00
2022-10-24 15:33:40 +03:00
drm_edid = drm_edid_alloc ( fwdata , fwsize ) ;
if ( ! drm_edid_valid ( drm_edid ) ) {
drm_err ( connector - > dev , " Invalid firmware EDID \" %s \" \n " , name ) ;
drm_edid_free ( drm_edid ) ;
drm_edid = ERR_PTR ( - EINVAL ) ;
2012-03-18 22:37:33 +01:00
}
2014-11-19 16:33:17 +01:00
release_firmware ( fw ) ;
2022-10-24 15:33:40 +03:00
return drm_edid ;
2012-03-18 22:37:33 +01:00
}
2022-10-24 15:33:40 +03:00
const struct drm_edid * drm_edid_load_firmware ( struct drm_connector * connector )
2012-03-18 22:37:33 +01:00
{
2015-08-27 10:04:13 -07:00
char * edidname , * last , * colon , * fwstr , * edidstr , * fallback = NULL ;
2022-10-24 15:33:40 +03:00
const struct drm_edid * drm_edid ;
2012-03-18 22:37:33 +01:00
2015-08-27 10:04:13 -07:00
if ( edid_firmware [ 0 ] = = ' \0 ' )
2017-02-17 17:20:51 +02:00
return ERR_PTR ( - ENOENT ) ;
2012-03-18 22:37:33 +01:00
2015-08-27 10:04:13 -07:00
/*
* If there are multiple edid files specified and separated
* by commas , search through the list looking for one that
* matches the connector .
*
2016-05-30 02:26:38 -04:00
* If there ' s one or more that doesn ' t specify a connector , keep
2015-08-27 10:04:13 -07:00
* the last one found one as a fallback .
*/
fwstr = kstrdup ( edid_firmware , GFP_KERNEL ) ;
2019-05-24 10:32:22 +08:00
if ( ! fwstr )
return ERR_PTR ( - ENOMEM ) ;
2015-08-27 10:04:13 -07:00
edidstr = fwstr ;
while ( ( edidname = strsep ( & edidstr , " , " ) ) ) {
colon = strchr ( edidname , ' : ' ) ;
if ( colon ! = NULL ) {
2022-10-24 15:33:38 +03:00
if ( strncmp ( connector - > name , edidname , colon - edidname ) )
2015-08-27 10:04:13 -07:00
continue ;
edidname = colon + 1 ;
break ;
}
if ( * edidname ! = ' \0 ' ) /* corner case: multiple ',' */
fallback = edidname ;
}
if ( ! edidname ) {
if ( ! fallback ) {
kfree ( fwstr ) ;
2017-02-17 17:20:51 +02:00
return ERR_PTR ( - ENOENT ) ;
2015-08-27 10:04:13 -07:00
}
edidname = fallback ;
2012-03-18 22:37:33 +01:00
}
last = edidname + strlen ( edidname ) - 1 ;
if ( * last = = ' \n ' )
* last = ' \0 ' ;
2022-10-24 15:33:40 +03:00
drm_edid = edid_load ( connector , edidname ) ;
2015-08-27 10:04:13 -07:00
kfree ( fwstr ) ;
2022-10-24 15:33:40 +03:00
return drm_edid ;
2012-03-18 22:37:33 +01:00
}