2019-06-01 11:08:42 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-01-21 01:08:59 +03:00
2005-04-17 02:20:36 +04:00
/*
* drm_sysfs . c - Modifications to drm_sysfs_class . c to support
* extra sysfs attribute from DRM . Normal drm_sysfs_class
* does not allow adding attributes .
*
* Copyright ( c ) 2004 Jon Smirl < jonsmirl @ gmail . com >
* Copyright ( c ) 2003 - 2004 Greg Kroah - Hartman < greg @ kroah . com >
* Copyright ( c ) 2003 - 2004 IBM Corp .
*/
# include <linux/device.h>
# include <linux/err.h>
2011-08-31 02:16:33 +04:00
# include <linux/export.h>
2019-05-26 20:35:35 +03:00
# include <linux/gfp.h>
2019-07-26 20:22:55 +03:00
# include <linux/i2c.h>
2019-05-26 20:35:35 +03:00
# include <linux/kdev_t.h>
# include <linux/slab.h>
# include <drm/drm_connector.h>
# include <drm/drm_device.h>
# include <drm/drm_file.h>
# include <drm/drm_modes.h>
# include <drm/drm_print.h>
# include <drm/drm_property.h>
2012-10-02 21:01:07 +04:00
# include <drm/drm_sysfs.h>
2019-05-26 20:35:35 +03:00
2014-09-10 14:43:53 +04:00
# include "drm_internal.h"
2019-08-01 14:41:16 +03:00
# include "drm_crtc_internal.h"
2005-04-17 02:20:36 +04:00
2013-10-11 08:07:25 +04:00
# define to_drm_minor(d) dev_get_drvdata(d)
# define to_drm_connector(d) dev_get_drvdata(d)
2007-11-22 07:02:38 +03:00
2017-04-04 12:52:55 +03:00
/**
* DOC : overview
*
* DRM provides very little additional support to drivers for sysfs
* interactions , beyond just all the standard stuff . Drivers who want to expose
* additional sysfs properties and property groups can attach them at either
* & drm_device . dev or & drm_connector . kdev .
*
* Registration is automatically handled when calling drm_dev_register ( ) , or
* drm_connector_register ( ) in case of hot - plugged connectors . Unregistration is
* also automatically handled by drm_dev_unregister ( ) and
* drm_connector_unregister ( ) .
*/
2009-08-20 13:02:31 +04:00
static struct device_type drm_sysfs_device_minor = {
. name = " drm_minor "
} ;
2015-09-09 15:21:30 +03:00
struct class * drm_class ;
2011-07-24 04:24:48 +04:00
static char * drm_devnode ( struct device * dev , umode_t * mode )
2009-04-30 17:23:42 +04:00
{
return kasprintf ( GFP_KERNEL , " dri/%s " , dev_name ( dev ) ) ;
}
drm: drop obsolete drm_core.h
The drm_core.h header contains a set of constants meant to be used
throughout DRM. However, as it turns out, they're each used just once and
don't bring any benefit. They're also grossly mis-named and lack
name-spacing. This patch inlines them, or moves them into drm_internal.h
as appropriate:
- CORE_AUTHOR and CORE_DESC are inlined into corresponding MODULE_*()
macros. It's just confusing having to follow 2 pointers when trying to
find the definition of these fields. Grep'ping for MODULE_AUTHOR()
should reveal the full information, if there's no strong reason not to.
- CORE_NAME, CORE_DATE, CORE_MAJOR, CORE_MINOR, and CORE_PATCHLEVEL are
inlined into the sysfs 'version' attribute. They're stripped
everywhere else (which is just some printk() statements). CORE_NAME
just doesn't make *any* sense, as we hard-code it in many places,
anyway. The other constants are outdated and just serve
binary-compatibility purposes. Hence, inline them in 'version' sysfs
attribute (we might even try dropping it..).
- DRM_IF_MAJOR and DRM_IF_MINOR are moved into drm_internal.h as they're
only used by the global ioctl handlers. Furthermore, versioning
interfaces breaks backports and as such is deprecated, anyway. We just
keep them for historic reasons. I doubt anyone will ever modify them
again.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20160901124837.680-6-dh.herrmann@gmail.com
2016-09-01 15:48:36 +03:00
static CLASS_ATTR_STRING ( version , S_IRUGO , " drm 1.1.0 20060810 " ) ;
2005-04-17 02:20:36 +04:00
/**
2015-09-09 15:21:30 +03:00
* drm_sysfs_init - initialize sysfs helpers
*
* This is used to create the DRM class , which is the implicit parent of any
* other top - level DRM sysfs objects .
2005-04-17 02:20:36 +04:00
*
2015-09-09 15:21:30 +03:00
* You must call drm_sysfs_destroy ( ) to release the allocated resources .
2005-04-17 02:20:36 +04:00
*
2015-09-09 15:21:30 +03:00
* Return : 0 on success , negative error code on failure .
2005-04-17 02:20:36 +04:00
*/
2015-09-09 15:21:30 +03:00
int drm_sysfs_init ( void )
2005-04-17 02:20:36 +04:00
{
2006-10-11 01:23:37 +04:00
int err ;
2006-01-21 01:08:59 +03:00
2015-09-09 15:21:30 +03:00
drm_class = class_create ( THIS_MODULE , " drm " ) ;
if ( IS_ERR ( drm_class ) )
return PTR_ERR ( drm_class ) ;
2007-11-22 07:02:38 +03:00
2015-09-09 15:21:30 +03:00
err = class_create_file ( drm_class , & class_attr_version . attr ) ;
if ( err ) {
class_destroy ( drm_class ) ;
drm_class = NULL ;
return err ;
}
2006-10-11 01:23:37 +04:00
2015-09-09 15:21:30 +03:00
drm_class - > devnode = drm_devnode ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/**
2007-11-22 07:02:38 +03:00
* drm_sysfs_destroy - destroys DRM class
2005-04-17 02:20:36 +04:00
*
2007-11-22 07:02:38 +03:00
* Destroy the DRM device class .
2005-04-17 02:20:36 +04:00
*/
2007-11-22 07:02:38 +03:00
void drm_sysfs_destroy ( void )
2005-04-17 02:20:36 +04:00
{
2015-09-09 15:21:29 +03:00
if ( IS_ERR_OR_NULL ( drm_class ) )
2005-04-17 02:20:36 +04:00
return ;
2010-01-05 14:48:09 +03:00
class_remove_file ( drm_class , & class_attr_version . attr ) ;
2007-11-22 07:02:38 +03:00
class_destroy ( drm_class ) ;
2012-07-06 21:06:42 +04:00
drm_class = NULL ;
2005-04-17 02:20:36 +04:00
}
2008-11-08 01:05:41 +03:00
/*
* Connector properties
*/
2015-03-06 15:36:42 +03:00
static ssize_t status_store ( struct device * device ,
2008-11-08 01:05:41 +03:00
struct device_attribute * attr ,
2015-03-06 15:36:42 +03:00
const char * buf , size_t count )
2008-11-08 01:05:41 +03:00
{
struct drm_connector * connector = to_drm_connector ( device ) ;
2015-03-06 15:36:42 +03:00
struct drm_device * dev = connector - > dev ;
2015-11-19 19:46:50 +03:00
enum drm_connector_force old_force ;
2011-03-15 14:40:00 +03:00
int ret ;
2015-03-06 15:36:42 +03:00
ret = mutex_lock_interruptible ( & dev - > mode_config . mutex ) ;
2011-03-15 14:40:00 +03:00
if ( ret )
return ret ;
2008-11-08 01:05:41 +03:00
2015-11-19 19:46:50 +03:00
old_force = connector - > force ;
2015-03-06 15:36:42 +03:00
2015-11-19 19:46:50 +03:00
if ( sysfs_streq ( buf , " detect " ) )
2015-03-06 15:36:42 +03:00
connector - > force = 0 ;
2015-11-19 19:46:50 +03:00
else if ( sysfs_streq ( buf , " on " ) )
2015-03-06 15:36:42 +03:00
connector - > force = DRM_FORCE_ON ;
2015-11-19 19:46:50 +03:00
else if ( sysfs_streq ( buf , " on-digital " ) )
2015-03-06 15:36:42 +03:00
connector - > force = DRM_FORCE_ON_DIGITAL ;
2015-11-19 19:46:50 +03:00
else if ( sysfs_streq ( buf , " off " ) )
2015-03-06 15:36:42 +03:00
connector - > force = DRM_FORCE_OFF ;
2015-11-19 19:46:50 +03:00
else
2015-03-06 15:36:42 +03:00
ret = - EINVAL ;
2015-11-19 19:46:50 +03:00
if ( old_force ! = connector - > force | | ! connector - > force ) {
DRM_DEBUG_KMS ( " [CONNECTOR:%d:%s] force updated from %d to %d or reprobing \n " ,
2015-03-06 15:36:42 +03:00
connector - > base . id ,
connector - > name ,
2015-11-19 19:46:50 +03:00
old_force , connector - > force ) ;
2015-03-06 15:36:42 +03:00
2015-11-19 19:46:50 +03:00
connector - > funcs - > fill_modes ( connector ,
dev - > mode_config . max_width ,
dev - > mode_config . max_height ) ;
2015-03-06 15:36:42 +03:00
}
mutex_unlock ( & dev - > mode_config . mutex ) ;
2015-06-06 01:27:30 +03:00
return ret ? ret : count ;
2015-03-06 15:36:42 +03:00
}
static ssize_t status_show ( struct device * device ,
struct device_attribute * attr ,
char * buf )
{
struct drm_connector * connector = to_drm_connector ( device ) ;
2016-03-30 12:45:13 +03:00
enum drm_connector_status status ;
status = READ_ONCE ( connector - > status ) ;
2011-03-15 14:40:00 +03:00
2009-05-31 07:42:25 +04:00
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
2016-03-30 12:45:13 +03:00
drm_get_connector_status_name ( status ) ) ;
2008-11-08 01:05:41 +03:00
}
static ssize_t dpms_show ( struct device * device ,
struct device_attribute * attr ,
char * buf )
{
struct drm_connector * connector = to_drm_connector ( device ) ;
2015-09-29 10:56:53 +03:00
int dpms ;
2008-11-08 01:05:41 +03:00
2015-09-29 10:56:53 +03:00
dpms = READ_ONCE ( connector - > dpms ) ;
2008-11-08 01:05:41 +03:00
2009-05-31 07:42:25 +04:00
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
2015-09-29 10:56:53 +03:00
drm_get_dpms_name ( dpms ) ) ;
2008-11-08 01:05:41 +03:00
}
static ssize_t enabled_show ( struct device * device ,
struct device_attribute * attr ,
char * buf )
{
struct drm_connector * connector = to_drm_connector ( device ) ;
2016-03-30 12:45:13 +03:00
bool enabled ;
enabled = READ_ONCE ( connector - > encoder ) ;
2008-11-08 01:05:41 +03:00
2016-03-30 12:45:13 +03:00
return snprintf ( buf , PAGE_SIZE , enabled ? " enabled \n " : " disabled \n " ) ;
2008-11-08 01:05:41 +03:00
}
2010-05-13 05:28:57 +04:00
static ssize_t edid_show ( struct file * filp , struct kobject * kobj ,
struct bin_attribute * attr , char * buf , loff_t off ,
size_t count )
2008-11-08 01:05:41 +03:00
{
2016-01-13 17:48:41 +03:00
struct device * connector_dev = kobj_to_dev ( kobj ) ;
2008-11-08 01:05:41 +03:00
struct drm_connector * connector = to_drm_connector ( connector_dev ) ;
unsigned char * edid ;
size_t size ;
2015-10-02 14:01:02 +03:00
ssize_t ret = 0 ;
2008-11-08 01:05:41 +03:00
2015-10-02 14:01:02 +03:00
mutex_lock ( & connector - > dev - > mode_config . mutex ) ;
2008-11-08 01:05:41 +03:00
if ( ! connector - > edid_blob_ptr )
2015-10-02 14:01:02 +03:00
goto unlock ;
2008-11-08 01:05:41 +03:00
edid = connector - > edid_blob_ptr - > data ;
size = connector - > edid_blob_ptr - > length ;
if ( ! edid )
2015-10-02 14:01:02 +03:00
goto unlock ;
2008-11-08 01:05:41 +03:00
if ( off > = size )
2015-10-02 14:01:02 +03:00
goto unlock ;
2008-11-08 01:05:41 +03:00
if ( off + count > size )
count = size - off ;
memcpy ( buf , edid + off , count ) ;
2015-10-02 14:01:02 +03:00
ret = count ;
unlock :
mutex_unlock ( & connector - > dev - > mode_config . mutex ) ;
return ret ;
2008-11-08 01:05:41 +03:00
}
static ssize_t modes_show ( struct device * device ,
struct device_attribute * attr ,
char * buf )
{
struct drm_connector * connector = to_drm_connector ( device ) ;
struct drm_display_mode * mode ;
int written = 0 ;
2015-10-02 14:01:02 +03:00
mutex_lock ( & connector - > dev - > mode_config . mutex ) ;
2008-11-08 01:05:41 +03:00
list_for_each_entry ( mode , & connector - > modes , head ) {
2020-03-11 10:35:40 +03:00
written + = scnprintf ( buf + written , PAGE_SIZE - written , " %s \n " ,
2008-11-08 01:05:41 +03:00
mode - > name ) ;
}
2015-10-02 14:01:02 +03:00
mutex_unlock ( & connector - > dev - > mode_config . mutex ) ;
2008-11-08 01:05:41 +03:00
return written ;
}
2015-03-06 15:36:42 +03:00
static DEVICE_ATTR_RW ( status ) ;
2015-02-04 13:58:53 +03:00
static DEVICE_ATTR_RO ( enabled ) ;
static DEVICE_ATTR_RO ( dpms ) ;
static DEVICE_ATTR_RO ( modes ) ;
static struct attribute * connector_dev_attrs [ ] = {
& dev_attr_status . attr ,
& dev_attr_enabled . attr ,
& dev_attr_dpms . attr ,
& dev_attr_modes . attr ,
NULL
2008-11-08 01:05:41 +03:00
} ;
static struct bin_attribute edid_attr = {
. attr . name = " edid " ,
2009-05-31 07:42:26 +04:00
. attr . mode = 0444 ,
2010-03-30 01:43:23 +04:00
. size = 0 ,
2008-11-08 01:05:41 +03:00
. read = edid_show ,
} ;
2015-02-04 13:58:53 +03:00
static struct bin_attribute * connector_bin_attrs [ ] = {
& edid_attr ,
NULL
} ;
static const struct attribute_group connector_dev_group = {
. attrs = connector_dev_attrs ,
. bin_attrs = connector_bin_attrs ,
} ;
static const struct attribute_group * connector_dev_groups [ ] = {
& connector_dev_group ,
NULL
} ;
2008-11-08 01:05:41 +03:00
int drm_sysfs_connector_add ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
2013-10-11 08:07:25 +04:00
if ( connector - > kdev )
return 0 ;
2008-11-08 01:05:41 +03:00
2015-02-04 13:58:53 +03:00
connector - > kdev =
device_create_with_groups ( drm_class , dev - > primary - > kdev , 0 ,
connector , connector_dev_groups ,
" card%d-%s " , dev - > primary - > index ,
connector - > name ) ;
2008-11-08 01:05:41 +03:00
DRM_DEBUG ( " adding \" %s \" to sysfs \n " ,
2014-06-03 15:56:20 +04:00
connector - > name ) ;
2008-11-08 01:05:41 +03:00
2013-10-11 08:07:25 +04:00
if ( IS_ERR ( connector - > kdev ) ) {
DRM_ERROR ( " failed to register connector device: %ld \n " , PTR_ERR ( connector - > kdev ) ) ;
2015-02-04 13:58:53 +03:00
return PTR_ERR ( connector - > kdev ) ;
2008-11-08 01:05:41 +03:00
}
2019-07-26 20:22:55 +03:00
if ( connector - > ddc )
return sysfs_create_link ( & connector - > kdev - > kobj ,
& connector - > ddc - > dev . kobj , " ddc " ) ;
2008-11-08 01:05:41 +03:00
return 0 ;
}
void drm_sysfs_connector_remove ( struct drm_connector * connector )
{
2013-10-11 08:07:25 +04:00
if ( ! connector - > kdev )
2012-02-20 18:15:02 +04:00
return ;
2019-07-26 20:22:55 +03:00
if ( connector - > ddc )
sysfs_remove_link ( & connector - > kdev - > kobj , " ddc " ) ;
2008-11-08 01:05:41 +03:00
DRM_DEBUG ( " removing \" %s \" from sysfs \n " ,
2014-06-03 15:56:20 +04:00
connector - > name ) ;
2008-11-08 01:05:41 +03:00
2013-10-11 08:07:25 +04:00
device_unregister ( connector - > kdev ) ;
connector - > kdev = NULL ;
2008-11-08 01:05:41 +03:00
}
2018-11-29 12:42:26 +03:00
void drm_sysfs_lease_event ( struct drm_device * dev )
{
char * event_string = " LEASE=1 " ;
char * envp [ ] = { event_string , NULL } ;
DRM_DEBUG ( " generating lease event \n " ) ;
kobject_uevent_env ( & dev - > primary - > kdev - > kobj , KOBJ_CHANGE , envp ) ;
}
2008-11-08 01:05:41 +03:00
/**
* drm_sysfs_hotplug_event - generate a DRM uevent
* @ dev : DRM device
*
* Send a uevent for the DRM device specified by @ dev . Currently we only
* set HOTPLUG = 1 in the uevent environment , but this could be expanded to
* deal with other types of events .
2019-08-01 14:41:16 +03:00
*
* Any new uapi should be using the drm_sysfs_connector_status_event ( )
* for uevents on connector status change .
2008-11-08 01:05:41 +03:00
*/
void drm_sysfs_hotplug_event ( struct drm_device * dev )
{
char * event_string = " HOTPLUG=1 " ;
char * envp [ ] = { event_string , NULL } ;
DRM_DEBUG ( " generating hotplug event \n " ) ;
2013-10-11 08:07:25 +04:00
kobject_uevent_env ( & dev - > primary - > kdev - > kobj , KOBJ_CHANGE , envp ) ;
2008-11-08 01:05:41 +03:00
}
2009-04-01 01:11:15 +04:00
EXPORT_SYMBOL ( drm_sysfs_hotplug_event ) ;
2008-11-08 01:05:41 +03:00
2019-08-01 14:41:16 +03:00
/**
* drm_sysfs_connector_status_event - generate a DRM uevent for connector
* property status change
* @ connector : connector on which property status changed
* @ property : connector property whose status changed .
*
* Send a uevent for the DRM device specified by @ dev . Currently we
* set HOTPLUG = 1 and connector id along with the attached property id
* related to the status change .
*/
void drm_sysfs_connector_status_event ( struct drm_connector * connector ,
struct drm_property * property )
{
struct drm_device * dev = connector - > dev ;
char hotplug_str [ ] = " HOTPLUG=1 " , conn_id [ 21 ] , prop_id [ 21 ] ;
char * envp [ 4 ] = { hotplug_str , conn_id , prop_id , NULL } ;
WARN_ON ( ! drm_mode_obj_find_prop_id ( & connector - > base ,
property - > base . id ) ) ;
snprintf ( conn_id , ARRAY_SIZE ( conn_id ) ,
" CONNECTOR=%u " , connector - > base . id ) ;
snprintf ( prop_id , ARRAY_SIZE ( prop_id ) ,
" PROPERTY=%u " , property - > base . id ) ;
DRM_DEBUG ( " generating connector status event \n " ) ;
kobject_uevent_env ( & dev - > primary - > kdev - > kobj , KOBJ_CHANGE , envp ) ;
}
EXPORT_SYMBOL ( drm_sysfs_connector_status_event ) ;
2013-11-21 14:50:50 +04:00
static void drm_sysfs_release ( struct device * dev )
{
kfree ( dev ) ;
}
2014-07-23 13:38:38 +04:00
struct device * drm_sysfs_minor_alloc ( struct drm_minor * minor )
2005-04-17 02:20:36 +04:00
{
2014-07-23 13:38:38 +04:00
const char * minor_str ;
struct device * kdev ;
2013-11-21 14:50:50 +04:00
int r ;
2007-11-22 07:02:38 +03:00
2018-04-20 09:51:59 +03:00
if ( minor - > type = = DRM_MINOR_RENDER )
2014-07-23 13:38:38 +04:00
minor_str = " renderD%d " ;
else
minor_str = " card%d " ;
kdev = kzalloc ( sizeof ( * kdev ) , GFP_KERNEL ) ;
if ( ! kdev )
return ERR_PTR ( - ENOMEM ) ;
device_initialize ( kdev ) ;
kdev - > devt = MKDEV ( DRM_MAJOR , minor - > index ) ;
kdev - > class = drm_class ;
kdev - > type = & drm_sysfs_device_minor ;
kdev - > parent = minor - > dev - > dev ;
kdev - > release = drm_sysfs_release ;
dev_set_drvdata ( kdev , minor ) ;
r = dev_set_name ( kdev , minor_str , minor - > index ) ;
2013-11-21 14:50:50 +04:00
if ( r < 0 )
2014-07-23 13:38:38 +04:00
goto err_free ;
2013-11-21 14:50:50 +04:00
2014-07-23 13:38:38 +04:00
return kdev ;
2005-04-17 02:20:36 +04:00
2014-07-23 13:38:38 +04:00
err_free :
put_device ( kdev ) ;
return ERR_PTR ( r ) ;
2005-04-17 02:20:36 +04:00
}
2009-08-17 18:28:37 +04:00
/**
2017-04-04 12:52:55 +03:00
* drm_class_device_register - register new device with the DRM sysfs class
* @ dev : device to register
2009-08-17 18:28:37 +04:00
*
2017-04-04 12:52:55 +03:00
* Registers a new & struct device within the DRM sysfs class . Essentially only
* used by ttm to have a place for its global settings . Drivers should never use
* this .
2009-08-17 18:28:37 +04:00
*/
int drm_class_device_register ( struct device * dev )
{
2012-07-06 21:06:42 +04:00
if ( ! drm_class | | IS_ERR ( drm_class ) )
return - ENOENT ;
2009-08-17 18:28:37 +04:00
dev - > class = drm_class ;
return device_register ( dev ) ;
}
EXPORT_SYMBOL_GPL ( drm_class_device_register ) ;
2017-04-04 12:52:55 +03:00
/**
* drm_class_device_unregister - unregister device with the DRM sysfs class
* @ dev : device to unregister
*
* Unregisters a & struct device from the DRM sysfs class . Essentially only used
* by ttm to have a place for its global settings . Drivers should never use
* this .
*/
2009-08-17 18:28:37 +04:00
void drm_class_device_unregister ( struct device * dev )
{
return device_unregister ( dev ) ;
}
EXPORT_SYMBOL_GPL ( drm_class_device_unregister ) ;