vga_switcheroo: Add support for switching only the DDC
Originally by Seth Forshee <seth.forshee@canonical.com>, 2012-10-04: During graphics driver initialization it's useful to be able to mux only the DDC to the inactive client in order to read the EDID. Add a switch_ddc callback to allow capable handlers to provide this functionality, and add vga_switcheroo_switch_ddc() to allow DRM to mux only the DDC. Modified by Dave Airlie <airlied@gmail.com>, 2012-12-22: I can't figure out why I didn't like this, but I rewrote this [...] to lock/unlock the ddc lines [...]. I think I'd prefer something like that otherwise the interface got really ugly. Modified by Lukas Wunner <lukas@wunner.de>, 2015-04 - 2015-10: Change semantics of ->switch_ddc handler callback to return previous DDC owner. Original version tried to determine previous DDC owner with find_active_client() but this fails if the inactive client registers before the active client. Don't lock vgasr_mutex in _lock_ddc() / _unlock_ddc(), it can cause deadlocks because (a) during switch (with vgasr_mutex already held), GPU is woken and probes its outputs, tries to re-acquire vgasr_mutex to lock DDC lines; (b) Likewise during switch, GPU is suspended and calls cancel_delayed_work_sync() to stop output polling, if poll task is running at this moment we may wait forever for it to finish. Instead, lock mux_hw_lock when unregistering the handler because the only reason why we'd want to lock vgasr_mutex in _lock_ddc() / _unlock_ddc() is to block the handler from disappearing while DDC lines are switched. Also acquire mux_hw_lock in stage2 to avoid race condition where reading the EDID and switching happens simultaneously. Likewise on MIGD / MDIS commands and on runtime suspend. v2.1: Overhaul locking, squash commits (Daniel Vetter) v2.2: Readability improvements (Thierry Reding) v2.3: Overhaul locking once more v2.4: Retain semantics of ->switchto handler callback to switch all pins, including DDC (Daniel Vetter) v5: Rename ddc_lock to mux_hw_lock: Since we acquire this both when calling ->switch_ddc and ->switchto, it protects not just access to the DDC lines but to the mux in general. This is in line with the DRM convention to use low-level locks to avoid concurrent hw access (e.g. i2c, dp_aux) which are often called hw_lock (Daniel Vetter) Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115 Tested-by: Lukas Wunner <lukas@wunner.de> [MBP 9,1 2012 intel IVB + nvidia GK107 pre-retina 15"] Cc: Seth Forshee <seth.forshee@canonical.com> Cc: Dave Airlie <airlied@gmail.com> Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: http://patchwork.freedesktop.org/patch/msgid/e81ae9722b84c5ed591805fee3ea6dbf5dc6c4b3.1452525860.git.lukas@wunner.de
This commit is contained in:

committed by
Daniel Vetter

parent
156d7d4120
commit
e4cb81d7e4
@@ -102,6 +102,9 @@ enum vga_switcheroo_client_id {
|
||||
* Mandatory. For muxless machines this should be a no-op. Returning 0
|
||||
* denotes success, anything else failure (in which case the switch is
|
||||
* aborted)
|
||||
* @switch_ddc: switch DDC lines to given client.
|
||||
* Optional. Should return the previous DDC owner on success or a
|
||||
* negative int on failure
|
||||
* @power_state: cut or reinstate power of given client.
|
||||
* Optional. The return value is ignored
|
||||
* @get_client_id: determine if given pci device is integrated or discrete GPU.
|
||||
@@ -113,6 +116,7 @@ enum vga_switcheroo_client_id {
|
||||
struct vga_switcheroo_handler {
|
||||
int (*init)(void);
|
||||
int (*switchto)(enum vga_switcheroo_client_id id);
|
||||
int (*switch_ddc)(enum vga_switcheroo_client_id id);
|
||||
int (*power_state)(enum vga_switcheroo_client_id id,
|
||||
enum vga_switcheroo_state state);
|
||||
enum vga_switcheroo_client_id (*get_client_id)(struct pci_dev *pdev);
|
||||
@@ -156,6 +160,8 @@ int vga_switcheroo_register_handler(const struct vga_switcheroo_handler *handler
|
||||
enum vga_switcheroo_handler_flags_t handler_flags);
|
||||
void vga_switcheroo_unregister_handler(void);
|
||||
enum vga_switcheroo_handler_flags_t vga_switcheroo_handler_flags(void);
|
||||
int vga_switcheroo_lock_ddc(struct pci_dev *pdev);
|
||||
int vga_switcheroo_unlock_ddc(struct pci_dev *pdev);
|
||||
|
||||
int vga_switcheroo_process_delayed_switch(void);
|
||||
|
||||
@@ -179,6 +185,8 @@ static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
|
||||
enum vga_switcheroo_client_id id) { return 0; }
|
||||
static inline void vga_switcheroo_unregister_handler(void) {}
|
||||
static inline enum vga_switcheroo_handler_flags_t vga_switcheroo_handler_flags(void) { return 0; }
|
||||
static inline int vga_switcheroo_lock_ddc(struct pci_dev *pdev) { return -ENODEV; }
|
||||
static inline int vga_switcheroo_unlock_ddc(struct pci_dev *pdev) { return -ENODEV; }
|
||||
static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
|
||||
static inline enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; }
|
||||
|
||||
|
Reference in New Issue
Block a user