From 7d52cb88c9ca20c32288de6ed62b32410c936ccc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:43 +0100 Subject: drm: Remove drm_pending_event->pid We might as well dump the drm_file pointer, that's about as useful a cookie as the pid. Noticed while typing docs for drm_file and friends. Since the only consumer of this is the tracepoints I think we can safely change this - those tracepoints should not be uapi relevant at all. It all goes back to commit b9c2c9ae882f058084e13e339925dbf8d2d20271 Author: Jesse Barnes Date: Thu Jul 1 16:48:09 2010 -0700 drm: add per-event vblank event trace points which doesn't give a special justification for using pid over a pointer. Also note that the nouveau code setting it is entirely pointless: Since this isn't a vblank event, it will never hit the vblank tracepoints. Cc: Ben Skeggs Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-11-daniel.vetter@ffwll.ch --- include/drm/drm_file.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index d1a25cc17fd1..4e347399a7bd 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -75,8 +75,6 @@ struct drm_pending_event { struct list_head link; struct list_head pending_link; struct drm_file *file_priv; - pid_t pid; /* pid of requester, no guarantee it's valid by the time - we deliver the event, for tracing only */ }; /** File private data */ -- cgit v1.2.3 From b93658f83fa7f89cde5cfba7158da2be8cac42b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:44 +0100 Subject: drm/doc: Document drm_file.[hc] Well, mostly drm_file.h, and clean up all related things: - I didnt' figure out the difference between preclose and postclose. The existing explanation in drm-internals.rst didn't convince me, since it's also really outdated - we clean up pending DRM events in the core nowadays. I put a FIXME in for the future. - Another FIXME is to have a macro for default fops. - Lots of links all around, main areas are to tie the overview in drm_file.c more into the callbacks in struct drm_device, and the other is to link render/primary node code to the right sections in drm-uapi.rst. - Also moved the open/close stuff to drm_drv.h from drm-internals.rst, seems like the better place for that information. Since that section was rather outdated this amounted to full-on rewrite. A big missing piece here is some overview graph, but I think better to wait with that one until drm_device and drm_driver are also fully documented. v2: Nits from Sean. Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-12-daniel.vetter@ffwll.ch --- Documentation/gpu/drm-internals.rst | 52 +------ Documentation/gpu/drm-uapi.rst | 4 + drivers/gpu/drm/drm_file.c | 66 +++++---- include/drm/drm_drv.h | 81 ++++++++++- include/drm/drm_file.h | 265 ++++++++++++++++++++++++++++++++---- 5 files changed, 354 insertions(+), 114 deletions(-) (limited to 'include') diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index 3bb4d937cdfe..cbf8e52cd9bb 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -243,61 +243,15 @@ drivers. Open/Close, File Operations and IOCTLs ====================================== -Open and Close --------------- - -Open and close handlers. None of those methods are mandatory:: - - int (*firstopen) (struct drm_device *); - void (*lastclose) (struct drm_device *); - int (*open) (struct drm_device *, struct drm_file *); - void (*preclose) (struct drm_device *, struct drm_file *); - void (*postclose) (struct drm_device *, struct drm_file *); - -The firstopen method is called by the DRM core for legacy UMS (User Mode -Setting) drivers only when an application opens a device that has no -other opened file handle. UMS drivers can implement it to acquire device -resources. KMS drivers can't use the method and must acquire resources -in the load method instead. - -Similarly the lastclose method is called when the last application -holding a file handle opened on the device closes it, for both UMS and -KMS drivers. Additionally, the method is also called at module unload -time or, for hot-pluggable devices, when the device is unplugged. The -firstopen and lastclose calls can thus be unbalanced. - -The open method is called every time the device is opened by an -application. Drivers can allocate per-file private data in this method -and store them in the struct :c:type:`struct drm_file -` driver_priv field. Note that the open method is -called before firstopen. - -The close operation is split into preclose and postclose methods. -Drivers must stop and cleanup all per-file operations in the preclose -method. For instance pending vertical blanking and page flip events must -be cancelled. No per-file operation is allowed on the file handle after -returning from the preclose method. - -Finally the postclose method is called as the last step of the close -operation, right before calling the lastclose method if no other open -file handle exists for the device. Drivers that have allocated per-file -private data in the open method should free it here. - -The lastclose method should restore CRTC and plane properties to default -value, so that a subsequent open of the device will not inherit state -from the previous user. It can also be used to execute delayed power -switching state changes, e.g. in conjunction with the :ref:`vga_switcheroo` -infrastructure. Beyond that KMS drivers should not do any -further cleanup. Only legacy UMS drivers might need to clean up device -state so that the vga console or an independent fbdev driver could take -over. - File Operations --------------- .. kernel-doc:: drivers/gpu/drm/drm_file.c :doc: file operations +.. kernel-doc:: include/drm/drm_file.h + :internal: + .. kernel-doc:: drivers/gpu/drm/drm_file.c :export: diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index fcc228ef5bc4..352652810dab 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -21,6 +21,8 @@ libdrm Device Lookup :doc: getunique and setversion story +.. _drm_primary_node: + Primary Nodes, DRM Master and Authentication ============================================ @@ -103,6 +105,8 @@ is already rather painful for the DRM subsystem, with multiple different uAPIs for the same thing co-existing. If we add a few more complete mistakes into the mix every year it would be entirely unmanageable. +.. _drm_render_node: + Render nodes ============ diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index d9e63d73d3ec..92848fe75ea6 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -50,12 +50,14 @@ DEFINE_MUTEX(drm_global_mutex); * * Drivers must define the file operations structure that forms the DRM * userspace API entry point, even though most of those operations are - * implemented in the DRM core. The mandatory functions are drm_open(), + * implemented in the DRM core. The resulting &struct file_operations must be + * stored in the &drm_driver.fops field. The mandatory functions are drm_open(), * drm_read(), drm_ioctl() and drm_compat_ioctl() if CONFIG_COMPAT is enabled - * (note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n). Drivers which - * implement private ioctls that require 32/64 bit compatibility support must - * provide their own .compat_ioctl() handler that processes private ioctls and - * calls drm_compat_ioctl() for core ioctls. + * Note that drm_compat_ioctl will be NULL if CONFIG_COMPAT=n, so there's no + * need to sprinkle #ifdef into the code. Drivers which implement private ioctls + * that require 32/64 bit compatibility support must provide their own + * &file_operations.compat_ioctl handler that processes private ioctls and calls + * drm_compat_ioctl() for core ioctls. * * In addition drm_read() and drm_poll() provide support for DRM events. DRM * events are a generic and extensible means to send asynchronous events to @@ -63,10 +65,14 @@ DEFINE_MUTEX(drm_global_mutex); * page flip completions by the KMS API. But drivers can also use it for their * own needs, e.g. to signal completion of rendering. * + * For the driver-side event interface see drm_event_reserve_init() and + * drm_send_event() as the main starting points. + * * The memory mapping implementation will vary depending on how the driver * manages memory. Legacy drivers will use the deprecated drm_legacy_mmap() * function, modern drivers should use one of the provided memory-manager - * specific implementations. For GEM-based drivers this is drm_gem_mmap(). + * specific implementations. For GEM-based drivers this is drm_gem_mmap(), and + * for drivers which use the CMA GEM helpers it's drm_gem_cma_mmap(). * * No other file operations are supported by the DRM userspace API. Overall the * following is an example #file_operations structure:: @@ -82,6 +88,9 @@ DEFINE_MUTEX(drm_global_mutex); * .llseek = no_llseek, * .mmap = drm_gem_mmap, * }; + * + * FIXME: We should have a macro for this (and the CMA version) so that drivers + * don't have to repeat it all the time. */ static int drm_open_helper(struct file *filp, struct drm_minor *minor); @@ -111,9 +120,9 @@ static int drm_setup(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .open() #file_operations - * method. It looks up the correct DRM device and instantiates all the per-file - * resources for it. + * This function must be used by drivers as their &file_operations.open method. + * It looks up the correct DRM device and instantiates all the per-file + * resources for it. It also calls the &drm_driver.open driver callback. * * RETURNS: * @@ -298,11 +307,6 @@ static void drm_events_release(struct drm_file *file_priv) spin_unlock_irqrestore(&dev->event_lock, flags); } -/* - * drm_legacy_dev_reinit - * - * Reinitializes a legacy/ums drm device in it's lastclose function. - */ static void drm_legacy_dev_reinit(struct drm_device *dev) { if (dev->irq_enabled) @@ -327,15 +331,6 @@ static void drm_legacy_dev_reinit(struct drm_device *dev) DRM_DEBUG("lastclose completed\n"); } -/* - * Take down the DRM device. - * - * \param dev DRM device structure. - * - * Frees every resource in \p dev. - * - * \sa drm_device - */ void drm_lastclose(struct drm_device * dev) { DRM_DEBUG("\n"); @@ -353,9 +348,11 @@ void drm_lastclose(struct drm_device * dev) * @inode: device inode * @filp: file pointer. * - * This function must be used by drivers as their .release() #file_operations - * method. It frees any resources associated with the open file, and if this is - * the last open file for the DRM device also proceeds to call drm_lastclose(). + * This function must be used by drivers as their &file_operations.release + * method. It frees any resources associated with the open file, and calls the + * &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is + * the last open file for the DRM device also proceeds to call the + * &drm_driver.lastclose driver callback. * * RETURNS: * @@ -443,13 +440,13 @@ EXPORT_SYMBOL(drm_release); * @count: count in bytes to read * @offset: offset to read * - * This function must be used by drivers as their .read() #file_operations + * This function must be used by drivers as their &file_operations.read * method iff they use DRM events for asynchronous signalling to userspace. * Since events are used by the KMS API for vblank and page flip completion this * means all modern display drivers must use it. * - * @offset is ignore, DRM events are read like a pipe. Therefore drivers also - * must set the .llseek() #file_operation to no_llseek(). Polling support is + * @offset is ignored, DRM events are read like a pipe. Therefore drivers also + * must set the &file_operation.llseek to no_llseek(). Polling support is * provided by drm_poll(). * * This function will only ever read a full event. Therefore userspace must @@ -537,10 +534,10 @@ EXPORT_SYMBOL(drm_read); * @filp: file pointer * @wait: poll waiter table * - * This function must be used by drivers as their .read() #file_operations - * method iff they use DRM events for asynchronous signalling to userspace. - * Since events are used by the KMS API for vblank and page flip completion this - * means all modern display drivers must use it. + * This function must be used by drivers as their &file_operations.read method + * iff they use DRM events for asynchronous signalling to userspace. Since + * events are used by the KMS API for vblank and page flip completion this means + * all modern display drivers must use it. * * See also drm_read(). * @@ -650,7 +647,8 @@ EXPORT_SYMBOL(drm_event_reserve_init); * @p: tracking structure for the pending event * * This function frees the event @p initialized with drm_event_reserve_init() - * and releases any allocated space. + * and releases any allocated space. It is used to cancel an event when the + * nonblocking operation could not be submitted and needed to be aborted. */ void drm_event_cancel_free(struct drm_device *dev, struct drm_pending_event *p) diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 4321d012c4ba..8f900fb30275 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -64,7 +64,6 @@ struct drm_mode_create_dumb; * structure for GEM drivers. */ struct drm_driver { - /** * @load: * @@ -76,14 +75,94 @@ struct drm_driver { * See drm_dev_init() and drm_dev_register() for proper and * race-free way to set up a &struct drm_device. * + * This is deprecated, do not use! + * * Returns: * * Zero on success, non-zero value on failure. */ int (*load) (struct drm_device *, unsigned long flags); + + /** + * @open: + * + * Driver callback when a new &struct drm_file is opened. Useful for + * setting up driver-private data structures like buffer allocators, + * execution contexts or similar things. Such driver-private resources + * must be released again in @postclose. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to set up any modeset related resources + * in this callback. Doing so would be a driver design bug. + * + * Returns: + * + * 0 on success, a negative error code on failure, which will be + * promoted to userspace as the result of the open() system call. + */ int (*open) (struct drm_device *, struct drm_file *); + + /** + * @preclose: + * + * One of the driver callbacks when a new &struct drm_file is closed. + * Useful for tearing down driver-private data structures allocated in + * @open like buffer allocators, execution contexts or similar things. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to tear down any modeset related + * resources in this callback. Doing so would be a driver design bug. + * + * FIXME: It is not really clear why there's both @preclose and + * @postclose. Without a really good reason, use @postclose only. + */ void (*preclose) (struct drm_device *, struct drm_file *file_priv); + + /** + * @postclose: + * + * One of the driver callbacks when a new &struct drm_file is closed. + * Useful for tearing down driver-private data structures allocated in + * @open like buffer allocators, execution contexts or similar things. + * + * Since the display/modeset side of DRM can only be owned by exactly + * one &struct drm_file (see &drm_file.is_master and &drm_device.master) + * there should never be a need to tear down any modeset related + * resources in this callback. Doing so would be a driver design bug. + * + * FIXME: It is not really clear why there's both @preclose and + * @postclose. Without a really good reason, use @postclose only. + */ void (*postclose) (struct drm_device *, struct drm_file *); + + /** + * @lastclose: + * + * Called when the last &struct drm_file has been closed and there's + * currently no userspace client for the &struct drm_device. + * + * Modern drivers should only use this to force-restore the fbdev + * framebuffer using drm_fb_helper_restore_fbdev_mode_unlocked(). + * Anything else would indicate there's something seriously wrong. + * Modern drivers can also use this to execute delayed power switching + * state changes, e.g. in conjunction with the :ref:`vga_switcheroo` + * infrastructure. + * + * This is called after @preclose and @postclose have been called. + * + * NOTE: + * + * All legacy drivers use this callback to de-initialize the hardware. + * This is purely because of the shadow-attach model, where the DRM + * kernel driver does not really own the hardware. Instead ownershipe is + * handled with the help of userspace through an inheritedly racy dance + * to set/unset the VT into raw mode. + * + * Legacy drivers initialize the hardware in the @firstopen callback, + * which isn't even called for modern drivers. + */ void (*lastclose) (struct drm_device *); /** diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 4e347399a7bd..5dd27ae5c47c 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -45,6 +45,7 @@ struct drm_device; * FIXME: Not sure we want to have drm_minor here in the end, but to avoid * header include loops we need it here for now. */ + enum drm_minor_type { DRM_MINOR_PRIMARY, DRM_MINOR_CONTROL, @@ -52,12 +53,19 @@ enum drm_minor_type { }; /** - * DRM minor structure. This structure represents a drm minor number. + * struct drm_minor - DRM device minor structure + * + * This structure represents a DRM minor number for device nodes in /dev. + * Entirely opaque to drivers and should never be inspected directly by drivers. + * Drivers instead should only interact with &struct drm_file and of course + * &struct drm_device, which is also where driver-private data and resources can + * be attached to. */ struct drm_minor { - int index; /**< Minor device number */ - int type; /**< Control or render */ - struct device *kdev; /**< Linux device */ + /* private: */ + int index; /* Minor device number */ + int type; /* Control or render */ + struct device *kdev; /* Linux device */ struct drm_device *dev; struct dentry *debugfs_root; @@ -66,89 +74,286 @@ struct drm_minor { struct mutex debugfs_lock; /* Protects debugfs_list. */ }; -/* Event queued up for userspace to read */ +/** + * struct drm_pending_event - Event queued up for userspace to read + * + * This represents a DRM event. Drivers can use this as a generic completion + * mechanism, which supports kernel-internal &struct completion, &struct dma_fence + * and also the DRM-specific &struct drm_event delivery mechanism. + */ struct drm_pending_event { + /** + * @completion: + * + * Optional pointer to a kernel internal completion signalled when + * drm_send_event() is called, useful to internally synchronize with + * nonblocking operations. + */ struct completion *completion; + + /** + * @completion_release: + * + * Optional callback currently only used by the atomic modeset helpers + * to clean up the reference count for the structure @completion is + * stored in. + */ void (*completion_release)(struct completion *completion); + + /** + * @event: + * + * Pointer to the actual event that should be sent to userspace to be + * read using drm_read(). Can be optional, since nowadays events are + * also used to signal kernel internal threads with @completion or DMA + * transactions using @fence. + */ struct drm_event *event; + + /** + * @fence: + * + * Optional DMA fence to unblock other hardware transactions which + * depend upon the nonblocking DRM operation this event represents. + */ struct dma_fence *fence; + + /** + * @file_priv: + * + * &struct drm_file where @event should be delivered to. Only set when + * @event is set. + */ + struct drm_file *file_priv; + + /** + * @link: + * + * Double-linked list to keep track of this event. Can be used by the + * driver up to the point when it calls drm_send_event(), after that + * this list entry is owned by the core for its own book-keeping. + */ struct list_head link; + + /** + * @pending_link: + * + * Entry on &drm_file.pending_event_list, to keep track of all pending + * events for @file_priv, to allow correct unwinding of them when + * userspace closes the file before the event is delivered. + */ struct list_head pending_link; - struct drm_file *file_priv; }; -/** File private data */ +/** + * struct drm_file - DRM file private data + * + * This structure tracks DRM state per open file descriptor. + */ struct drm_file { + /** + * @authenticated: + * + * Whether the client is allowed to submit rendering, which for legacy + * nodes means it must be authenticated. + * + * See also the :ref:`section on primary nodes and authentication + * `. + */ unsigned authenticated :1; - /* true when the client has asked us to expose stereo 3D mode flags */ + + /** + * @stereo_allowed: + * + * True when the client has asked us to expose stereo 3D mode flags. + */ unsigned stereo_allowed :1; - /* - * true if client understands CRTC primary planes and cursor planes - * in the plane list + + /** + * @universal_planes: + * + * True if client understands CRTC primary planes and cursor planes + * in the plane list. Automatically set when @atomic is set. */ unsigned universal_planes:1; - /* true if client understands atomic properties */ + + /** @atomic: True if client understands atomic properties. */ unsigned atomic:1; - /* - * This client is the creator of @master. - * Protected by struct drm_device::master_mutex. + + /** + * @is_master: + * + * This client is the creator of @master. Protected by struct + * &drm_device.master_mutex. + * + * See also the :ref:`section on primary nodes and authentication + * `. */ unsigned is_master:1; + /** + * @master: + * + * Master this node is currently associated with. Only relevant if + * drm_is_primary_client() returns true. Note that this only + * matches &drm_device.master if the master is the currently active one. + * + * See also @authentication and @is_master and the :ref:`section on + * primary nodes and authentication `. + */ + struct drm_master *master; + + /** @pid: Process that opened this file. */ struct pid *pid; + + /** @magic: Authentication magic, see @authenticated. */ drm_magic_t magic; + + /** + * @lhead: + * + * List of all open files of a DRM device, linked into + * &drm_device.filelist. Protected by &drm_device.filelist_mutex. + */ struct list_head lhead; + + /** @minor: &struct drm_minor for this file. */ struct drm_minor *minor; - unsigned long lock_count; - /** Mapping of mm object handles to object pointers. */ + /** + * @object_idr: + * + * Mapping of mm object handles to object pointers. Used by the GEM + * subsystem. Protected by @table_lock. + */ struct idr object_idr; - /** Lock for synchronization of access to object_idr. */ + + /** @table_lock: Protects @object_idr. */ spinlock_t table_lock; + /** @filp: Pointer to the core file structure. */ struct file *filp; + + /** + * @driver_priv: + * + * Optional pointer for driver private data. Can be allocated in + * &drm_driver.open and should be freed in &drm_driver.postclose. + */ void *driver_priv; - struct drm_master *master; /* master this node is currently associated with - N.B. not always dev->master */ /** - * fbs - List of framebuffers associated with this file. + * @fbs: + * + * List of &struct drm_framebuffer associated with this file, using the + * &drm_framebuffer.filp_head entry. * - * Protected by fbs_lock. Note that the fbs list holds a reference on - * the fb object to prevent it from untimely disappearing. + * Protected by @fbs_lock. Note that the @fbs list holds a reference on + * the framebuffer object to prevent it from untimely disappearing. */ struct list_head fbs; + + /** @fbs_lock: Protects @fbs. */ struct mutex fbs_lock; - /** User-created blob properties; this retains a reference on the - * property. */ + /** + * @blobs: + * + * User-created blob properties; this retains a reference on the + * property. + * + * Protected by @drm_mode_config.blob_lock; + */ struct list_head blobs; + /** @event_wait: Waitqueue for new events added to @event_list. */ wait_queue_head_t event_wait; + + /** + * @pending_event_list: + * + * List of pending &struct drm_pending_event, used to clean up pending + * events in case this file gets closed before the event is signalled. + * Uses the &drm_pending_event.pending_link entry. + * + * Protect by &drm_device.event_lock. + */ struct list_head pending_event_list; + + /** + * @event_list: + * + * List of &struct drm_pending_event, ready for delivery to userspace + * through drm_read(). Uses the &drm_pending_event.link entry. + * + * Protect by &drm_device.event_lock. + */ struct list_head event_list; + + /** + * @event_space: + * + * Available event space to prevent userspace from + * exhausting kernel memory. Currently limited to the fairly arbitrary + * value of 4KB. + */ int event_space; + /** @event_read_lock: Serializes drm_read(). */ struct mutex event_read_lock; + /** + * @prime: + * + * Per-file buffer caches used by the PRIME buffer sharing code. + */ struct drm_prime_file_private prime; + + /* private: */ + unsigned long lock_count; /* DRI1 legacy lock count */ }; +/** + * drm_is_primary_client - is this an open file of the primary node + * @file_priv: DRM file + * + * Returns true if this is an open file of the primary node, i.e. + * &drm_file.minor of @file_priv is a primary minor. + * + * See also the :ref:`section on primary nodes and authentication + * `. + */ +static inline bool drm_is_primary_client(const struct drm_file *file_priv) +{ + return file_priv->minor->type == DRM_MINOR_PRIMARY; +} + +/** + * drm_is_render_client - is this an open file of the render node + * @file_priv: DRM file + * + * Returns true if this is an open file of the render node, i.e. + * &drm_file.minor of @file_priv is a render minor. + * + * See also the :ref:`section on render nodes `. + */ static inline bool drm_is_render_client(const struct drm_file *file_priv) { return file_priv->minor->type == DRM_MINOR_RENDER; } +/** + * drm_is_control_client - is this an open file of the control node + * @file_priv: DRM file + * + * Control nodes are deprecated and in the process of getting removed from the + * DRM userspace API. Do not ever use! + */ static inline bool drm_is_control_client(const struct drm_file *file_priv) { return file_priv->minor->type == DRM_MINOR_CONTROL; } -static inline bool drm_is_primary_client(const struct drm_file *file_priv) -{ - return file_priv->minor->type == DRM_MINOR_PRIMARY; -} - int drm_open(struct inode *inode, struct file *filp); ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset); -- cgit v1.2.3 From d55f7e5d54c31c8ed61fdbfc59835cd6804ba954 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:56 +0100 Subject: drm: Create DEFINE_DRM_GEM_CMA_FOPS and roll it out to drivers Less code ftw. This converts all drivers except the tinydrm helper module. That one needs more work, since it gets the THIS_MODULE reference from tinydrm.ko instead of the actual driver module like it should. Probably needs a similar trick like I used here with generating the entire struct with a macro. Cc: Noralf Trønnes Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-24-daniel.vetter@ffwll.ch --- drivers/gpu/drm/arm/hdlcd_drv.c | 12 +----------- drivers/gpu/drm/arm/malidp_drv.c | 12 +----------- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 12 +----------- drivers/gpu/drm/drm_file.c | 3 +++ drivers/gpu/drm/drm_gem_cma_helper.c | 3 +++ drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 12 +----------- drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c | 12 +----------- drivers/gpu/drm/imx/imx-drm-core.c | 11 +---------- drivers/gpu/drm/meson/meson_drv.c | 14 +------------ drivers/gpu/drm/mxsfb/mxsfb_drv.c | 14 +------------ drivers/gpu/drm/rcar-du/rcar_du_drv.c | 12 +----------- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 12 +----------- drivers/gpu/drm/sti/sti_drv.c | 11 +---------- drivers/gpu/drm/sun4i/sun4i_drv.c | 12 +----------- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 12 +----------- drivers/gpu/drm/zte/zx_drm_drv.c | 14 +------------ include/drm/drm_gem_cma_helper.h | 26 +++++++++++++++++++++++++ 17 files changed, 46 insertions(+), 158 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 4840dc277339..0e74880b5e94 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -239,17 +239,7 @@ static int hdlcd_debugfs_init(struct drm_minor *minor) } #endif -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver hdlcd_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index a9608a2e5a29..ea2546f766c2 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -178,17 +178,7 @@ static void malidp_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(malidp->fbdev); } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver malidp_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 970bd87d7cce..178acbf5f6c5 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -724,17 +724,7 @@ static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver atmel_hlcdc_dc_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 92848fe75ea6..b42c1334112c 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -89,6 +89,9 @@ DEFINE_MUTEX(drm_global_mutex); * .mmap = drm_gem_mmap, * }; * + * For CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make + * this simpler. + * * FIXME: We should have a macro for this (and the CMA version) so that drivers * don't have to repeat it all the time. */ diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 906984d4bec2..f478a597cd79 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -338,6 +338,9 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj, * as their ->mmap() handler in the DRM device file's file_operations * structure. * + * Instead of directly referencing this function, drivers should use the + * DEFINE_DRM_GEM_CMA_FOPS().macro. + * * Returns: * 0 on success or a negative error code on failure. */ diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index b5391c124c64..6e00f4b267f1 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -161,17 +161,7 @@ static void fsl_dcu_drm_lastclose(struct drm_device *dev) drm_fbdev_cma_restore_mode(fsl_dev->fbdev); } -static const struct file_operations fsl_dcu_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops); static struct drm_driver fsl_dcu_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index 7ec93aec7e88..df4f50713e54 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -146,17 +146,7 @@ err_mode_config_cleanup: return ret; } -static const struct file_operations kirin_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops); static int kirin_gem_cma_dumb_create(struct drm_file *file, struct drm_device *dev, diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 4b7b92a7bcf7..70736615983c 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -57,16 +57,7 @@ static void imx_drm_driver_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(imxdrm->fbhelper); } -static const struct file_operations imx_drm_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_cma_mmap, - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, -}; +DEFINE_DRM_GEM_CMA_FOPS(imx_drm_driver_fops); void imx_drm_connector_destroy(struct drm_connector *connector) { diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 8d17d0e59cbe..bc562a07847b 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -91,19 +91,7 @@ static irqreturn_t meson_irq(int irq, void *arg) return IRQ_HANDLED; } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver meson_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index a4633ada8429..5ac712325c75 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -319,19 +319,7 @@ static irqreturn_t mxsfb_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver mxsfb_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 192346d4fb34..62a3b3e32153 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -226,17 +226,7 @@ static void rcar_du_lastclose(struct drm_device *dev) drm_fbdev_cma_restore_mode(rcdu->fbdev); } -static const struct file_operations rcar_du_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops); static struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 1c7b318b8998..800d1d2c435d 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -127,17 +127,7 @@ static irqreturn_t shmob_drm_irq(int irq, void *arg) return IRQ_HANDLED; } -static const struct file_operations shmob_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(shmob_drm_fops); static struct drm_driver shmob_drm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 6003c664ba0b..a4b574283269 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -167,16 +167,7 @@ static void sti_mode_config_init(struct drm_device *dev) dev->mode_config.funcs = &sti_mode_config_funcs; } -static const struct file_operations sti_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .mmap = drm_gem_cma_mmap, - .poll = drm_poll, - .read = drm_read, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .release = drm_release, -}; +DEFINE_DRM_GEM_CMA_FOPS(sti_driver_fops); static struct drm_driver sti_driver = { .driver_features = DRIVER_MODESET | diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 9ccf7c4deb6d..329ea56106a5 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -25,17 +25,7 @@ #include "sun4i_framebuffer.h" #include "sun4i_layer.h" -static const struct file_operations sun4i_drv_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); static struct drm_driver sun4i_drv_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 5b4ed0ea7768..d7ae5be56d12 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -529,17 +529,7 @@ static int tilcdc_debugfs_init(struct drm_minor *minor) } #endif -static const struct file_operations fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .compat_ioctl = drm_compat_ioctl, - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(fops); static struct drm_driver tilcdc_driver = { .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c index b24a70ba4b83..614e854f6be5 100644 --- a/drivers/gpu/drm/zte/zx_drm_drv.c +++ b/drivers/gpu/drm/zte/zx_drm_drv.c @@ -53,19 +53,7 @@ static void zx_drm_lastclose(struct drm_device *drm) drm_fbdev_cma_restore_mode(priv->fbdev); } -static const struct file_operations zx_drm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = noop_llseek, - .mmap = drm_gem_cma_mmap, -}; +DEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops); static struct drm_driver zx_drm_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index 2abcd5190cc1..f962d33667cf 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -26,6 +26,32 @@ to_drm_gem_cma_obj(struct drm_gem_object *gem_obj) return container_of(gem_obj, struct drm_gem_cma_object, base); } +/** + * DEFINE_DRM_GEM_CMA_FOPS() - macro to generate file operations for CMA drivers + * @name: name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations for CMA based + * drivers, which can be assigned to &drm_driver.fops. Note that this structure + * cannot be shared between drivers, because it contains a reference to the + * current module using THIS_MODULE. + * + * Note that the declaration is already marked as static - if you need a + * non-static version of this you're probably doing it wrong and will break the + * THIS_MODULE reference by accident. + */ +#define DEFINE_DRM_GEM_CMA_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_gem_cma_mmap,\ + } + /* free GEM object */ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj); -- cgit v1.2.3 From f42e181935d5e5670c87d31ae48063a495bbacae Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Mar 2017 15:12:57 +0100 Subject: drm/gem: Add DEFINE_DRM_GEM_FOPS Sadly there's only 1 driver which can use it, everyone else is special for some reason: - gma500 has a horrible runtime PM ioctl wrapper that probably doesn't really work but meh. - i915 needs special compat_ioctl handler because regrets. - arcgpu needs to fixup the pgprot because (no idea why it can't do that in the fault handler like everyone else). - tegra does even worse stuff with pgprot - udl does something with vm_flags too ... - cma helpers, etnaviv, mtk, msm, rockchip, omap all implement some variation on prefaulting. - exynos is exynos, I got lost in the midlayers. - vc4 has to reinvent half of cma helpers because those are too much midlayer, plus vm_flags dances. - vgem also seems unhappy with the default vm_flags. So pretty sad divergence and I'm sure we could do better, but not really an idea. Oh well, maybe this macro here helps to encourage more consistency at least going forward. Reviewed-by: Sean Paul Reviewed-by: Liviu Dudau Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/20170308141257.12119-25-daniel.vetter@ffwll.ch --- drivers/gpu/drm/armada/armada_drv.c | 11 +---------- drivers/gpu/drm/drm_file.c | 8 +++----- include/drm/drm_gem.h | 26 ++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 408e00b64cf6..e618fab7f519 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -60,16 +60,7 @@ static void armada_drm_lastclose(struct drm_device *dev) armada_fbdev_lastclose(dev); } -static const struct file_operations armada_drm_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = drm_read, - .poll = drm_poll, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_mmap, - .open = drm_open, - .release = drm_release, -}; +DEFINE_DRM_GEM_FOPS(armada_drm_fops); static struct drm_driver armada_drm_driver = { .lastclose = armada_drm_lastclose, diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index b42c1334112c..3783b659cd38 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -89,11 +89,9 @@ DEFINE_MUTEX(drm_global_mutex); * .mmap = drm_gem_mmap, * }; * - * For CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make - * this simpler. - * - * FIXME: We should have a macro for this (and the CMA version) so that drivers - * don't have to repeat it all the time. + * For plain GEM based drivers there is the DEFINE_DRM_GEM_FOPS() macro, and for + * CMA based drivers there is the DEFINE_DRM_GEM_CMA_FOPS() macro to make this + * simpler. */ static int drm_open_helper(struct file *filp, struct drm_minor *minor); diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index b9ade75ecd82..663d80358057 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -178,6 +178,32 @@ struct drm_gem_object { struct dma_buf_attachment *import_attach; }; +/** + * DEFINE_DRM_GEM_FOPS() - macro to generate file operations for GEM drivers + * @name: name for the generated structure + * + * This macro autogenerates a suitable &struct file_operations for GEM based + * drivers, which can be assigned to &drm_driver.fops. Note that this structure + * cannot be shared between drivers, because it contains a reference to the + * current module using THIS_MODULE. + * + * Note that the declaration is already marked as static - if you need a + * non-static version of this you're probably doing it wrong and will break the + * THIS_MODULE reference by accident. + */ +#define DEFINE_DRM_GEM_FOPS(name) \ + static const struct file_operations name = {\ + .owner = THIS_MODULE,\ + .open = drm_open,\ + .release = drm_release,\ + .unlocked_ioctl = drm_ioctl,\ + .compat_ioctl = drm_compat_ioctl,\ + .poll = drm_poll,\ + .read = drm_read,\ + .llseek = noop_llseek,\ + .mmap = drm_gem_mmap,\ + } + void drm_gem_object_release(struct drm_gem_object *obj); void drm_gem_object_free(struct kref *kref); int drm_gem_object_init(struct drm_device *dev, -- cgit v1.2.3 From d5b72a2123dfaf9416b1a1177b4be041f8a8b6d4 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 17 Mar 2017 17:34:49 +0100 Subject: dma-fence: add dma_fence_match_context helper Add a helper to check if all fences in a fence array are from a given context. For convenience, the function can also handle being given a non-array fence. Signed-off-by: Philipp Zabel Reviewed-by: Gustavo Padovan Acked-by: Sumit Semwal Signed-off-by: Sumit Semwal Link: http://patchwork.freedesktop.org/patch/msgid/1489768492-25190-1-git-send-email-p.zabel@pengutronix.de --- drivers/dma-buf/dma-fence-array.c | 26 ++++++++++++++++++++++++++ include/linux/dma-fence-array.h | 2 ++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/drivers/dma-buf/dma-fence-array.c b/drivers/dma-buf/dma-fence-array.c index 67eb7c8fb88c..0350829ba62e 100644 --- a/drivers/dma-buf/dma-fence-array.c +++ b/drivers/dma-buf/dma-fence-array.c @@ -144,3 +144,29 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, return array; } EXPORT_SYMBOL(dma_fence_array_create); + +/** + * dma_fence_match_context - Check if all fences are from the given context + * @fence: [in] fence or fence array + * @context: [in] fence context to check all fences against + * + * Checks the provided fence or, for a fence array, all fences in the array + * against the given context. Returns false if any fence is from a different + * context. + */ +bool dma_fence_match_context(struct dma_fence *fence, u64 context) +{ + struct dma_fence_array *array = to_dma_fence_array(fence); + unsigned i; + + if (!dma_fence_is_array(fence)) + return fence->context == context; + + for (i = 0; i < array->num_fences; i++) { + if (array->fences[i]->context != context) + return false; + } + + return true; +} +EXPORT_SYMBOL(dma_fence_match_context); diff --git a/include/linux/dma-fence-array.h b/include/linux/dma-fence-array.h index 5900945f962d..332a5420243c 100644 --- a/include/linux/dma-fence-array.h +++ b/include/linux/dma-fence-array.h @@ -83,4 +83,6 @@ struct dma_fence_array *dma_fence_array_create(int num_fences, u64 context, unsigned seqno, bool signal_on_any); +bool dma_fence_match_context(struct dma_fence *fence, u64 context); + #endif /* __LINUX_DMA_FENCE_ARRAY_H */ -- cgit v1.2.3 From 3ad33ae2bc800496e979b9f7920a57835740083d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Mar 2017 16:53:59 +0530 Subject: drm: Add SCDC helpers SCDC is a mechanism defined in the HDMI 2.0 specification that allows the source and sink devices to communicate. This commit introduces helpers to access the SCDC and provides the symbolic names for the various registers defined in the specification. V2: Rebase. V3: Added R-B from Jose. V4: Rebase V5: Addressed review comments from Ville - Handle the I2c return values in a better way (dp_dual_mode) - Make the macros for SCDC Major/Minor more readable, by adding a 'GET' in the macro names V6: Rebase V7: Rebase V8: Rebase V9: Rebase V10: Rebase Signed-off-by: Thierry Reding Signed-off-by: Shashank Sharma Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-2-git-send-email-shashank.sharma@intel.com --- Documentation/gpu/drm-kms-helpers.rst | 12 ++++ drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_scdc_helper.c | 123 +++++++++++++++++++++++++++++++ include/drm/drm_scdc_helper.h | 132 ++++++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_scdc_helper.c create mode 100644 include/drm/drm_scdc_helper.h (limited to 'include') diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index ac53c0b893f6..c075aadd7078 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -223,6 +223,18 @@ EDID Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_edid.c :export: +SCDC Helper Functions Reference +=============================== + +.. kernel-doc:: drivers/gpu/drm/drm_scdc_helper.c + :doc: scdc helpers + +.. kernel-doc:: include/drm/drm_scdc_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_scdc_helper.c + :export: + Rectangle Utilities Reference ============================= diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 59aae43005ee..59f0f9b696eb 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -31,7 +31,8 @@ drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \ drm_kms_helper_common.o drm_dp_dual_mode_helper.o \ - drm_simple_kms_helper.o drm_modeset_helper.o + drm_simple_kms_helper.o drm_modeset_helper.o \ + drm_scdc_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c new file mode 100644 index 000000000000..c2dd33f89c17 --- /dev/null +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2015 NVIDIA Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +/** + * DOC: scdc helpers + * + * Status and Control Data Channel (SCDC) is a mechanism introduced by the + * HDMI 2.0 specification. It is a point-to-point protocol that allows the + * HDMI source and HDMI sink to exchange data. The same I2C interface that + * is used to access EDID serves as the transport mechanism for SCDC. + */ + +#define SCDC_I2C_SLAVE_ADDRESS 0x54 + +/** + * drm_scdc_read - read a block of data from SCDC + * @adapter: I2C controller + * @offset: start offset of block to read + * @buffer: return location for the block to read + * @size: size of the block to read + * + * Reads a block of data from SCDC, starting at a given offset. + * + * Returns: + * 0 on success, negative error code on failure. + */ +ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, + size_t size) +{ + int ret; + struct i2c_msg msgs[2] = { + { + .addr = SCDC_I2C_SLAVE_ADDRESS, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = SCDC_I2C_SLAVE_ADDRESS, + .flags = I2C_M_RD, + .len = size, + .buf = buffer, + } + }; + + ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(msgs)) + return -EPROTO; + + return 0; +} +EXPORT_SYMBOL(drm_scdc_read); + +/** + * drm_scdc_write - write a block of data to SCDC + * @adapter: I2C controller + * @offset: start offset of block to write + * @buffer: block of data to write + * @size: size of the block to write + * + * Writes a block of data to SCDC, starting at a given offset. + * + * Returns: + * 0 on success, negative error code on failure. + */ +ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, + const void *buffer, size_t size) +{ + struct i2c_msg msg = { + .addr = SCDC_I2C_SLAVE_ADDRESS, + .flags = 0, + .len = 1 + size, + .buf = NULL, + }; + void *data; + int err; + + data = kmalloc(1 + size, GFP_TEMPORARY); + if (!data) + return -ENOMEM; + + msg.buf = data; + + memcpy(data, &offset, sizeof(offset)); + memcpy(data + 1, buffer, size); + + err = i2c_transfer(adapter, &msg, 1); + + kfree(data); + + if (err < 0) + return err; + if (err != 1) + return -EPROTO; + + return 0; +} +EXPORT_SYMBOL(drm_scdc_write); diff --git a/include/drm/drm_scdc_helper.h b/include/drm/drm_scdc_helper.h new file mode 100644 index 000000000000..9c52deb13df4 --- /dev/null +++ b/include/drm/drm_scdc_helper.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015 NVIDIA Corporation. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_SCDC_HELPER_H +#define DRM_SCDC_HELPER_H + +#include +#include + +#define SCDC_SINK_VERSION 0x01 + +#define SCDC_SOURCE_VERSION 0x02 + +#define SCDC_UPDATE_0 0x10 +#define SCDC_READ_REQUEST_TEST (1 << 2) +#define SCDC_CED_UPDATE (1 << 1) +#define SCDC_STATUS_UPDATE (1 << 0) + +#define SCDC_UPDATE_1 0x11 + +#define SCDC_TMDS_CONFIG 0x20 +#define SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 (1 << 1) +#define SCDC_TMDS_BIT_CLOCK_RATIO_BY_10 (0 << 1) +#define SCDC_SCRAMBLING_ENABLE (1 << 0) + +#define SCDC_SCRAMBLER_STATUS 0x21 +#define SCDC_SCRAMBLING_STATUS (1 << 0) + +#define SCDC_CONFIG_0 0x30 +#define SCDC_READ_REQUEST_ENABLE (1 << 0) + +#define SCDC_STATUS_FLAGS_0 0x40 +#define SCDC_CH2_LOCK (1 < 3) +#define SCDC_CH1_LOCK (1 < 2) +#define SCDC_CH0_LOCK (1 < 1) +#define SCDC_CH_LOCK_MASK (SCDC_CH2_LOCK | SCDC_CH1_LOCK | SCDC_CH0_LOCK) +#define SCDC_CLOCK_DETECT (1 << 0) + +#define SCDC_STATUS_FLAGS_1 0x41 + +#define SCDC_ERR_DET_0_L 0x50 +#define SCDC_ERR_DET_0_H 0x51 +#define SCDC_ERR_DET_1_L 0x52 +#define SCDC_ERR_DET_1_H 0x53 +#define SCDC_ERR_DET_2_L 0x54 +#define SCDC_ERR_DET_2_H 0x55 +#define SCDC_CHANNEL_VALID (1 << 7) + +#define SCDC_ERR_DET_CHECKSUM 0x56 + +#define SCDC_TEST_CONFIG_0 0xc0 +#define SCDC_TEST_READ_REQUEST (1 << 7) +#define SCDC_TEST_READ_REQUEST_DELAY(x) ((x) & 0x7f) + +#define SCDC_MANUFACTURER_IEEE_OUI 0xd0 +#define SCDC_MANUFACTURER_IEEE_OUI_SIZE 3 + +#define SCDC_DEVICE_ID 0xd3 +#define SCDC_DEVICE_ID_SIZE 8 + +#define SCDC_DEVICE_HARDWARE_REVISION 0xdb +#define SCDC_GET_DEVICE_HARDWARE_REVISION_MAJOR(x) (((x) >> 4) & 0xf) +#define SCDC_GET_DEVICE_HARDWARE_REVISION_MINOR(x) (((x) >> 0) & 0xf) + +#define SCDC_DEVICE_SOFTWARE_MAJOR_REVISION 0xdc +#define SCDC_DEVICE_SOFTWARE_MINOR_REVISION 0xdd + +#define SCDC_MANUFACTURER_SPECIFIC 0xde +#define SCDC_MANUFACTURER_SPECIFIC_SIZE 34 + +ssize_t drm_scdc_read(struct i2c_adapter *adapter, u8 offset, void *buffer, + size_t size); +ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, + const void *buffer, size_t size); + +/** + * drm_scdc_readb - read a single byte from SCDC + * @adapter: I2C adapter + * @offset: offset of register to read + * @value: return location for the register value + * + * Reads a single byte from SCDC. This is a convenience wrapper around the + * drm_scdc_read() function. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_scdc_readb(struct i2c_adapter *adapter, u8 offset, + u8 *value) +{ + return drm_scdc_read(adapter, offset, value, sizeof(*value)); +} + +/** + * drm_scdc_writeb - write a single byte to SCDC + * @adapter: I2C adapter + * @offset: offset of register to read + * @value: return location for the register value + * + * Writes a single byte to SCDC. This is a convenience wrapper around the + * drm_scdc_write() function. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +static inline int drm_scdc_writeb(struct i2c_adapter *adapter, u8 offset, + u8 value) +{ + return drm_scdc_write(adapter, offset, &value, sizeof(value)); +} + +#endif -- cgit v1.2.3 From 50dd1bd1e2395a78e5adfad96487092e215483e0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 13 Mar 2017 16:54:00 +0530 Subject: drm/edid: check for HF-VSDB block This patch implements a small function that finds if a given CEA db is hdmi-forum vendor specific data block or not. V2: Rebase. V3: Added R-B from Jose. V4: Rebase V5: Rebase V6: Rebase V7: Rebase V8: Rebase V9: Rebase V10: Rebase Signed-off-by: Thierry Reding Signed-off-by: Shashank Sharma Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-3-git-send-email-shashank.sharma@intel.com --- drivers/gpu/drm/drm_edid.c | 15 +++++++++++++++ include/linux/hdmi.h | 1 + 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 171d7a02ace0..92e9551f3c38 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3248,6 +3248,21 @@ static bool cea_db_is_hdmi_vsdb(const u8 *db) return hdmi_id == HDMI_IEEE_OUI; } +static bool cea_db_is_hdmi_forum_vsdb(const u8 *db) +{ + unsigned int oui; + + if (cea_db_tag(db) != VENDOR_BLOCK) + return false; + + if (cea_db_payload_len(db) < 7) + return false; + + oui = db[3] << 16 | db[2] << 8 | db[1]; + + return oui == HDMI_FORUM_IEEE_OUI; +} + #define for_each_cea_db(cea, i, start, end) \ for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index edbb4fc674ed..d271ff23984f 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -35,6 +35,7 @@ enum hdmi_infoframe_type { }; #define HDMI_IEEE_OUI 0x000c03 +#define HDMI_FORUM_IEEE_OUI 0xc45dd8 #define HDMI_INFOFRAME_HEADER_SIZE 4 #define HDMI_AVI_INFOFRAME_SIZE 13 #define HDMI_SPD_INFOFRAME_SIZE 25 -- cgit v1.2.3 From afa1c763653e0fd68f2d1e3dca869c9453d8ef06 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 13 Mar 2017 16:54:01 +0530 Subject: drm/edid: detect SCDC support in HF-VSDB This patch does following: - Adds a new structure (drm_hdmi_info) in drm_display_info. This structure will be used to save and indicate if sink supports advanced HDMI 2.0 features - Adds another structure drm_scdc within drm_hdmi_info, to reflect scdc support and capabilities in connected HDMI 2.0 sink. - Checks the HF-VSDB block for presence of SCDC, and marks it in scdc structure - If SCDC is present, checks if sink is capable of generating SCDC read request, and marks it in scdc structure. V2: Addressed review comments Thierry: - Fix typos in commit message and make abbreviation consistent across the commit message. - Change structure object name from hdmi_info -> hdmi - Fix typos and abbreviations in description of structure drm_hdmi_info end the description with a full stop. - Create a structure drm_scdc, and keep all information related to SCDC register set (supported, read request supported) etc in it. Ville: - Change rr -> read_request - Call drm_detect_scrambling function drm_parse_hf_vsdb so that all of HF-VSDB parsing can be kept in same function, in incremental patches. V3: Rebase. V4: Rebase. V5: Rebase. V6: Rebase. V7: Added R-B from Jose. V8: Rebase. V9: Rebase. V10: Rebase. Signed-off-by: Shashank Sharma Reviewed-by: Thierry Reding Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-4-git-send-email-shashank.sharma@intel.com --- drivers/gpu/drm/drm_edid.c | 14 ++++++++++++++ include/drm/drm_connector.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 92e9551f3c38..d6f16c02afb5 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3814,6 +3814,18 @@ drm_default_rgb_quant_range(const struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_default_rgb_quant_range); +static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, + const u8 *hf_vsdb) +{ + struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; + + if (hf_vsdb[6] & 0x80) { + hdmi->scdc.supported = true; + if (hf_vsdb[6] & 0x40) + hdmi->scdc.read_request = true; + } +} + static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, const u8 *hdmi) { @@ -3928,6 +3940,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector, if (cea_db_is_hdmi_vsdb(db)) drm_parse_hdmi_vsdb_video(connector, db); + if (cea_db_is_hdmi_forum_vsdb(db)) + drm_parse_hdmi_forum_vsdb(connector, db); } } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index fabb35aba5f6..bf9d6f54ad16 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -87,6 +87,34 @@ enum subpixel_order { SubPixelVerticalRGB, SubPixelVerticalBGR, SubPixelNone, + +}; + +/* + * struct drm_scdc - Information about scdc capabilities of a HDMI 2.0 sink + * + * Provides SCDC register support and capabilities related information on a + * HDMI 2.0 sink. In case of a HDMI 1.4 sink, all parameter must be 0. + */ +struct drm_scdc { + /** + * @supported: status control & data channel present. + */ + bool supported; + /** + * @read_request: sink is capable of generating scdc read request. + */ + bool read_request; +}; + +/** + * struct drm_hdmi_info - runtime information about the connected HDMI sink + * + * Describes if a given display supports advanced HDMI 2.0 features. + * This information is available in CEA-861-F extension blocks (like HF-VSDB). + */ +struct drm_hdmi_info { + struct drm_scdc scdc; }; /** @@ -204,6 +232,11 @@ struct drm_display_info { * @cea_rev: CEA revision of the HDMI sink. */ u8 cea_rev; + + /** + * @hdmi: advance features of a HDMI sink. + */ + struct drm_hdmi_info hdmi; }; int drm_display_info_set_bus_formats(struct drm_display_info *info, -- cgit v1.2.3 From 62c58af32c935a98a1e8d8ceb39a3a47b36fbbcd Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 13 Mar 2017 16:54:02 +0530 Subject: drm/edid: detect SCDC support in HF-VSDB This patch does following: - Adds a new structure (drm_hdmi_info) in drm_display_info. This structure will be used to save and indicate if sink supports advanced HDMI 2.0 features - Adds another structure drm_scdc within drm_hdmi_info, to reflect scdc support and capabilities in connected HDMI 2.0 sink. - Checks the HF-VSDB block for presence of SCDC, and marks it in scdc structure - If SCDC is present, checks if sink is capable of generating SCDC read request, and marks it in scdc structure. V2: Addressed review comments Thierry: - Fix typos in commit message and make abbreviation consistent across the commit message. - Change structure object name from hdmi_info -> hdmi - Fix typos and abbreviations in description of structure drm_hdmi_info end the description with a full stop. - Create a structure drm_scdc, and keep all information related to SCDC register set (supported, read request supported) etc in it. Ville: - Change rr -> read_request - Call drm_detect_scrambling function drm_parse_hf_vsdb so that all of HF-VSDB parsing can be kept in same function, in incremental patches. V3: Rebase. V4: Rebase. V5: Rebase. V6: Addressed review comments from Ville - Add clock rate calculations for 1/10 and 1/40 ratios - Remove leftovers from old patchset V7: Added R-B from Jose. V8: Rebase. V9: Rebase. V10: Rebase. Signed-off-by: Shashank Sharma Reviewed-by: Thierry Reding Reviewed-by: Jose Abreu Signed-off-by: Jani Nikula Link: http://patchwork.freedesktop.org/patch/msgid/1489404244-16608-5-git-send-email-shashank.sharma@intel.com --- drivers/gpu/drm/drm_edid.c | 33 ++++++++++- drivers/gpu/drm/drm_scdc_helper.c | 121 ++++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 19 ++++++ include/drm/drm_edid.h | 1 - include/drm/drm_scdc_helper.h | 27 +++++++++ 5 files changed, 199 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d6f16c02afb5..99144f879a4f 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" @@ -3817,13 +3818,43 @@ EXPORT_SYMBOL(drm_default_rgb_quant_range); static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector, const u8 *hf_vsdb) { - struct drm_hdmi_info *hdmi = &connector->display_info.hdmi; + struct drm_display_info *display = &connector->display_info; + struct drm_hdmi_info *hdmi = &display->hdmi; if (hf_vsdb[6] & 0x80) { hdmi->scdc.supported = true; if (hf_vsdb[6] & 0x40) hdmi->scdc.read_request = true; } + + /* + * All HDMI 2.0 monitors must support scrambling at rates > 340 MHz. + * And as per the spec, three factors confirm this: + * * Availability of a HF-VSDB block in EDID (check) + * * Non zero Max_TMDS_Char_Rate filed in HF-VSDB (let's check) + * * SCDC support available (let's check) + * Lets check it out. + */ + + if (hf_vsdb[5]) { + /* max clock is 5000 KHz times block value */ + u32 max_tmds_clock = hf_vsdb[5] * 5000; + struct drm_scdc *scdc = &hdmi->scdc; + + if (max_tmds_clock > 340000) { + display->max_tmds_clock = max_tmds_clock; + DRM_DEBUG_KMS("HF-VSDB: max TMDS clock %d kHz\n", + display->max_tmds_clock); + } + + if (scdc->supported) { + scdc->scrambling.supported = true; + + /* Few sinks support scrambling for cloks < 340M */ + if ((hf_vsdb[6] & 0x8)) + scdc->scrambling.low_rates = true; + } + } } static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector, diff --git a/drivers/gpu/drm/drm_scdc_helper.c b/drivers/gpu/drm/drm_scdc_helper.c index c2dd33f89c17..3cd96a95736d 100644 --- a/drivers/gpu/drm/drm_scdc_helper.c +++ b/drivers/gpu/drm/drm_scdc_helper.c @@ -22,8 +22,10 @@ */ #include +#include #include +#include /** * DOC: scdc helpers @@ -121,3 +123,122 @@ ssize_t drm_scdc_write(struct i2c_adapter *adapter, u8 offset, return 0; } EXPORT_SYMBOL(drm_scdc_write); + +/** + * drm_scdc_check_scrambling_status - what is status of scrambling? + * @adapter: I2C adapter for DDC channel + * + * Reads the scrambler status over SCDC, and checks the + * scrambling status. + * + * Returns: + * True if the scrambling is enabled, false otherwise. + */ + +bool drm_scdc_get_scrambling_status(struct i2c_adapter *adapter) +{ + u8 status; + int ret; + + ret = drm_scdc_readb(adapter, SCDC_SCRAMBLER_STATUS, &status); + if (ret < 0) { + DRM_ERROR("Failed to read scrambling status, error %d\n", ret); + return false; + } + + return status & SCDC_SCRAMBLING_STATUS; +} +EXPORT_SYMBOL(drm_scdc_get_scrambling_status); + +/** + * drm_scdc_set_scrambling - enable scrambling + * @adapter: I2C adapter for DDC channel + * @enable: bool to indicate if scrambling is to be enabled/disabled + * + * Writes the TMDS config register over SCDC channel, and: + * enables scrambling when enable = 1 + * disables scrambling when enable = 0 + * + * Returns: + * True if scrambling is set/reset successfully, false otherwise. + */ + +bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable) +{ + u8 config; + int ret; + + ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); + if (ret < 0) { + DRM_ERROR("Failed to read tmds config, err=%d\n", ret); + return false; + } + + if (enable) + config |= SCDC_SCRAMBLING_ENABLE; + else + config &= ~SCDC_SCRAMBLING_ENABLE; + + ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); + if (ret < 0) { + DRM_ERROR("Failed to enable scrambling, error %d\n", ret); + return false; + } + + return true; +} +EXPORT_SYMBOL(drm_scdc_set_scrambling); + +/** + * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio + * @adapter: I2C adapter for DDC channel + * @set: ret or reset the high clock ratio + * + * TMDS clock ratio calculations go like this: + * TMDS character = 10 bit TMDS encoded value + * TMDS character rate = The rate at which TMDS characters are transmitted(Mcsc) + * TMDS bit rate = 10x TMDS character rate + * As per the spec: + * TMDS clock rate for pixel clock < 340 MHz = 1x the character rate + * = 1/10 pixel clock rate + * TMDS clock rate for pixel clock > 340 MHz = 0.25x the character rate + * = 1/40 pixel clock rate + * + * Writes to the TMDS config register over SCDC channel, and: + * sets TMDS clock ratio to 1/40 when set = 1 + * sets TMDS clock ratio to 1/10 when set = 0 + * + * Returns: + * True if write is successful, false otherwise. + */ +bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set) +{ + u8 config; + int ret; + + ret = drm_scdc_readb(adapter, SCDC_TMDS_CONFIG, &config); + if (ret < 0) { + DRM_ERROR("Failed to read tmds config, err=%d\n", ret); + return false; + } + + if (set) + config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; + else + config &= ~SCDC_TMDS_BIT_CLOCK_RATIO_BY_40; + + ret = drm_scdc_writeb(adapter, SCDC_TMDS_CONFIG, config); + if (ret < 0) { + DRM_ERROR("Failed to set TMDS clock ratio, error %d\n", ret); + return false; + } + + /* + * The spec says that a source should wait minimum 1ms and maximum + * 100ms after writing the TMDS config for clock ratio. Lets allow a + * wait of upto 2ms here. + */ + usleep_range(1000, 2000); + return true; +} +EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index bf9d6f54ad16..f8b766d70a46 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -90,6 +90,20 @@ enum subpixel_order { }; +/** + * struct drm_scrambling: sink's scrambling support. + */ +struct drm_scrambling { + /** + * @supported: scrambling supported for rates > 340 Mhz. + */ + bool supported; + /** + * @low_rates: scrambling supported for rates <= 340 Mhz. + */ + bool low_rates; +}; + /* * struct drm_scdc - Information about scdc capabilities of a HDMI 2.0 sink * @@ -105,8 +119,13 @@ struct drm_scdc { * @read_request: sink is capable of generating scdc read request. */ bool read_request; + /** + * @scrambling: sink's scrambling capabilities + */ + struct drm_scrambling scrambling; }; + /** * struct drm_hdmi_info - runtime information about the connected HDMI sink * diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 3ead84d93792..7b9f48b62e07 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -476,5 +476,4 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name, struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh, bool rb); - #endif /* __DRM_EDID_H__ */ diff --git a/include/drm/drm_scdc_helper.h b/include/drm/drm_scdc_helper.h index 9c52deb13df4..ab6bcfbceba9 100644 --- a/include/drm/drm_scdc_helper.h +++ b/include/drm/drm_scdc_helper.h @@ -129,4 +129,31 @@ static inline int drm_scdc_writeb(struct i2c_adapter *adapter, u8 offset, return drm_scdc_write(adapter, offset, &value, sizeof(value)); } +/** + * drm_scdc_set_scrambling - enable scrambling + * @adapter: I2C adapter for DDC channel + * @enable: bool to indicate if scrambling is to be enabled/disabled + * + * Writes the TMDS config register over SCDC channel, and: + * enables scrambling when enable = 1 + * disables scrambling when enable = 0 + * + * Returns: + * True if scrambling is set/reset successfully, false otherwise. + */ +bool drm_scdc_set_scrambling(struct i2c_adapter *adapter, bool enable); + +/** + * drm_scdc_set_high_tmds_clock_ratio - set TMDS clock ratio + * @adapter: I2C adapter for DDC channel + * @set: ret or reset the high clock ratio + * + * Writes to the TMDS config register over SCDC channel, and: + * sets TMDS clock ratio to 1/40 when set = 1 + * sets TMDS clock ratio to 1/10 when set = 0 + * + * Returns: + * True if write is successful, false otherwise. + */ +bool drm_scdc_set_high_tmds_clock_ratio(struct i2c_adapter *adapter, bool set); #endif -- cgit v1.2.3