Merge tag 'v4.3-rc2' into topic/drm-misc
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 22 Sep 2015 09:02:18 +0000 (11:02 +0200)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 24 Sep 2015 15:18:41 +0000 (17:18 +0200)
Backmerge Linux 4.3-rc2 because of conflicts in the dp helper code
between bugfixes and new code. Just adjacent lines really.

On top of that there's a silent conflict in the new fsl-dcu driver
merged into 4.3 and

commit 844f9111f6f54f88eb2f0fac121b82ce77193866
Author: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Date:   Wed Sep 2 10:42:40 2015 +0200

    drm/atomic: Make prepare_fb/cleanup_fb only take state, v3.

which Thierry Reding spotted and provided a fixup for.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
49 files changed:
Documentation/kernel-parameters.txt
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_dp_helper.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_edid_load.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_internal.h
drivers/gpu/drm/drm_mm.c
drivers/gpu/drm/drm_modeset_lock.c
drivers/gpu/drm/drm_plane_helper.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
drivers/gpu/drm/i2c/ch7006_drv.c
drivers/gpu/drm/i2c/ch7006_mode.c
drivers/gpu/drm/i2c/ch7006_priv.h
drivers/gpu/drm/i915/intel_audio.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fbdev.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
drivers/gpu/drm/msm/msm_atomic.c
drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c
drivers/gpu/drm/nouveau/dispnv04/tvnv17.h
drivers/gpu/drm/omapdrm/omap_drv.c
drivers/gpu/drm/omapdrm/omap_plane.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/rcar-du/rcar_du_kms.c
drivers/gpu/drm/sti/sti_drv.c
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/dpaux.c
drivers/gpu/drm/tegra/drm.c
drivers/gpu/vga/vga_switcheroo.c
include/drm/drm_atomic_helper.h
include/drm/drm_crtc.h
include/drm/drm_dp_helper.h
include/drm/drm_edid.h
include/drm/drm_fb_helper.h
include/drm/drm_modeset_lock.h
include/drm/drm_plane_helper.h
include/linux/vga_switcheroo.h
include/uapi/drm/drm_mode.h

index 22a4b68..c6dd5f3 100644 (file)
@@ -929,11 +929,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        The filter can be disabled or changed to another
                        driver later using sysfs.
 
-       drm_kms_helper.edid_firmware=[<connector>:]<file>
-                       Broken monitors, graphic adapters and KVMs may
-                       send no or incorrect EDID data sets. This parameter
-                       allows to specify an EDID data set in the
-                       /lib/firmware directory that is used instead.
+       drm_kms_helper.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>]
+                       Broken monitors, graphic adapters, KVMs and EDIDless
+                       panels may send no or incorrect EDID data sets.
+                       This parameter allows to specify an EDID data sets
+                       in the /lib/firmware directory that are used instead.
                        Generic built-in EDID data sets are used, if one of
                        edid/1024x768.bin, edid/1280x1024.bin,
                        edid/1680x1050.bin, or edid/1920x1080.bin is given
@@ -942,7 +942,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        available in Documentation/EDID/HOWTO.txt. An EDID
                        data set will only be used for a particular connector,
                        if its name and a colon are prepended to the EDID
-                       name.
+                       name. Each connector may use a unique EDID data
+                       set by separating the files with a comma.  An EDID
+                       data set with no connector name will be used for
+                       any connectors not explicitly specified.
 
        dscc4.setup=    [NET]
 
index be9fa82..36fda86 100644 (file)
@@ -712,11 +712,13 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 }
 
 static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
-                                       struct drm_framebuffer *fb,
                                        const struct drm_plane_state *new_state)
 {
        struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
 
+       if (!new_state->fb)
+               return 0;
+
        return atmel_hlcdc_layer_update_start(&plane->layer);
 }
 
index f7d5166..940f80b 100644 (file)
@@ -663,6 +663,25 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
        return 0;
 }
 
+static bool
+plane_switching_crtc(struct drm_atomic_state *state,
+                    struct drm_plane *plane,
+                    struct drm_plane_state *plane_state)
+{
+       if (!plane->state->crtc || !plane_state->crtc)
+               return false;
+
+       if (plane->state->crtc == plane_state->crtc)
+               return false;
+
+       /* This could be refined, but currently there's no helper or driver code
+        * to implement direct switching of active planes nor userspace to take
+        * advantage of more direct plane switching without the intermediate
+        * full OFF state.
+        */
+       return true;
+}
+
 /**
  * drm_atomic_plane_check - check plane state
  * @plane: plane to check
@@ -734,6 +753,12 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
                return -ENOSPC;
        }
 
+       if (plane_switching_crtc(state->state, plane, state)) {
+               DRM_DEBUG_ATOMIC("[PLANE:%d] switching CRTC directly\n",
+                                plane->base.id);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
index aecb5d6..87a2a44 100644 (file)
  * add their own additional internal state.
  *
  * This library also provides default implementations for the check callback in
- * drm_atomic_helper_check and for the commit callback with
- * drm_atomic_helper_commit. But the individual stages and callbacks are expose
- * to allow drivers to mix and match and e.g. use the plane helpers only
+ * drm_atomic_helper_check() and for the commit callback with
+ * drm_atomic_helper_commit(). But the individual stages and callbacks are
+ * exposed to allow drivers to mix and match and e.g. use the plane helpers only
  * together with a driver private modeset implementation.
  *
  * This library also provides implementations for all the legacy driver
- * interfaces on top of the atomic interface. See drm_atomic_helper_set_config,
- * drm_atomic_helper_disable_plane, drm_atomic_helper_disable_plane and the
+ * interfaces on top of the atomic interface. See drm_atomic_helper_set_config(),
+ * drm_atomic_helper_disable_plane(), drm_atomic_helper_disable_plane() and the
  * various functions to implement set_property callbacks. New drivers must not
  * implement these functions themselves but must use the provided helpers.
  */
@@ -993,6 +993,22 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
  * object. This can still fail when e.g. the framebuffer reservation fails. For
  * now this doesn't implement asynchronous commits.
  *
+ * Note that right now this function does not support async commits, and hence
+ * driver writers must implement their own version for now. Also note that the
+ * default ordering of how the various stages are called is to match the legacy
+ * modeset helper library closest. One peculiarity of that is that it doesn't
+ * mesh well with runtime PM at all.
+ *
+ * For drivers supporting runtime PM the recommended sequence is
+ *
+ *     drm_atomic_helper_commit_modeset_disables(dev, state);
+ *
+ *     drm_atomic_helper_commit_modeset_enables(dev, state);
+ *
+ *     drm_atomic_helper_commit_planes(dev, state, true);
+ *
+ * See the kerneldoc entries for these three functions for more details.
+ *
  * RETURNS
  * Zero for success or -errno.
  */
@@ -1037,7 +1053,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state);
+       drm_atomic_helper_commit_planes(dev, state, false);
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
@@ -1077,7 +1093,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  * work item, which allows nice concurrent updates on disjoint sets of crtcs.
  *
  * 3. The software state is updated synchronously with
- * drm_atomic_helper_swap_state. Doing this under the protection of all modeset
+ * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
  * locks means concurrent callers never see inconsistent state. And doing this
  * while it's guaranteed that no relevant async worker runs means that async
  * workers do not need grab any locks. Actually they must not grab locks, for
@@ -1111,17 +1127,14 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
                struct drm_plane_state *plane_state = state->plane_states[i];
-               struct drm_framebuffer *fb;
 
                if (!plane)
                        continue;
 
                funcs = plane->helper_private;
 
-               fb = plane_state->fb;
-
-               if (fb && funcs->prepare_fb) {
-                       ret = funcs->prepare_fb(plane, fb, plane_state);
+               if (funcs->prepare_fb) {
+                       ret = funcs->prepare_fb(plane, plane_state);
                        if (ret)
                                goto fail;
                }
@@ -1134,17 +1147,14 @@ fail:
                const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
                struct drm_plane_state *plane_state = state->plane_states[i];
-               struct drm_framebuffer *fb;
 
                if (!plane)
                        continue;
 
                funcs = plane->helper_private;
 
-               fb = state->plane_states[i]->fb;
-
-               if (fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, fb, plane_state);
+               if (funcs->cleanup_fb)
+                       funcs->cleanup_fb(plane, plane_state);
 
        }
 
@@ -1152,10 +1162,16 @@ fail:
 }
 EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
 
+bool plane_crtc_active(struct drm_plane_state *state)
+{
+       return state->crtc && state->crtc->state->active;
+}
+
 /**
  * drm_atomic_helper_commit_planes - commit plane state
  * @dev: DRM device
  * @old_state: atomic state object with old state structures
+ * @active_only: Only commit on active CRTC if set
  *
  * This function commits the new plane state using the plane and atomic helper
  * functions for planes and crtcs. It assumes that the atomic state has already
@@ -1168,9 +1184,26 @@ EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
  * Note that this function does all plane updates across all CRTCs in one step.
  * If the hardware can't support this approach look at
  * drm_atomic_helper_commit_planes_on_crtc() instead.
+ *
+ * Plane parameters can be updated by applications while the associated CRTC is
+ * disabled. The DRM/KMS core will store the parameters in the plane state,
+ * which will be available to the driver when the CRTC is turned on. As a result
+ * most drivers don't need to be immediately notified of plane updates for a
+ * disabled CRTC.
+ *
+ * Unless otherwise needed, drivers are advised to set the @active_only
+ * parameters to true in order not to receive plane update notifications related
+ * to a disabled CRTC. This avoids the need to manually ignore plane updates in
+ * driver code when the driver and/or hardware can't or just don't need to deal
+ * with updates on disabled CRTCs, for example when supporting runtime PM.
+ *
+ * The drm_atomic_helper_commit() default implementation only sets @active_only
+ * to false to most closely match the behaviour of the legacy helpers. This should
+ * not be copied blindly by drivers.
  */
 void drm_atomic_helper_commit_planes(struct drm_device *dev,
-                                    struct drm_atomic_state *old_state)
+                                    struct drm_atomic_state *old_state,
+                                    bool active_only)
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state;
@@ -1186,25 +1219,43 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_begin)
                        continue;
 
+               if (active_only && !crtc->state->active)
+                       continue;
+
                funcs->atomic_begin(crtc, old_crtc_state);
        }
 
        for_each_plane_in_state(old_state, plane, old_plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
+               bool disabling;
 
                funcs = plane->helper_private;
 
                if (!funcs)
                        continue;
 
+               disabling = drm_atomic_plane_disabling(plane, old_plane_state);
+
+               if (active_only) {
+                       /*
+                        * Skip planes related to inactive CRTCs. If the plane
+                        * is enabled use the state of the current CRTC. If the
+                        * plane is being disabled use the state of the old
+                        * CRTC to avoid skipping planes being disabled on an
+                        * active CRTC.
+                        */
+                       if (!disabling && !plane_crtc_active(plane->state))
+                               continue;
+                       if (disabling && !plane_crtc_active(old_plane_state))
+                               continue;
+               }
+
                /*
                 * Special-case disabling the plane if drivers support it.
                 */
-               if (drm_atomic_plane_disabling(plane, old_plane_state) &&
-                   funcs->atomic_disable)
+               if (disabling && funcs->atomic_disable)
                        funcs->atomic_disable(plane, old_plane_state);
-               else if (plane->state->crtc ||
-                        drm_atomic_plane_disabling(plane, old_plane_state))
+               else if (plane->state->crtc || disabling)
                        funcs->atomic_update(plane, old_plane_state);
        }
 
@@ -1216,6 +1267,9 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_flush)
                        continue;
 
+               if (active_only && !crtc->state->active)
+                       continue;
+
                funcs->atomic_flush(crtc, old_crtc_state);
        }
 }
@@ -1300,14 +1354,11 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
 
        for_each_plane_in_state(old_state, plane, plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
-               struct drm_framebuffer *old_fb;
 
                funcs = plane->helper_private;
 
-               old_fb = plane_state->fb;
-
-               if (old_fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, old_fb, plane_state);
+               if (funcs->cleanup_fb)
+                       funcs->cleanup_fb(plane, plane_state);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
@@ -1334,7 +1385,7 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
  *
  * 4. Actually commit the hardware state.
  *
- * 5. Call drm_atomic_helper_cleanup_planes with @state, which since step 3
+ * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3
  * contains the old state. Also do any other cleanup required with that state.
  */
 void drm_atomic_helper_swap_state(struct drm_device *dev,
@@ -1502,21 +1553,9 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+       ret = __drm_atomic_helper_disable_plane(plane, plane_state);
        if (ret != 0)
                goto fail;
-       drm_atomic_set_fb_for_plane(plane_state, NULL);
-       plane_state->crtc_x = 0;
-       plane_state->crtc_y = 0;
-       plane_state->crtc_h = 0;
-       plane_state->crtc_w = 0;
-       plane_state->src_x = 0;
-       plane_state->src_y = 0;
-       plane_state->src_h = 0;
-       plane_state->src_w = 0;
-
-       if (plane == plane->crtc->cursor)
-               state->legacy_cursor_update = true;
 
        ret = drm_atomic_commit(state);
        if (ret != 0)
@@ -1546,6 +1585,32 @@ backoff:
 }
 EXPORT_SYMBOL(drm_atomic_helper_disable_plane);
 
+/* just used from fb-helper and atomic-helper: */
+int __drm_atomic_helper_disable_plane(struct drm_plane *plane,
+               struct drm_plane_state *plane_state)
+{
+       int ret;
+
+       ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+       if (ret != 0)
+               return ret;
+
+       drm_atomic_set_fb_for_plane(plane_state, NULL);
+       plane_state->crtc_x = 0;
+       plane_state->crtc_y = 0;
+       plane_state->crtc_h = 0;
+       plane_state->crtc_w = 0;
+       plane_state->src_x = 0;
+       plane_state->src_y = 0;
+       plane_state->src_h = 0;
+       plane_state->src_w = 0;
+
+       if (plane->crtc && (plane == plane->crtc->cursor))
+               plane_state->state->legacy_cursor_update = true;
+
+       return 0;
+}
+
 static int update_output_state(struct drm_atomic_state *state,
                               struct drm_mode_set *set)
 {
@@ -1629,8 +1694,6 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set)
 {
        struct drm_atomic_state *state;
        struct drm_crtc *crtc = set->crtc;
-       struct drm_crtc_state *crtc_state;
-       struct drm_plane_state *primary_state;
        int ret = 0;
 
        state = drm_atomic_state_alloc(crtc->dev);
@@ -1639,17 +1702,54 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set)
 
        state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
 retry:
-       crtc_state = drm_atomic_get_crtc_state(state, crtc);
-       if (IS_ERR(crtc_state)) {
-               ret = PTR_ERR(crtc_state);
+       ret = __drm_atomic_helper_set_config(set, state);
+       if (ret != 0)
                goto fail;
-       }
 
-       primary_state = drm_atomic_get_plane_state(state, crtc->primary);
-       if (IS_ERR(primary_state)) {
-               ret = PTR_ERR(primary_state);
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
                goto fail;
-       }
+
+       /* Driver takes ownership of state on successful commit. */
+       return 0;
+fail:
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       drm_atomic_state_free(state);
+
+       return ret;
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       /*
+        * Someone might have exchanged the framebuffer while we dropped locks
+        * in the backoff code. We need to fix up the fb refcount tracking the
+        * core does for us.
+        */
+       crtc->primary->old_fb = crtc->primary->fb;
+
+       goto retry;
+}
+EXPORT_SYMBOL(drm_atomic_helper_set_config);
+
+/* just used from fb-helper and atomic-helper: */
+int __drm_atomic_helper_set_config(struct drm_mode_set *set,
+               struct drm_atomic_state *state)
+{
+       struct drm_crtc_state *crtc_state;
+       struct drm_plane_state *primary_state;
+       struct drm_crtc *crtc = set->crtc;
+       int ret;
+
+       crtc_state = drm_atomic_get_crtc_state(state, crtc);
+       if (IS_ERR(crtc_state))
+               return PTR_ERR(crtc_state);
+
+       primary_state = drm_atomic_get_plane_state(state, crtc->primary);
+       if (IS_ERR(primary_state))
+               return PTR_ERR(primary_state);
 
        if (!set->mode) {
                WARN_ON(set->fb);
@@ -1657,13 +1757,13 @@ retry:
 
                ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
                if (ret != 0)
-                       goto fail;
+                       return ret;
 
                crtc_state->active = false;
 
                ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
                if (ret != 0)
-                       goto fail;
+                       return ret;
 
                drm_atomic_set_fb_for_plane(primary_state, NULL);
 
@@ -1675,13 +1775,14 @@ retry:
 
        ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
        if (ret != 0)
-               goto fail;
+               return ret;
 
        crtc_state->active = true;
 
        ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
        if (ret != 0)
-               goto fail;
+               return ret;
+
        drm_atomic_set_fb_for_plane(primary_state, set->fb);
        primary_state->crtc_x = 0;
        primary_state->crtc_y = 0;
@@ -1695,35 +1796,10 @@ retry:
 commit:
        ret = update_output_state(state, set);
        if (ret)
-               goto fail;
-
-       ret = drm_atomic_commit(state);
-       if (ret != 0)
-               goto fail;
+               return ret;
 
-       /* Driver takes ownership of state on successful commit. */
        return 0;
-fail:
-       if (ret == -EDEADLK)
-               goto backoff;
-
-       drm_atomic_state_free(state);
-
-       return ret;
-backoff:
-       drm_atomic_state_clear(state);
-       drm_atomic_legacy_backoff(state);
-
-       /*
-        * Someone might have exchanged the framebuffer while we dropped locks
-        * in the backoff code. We need to fix up the fb refcount tracking the
-        * core does for us.
-        */
-       crtc->primary->old_fb = crtc->primary->fb;
-
-       goto retry;
 }
-EXPORT_SYMBOL(drm_atomic_helper_set_config);
 
 /**
  * drm_atomic_helper_crtc_set_property - helper for crtc properties
@@ -2332,6 +2408,84 @@ drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
 
+/**
+ * drm_atomic_helper_duplicate_state - duplicate an atomic state object
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * Makes a copy of the current atomic state by looping over all objects and
+ * duplicating their respective states.
+ *
+ * Note that this treats atomic state as persistent between save and restore.
+ * Drivers must make sure that this is possible and won't result in confusion
+ * or erroneous behaviour.
+ *
+ * Note that if callers haven't already acquired all modeset locks this might
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
+ *
+ * Returns:
+ * A pointer to the copy of the atomic state object on success or an
+ * ERR_PTR()-encoded error code on failure.
+ */
+struct drm_atomic_state *
+drm_atomic_helper_duplicate_state(struct drm_device *dev,
+                                 struct drm_modeset_acquire_ctx *ctx)
+{
+       struct drm_atomic_state *state;
+       struct drm_connector *conn;
+       struct drm_plane *plane;
+       struct drm_crtc *crtc;
+       int err = 0;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return ERR_PTR(-ENOMEM);
+
+       state->acquire_ctx = ctx;
+
+       drm_for_each_crtc(crtc, dev) {
+               struct drm_crtc_state *crtc_state;
+
+               crtc_state = drm_atomic_get_crtc_state(state, crtc);
+               if (IS_ERR(crtc_state)) {
+                       err = PTR_ERR(crtc_state);
+                       goto free;
+               }
+       }
+
+       drm_for_each_plane(plane, dev) {
+               struct drm_plane_state *plane_state;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       err = PTR_ERR(plane_state);
+                       goto free;
+               }
+       }
+
+       drm_for_each_connector(conn, dev) {
+               struct drm_connector_state *conn_state;
+
+               conn_state = drm_atomic_get_connector_state(state, conn);
+               if (IS_ERR(conn_state)) {
+                       err = PTR_ERR(conn_state);
+                       goto free;
+               }
+       }
+
+       /* clear the acquire context so that it isn't accidentally reused */
+       state->acquire_ctx = NULL;
+
+free:
+       if (err < 0) {
+               drm_atomic_state_free(state);
+               state = ERR_PTR(err);
+       }
+
+       return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
+
 /**
  * __drm_atomic_helper_connector_destroy_state - release connector state
  * @connector: connector object
index 33d877c..9b9c4b4 100644 (file)
@@ -538,7 +538,12 @@ EXPORT_SYMBOL(drm_framebuffer_reference);
  */
 void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
 {
-       struct drm_device *dev = fb->dev;
+       struct drm_device *dev;
+
+       if (!fb)
+               return;
+
+       dev = fb->dev;
 
        mutex_lock(&dev->mode_config.fb_lock);
        /* Mark fb as reaped and drop idr ref. */
@@ -589,12 +594,17 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
  */
 void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
-       struct drm_device *dev = fb->dev;
+       struct drm_device *dev;
        struct drm_crtc *crtc;
        struct drm_plane *plane;
        struct drm_mode_set set;
        int ret;
 
+       if (!fb)
+               return;
+
+       dev = fb->dev;
+
        WARN_ON(!list_empty(&fb->filp_head));
 
        /*
@@ -1509,7 +1519,7 @@ EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
  */
 int drm_mode_create_tv_properties(struct drm_device *dev,
                                  unsigned int num_modes,
-                                 char *modes[])
+                                 const char * const modes[])
 {
        struct drm_property *tv_selector;
        struct drm_property *tv_subconnector;
@@ -5732,7 +5742,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
         */
        WARN_ON(!list_empty(&dev->mode_config.fb_list));
        list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
-               drm_framebuffer_remove(fb);
+               drm_framebuffer_free(&fb->refcount);
        }
 
        list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
index 291734e..9535c5b 100644 (file)
@@ -424,6 +424,19 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
               I2C_FUNC_10BIT_ADDR;
 }
 
+static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg)
+{
+       /*
+        * In case of i2c defer or short i2c ack reply to a write,
+        * we need to switch to WRITE_STATUS_UPDATE to drain the
+        * rest of the message
+        */
+       if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) {
+               msg->request &= DP_AUX_I2C_MOT;
+               msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE;
+       }
+}
+
 #define AUX_PRECHARGE_LEN 10 /* 10 to 16 */
 #define AUX_SYNC_LEN (16 + 4) /* preamble + AUX_SYNC_END */
 #define AUX_STOP_LEN 4
@@ -579,6 +592,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                         * Both native ACK and I2C ACK replies received. We
                         * can assume the transfer was successful.
                         */
+                       if (ret != msg->size)
+                               drm_dp_i2c_msg_write_status_update(msg);
                        return ret;
 
                case DP_AUX_I2C_REPLY_NACK:
@@ -596,6 +611,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                        if (defer_i2c < 7)
                                defer_i2c++;
                        usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+                       drm_dp_i2c_msg_write_status_update(msg);
+
                        continue;
 
                default:
@@ -608,6 +625,14 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
        return -EREMOTEIO;
 }
 
+static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
+                                      const struct i2c_msg *i2c_msg)
+{
+       msg->request = (i2c_msg->flags & I2C_M_RD) ?
+               DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
+       msg->request |= DP_AUX_I2C_MOT;
+}
+
 /*
  * Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
  *
@@ -661,10 +686,7 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
 
        for (i = 0; i < num; i++) {
                msg.address = msgs[i].addr;
-               msg.request = (msgs[i].flags & I2C_M_RD) ?
-                       DP_AUX_I2C_READ :
-                       DP_AUX_I2C_WRITE;
-               msg.request |= DP_AUX_I2C_MOT;
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
                /* Send a bare address packet to start the transaction.
                 * Zero sized messages specify an address only (bare
                 * address) transaction.
@@ -672,6 +694,13 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                msg.buffer = NULL;
                msg.size = 0;
                err = drm_dp_i2c_do_msg(aux, &msg);
+
+               /*
+                * Reset msg.request in case in case it got
+                * changed into a WRITE_STATUS_UPDATE.
+                */
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
                if (err < 0)
                        break;
                /* We want each transaction to be as large as possible, but
@@ -684,6 +713,13 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                        msg.size = min(transfer_size, msgs[i].len - j);
 
                        err = drm_dp_i2c_drain_msg(aux, &msg);
+
+                       /*
+                        * Reset msg.request in case in case it got
+                        * changed into a WRITE_STATUS_UPDATE.
+                        */
+                       drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
                        if (err < 0)
                                break;
                        transfer_size = err;
index 53d09a1..9ad823f 100644 (file)
@@ -55,7 +55,6 @@ module_param_named(debug, drm_debug, int, 0600);
 static DEFINE_SPINLOCK(drm_minor_lock);
 static struct idr drm_minors_idr;
 
-struct class *drm_class;
 static struct dentry *drm_debugfs_root;
 
 void drm_err(const char *format, ...)
@@ -566,6 +565,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
                ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL);
                if (ret)
                        goto err_minors;
+
+               WARN_ON(driver->suspend || driver->resume);
        }
 
        if (drm_core_check_feature(dev, DRIVER_RENDER)) {
@@ -839,10 +840,9 @@ static int __init drm_core_init(void)
        if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
                goto err_p1;
 
-       drm_class = drm_sysfs_create(THIS_MODULE, "drm");
-       if (IS_ERR(drm_class)) {
+       ret = drm_sysfs_init();
+       if (ret < 0) {
                printk(KERN_ERR "DRM: Error creating drm class.\n");
-               ret = PTR_ERR(drm_class);
                goto err_p2;
        }
 
index 05bb731..d895556 100644 (file)
@@ -2044,7 +2044,7 @@ mode_in_range(const struct drm_display_mode *mode, struct edid *edid,
 static bool valid_inferred_mode(const struct drm_connector *connector,
                                const struct drm_display_mode *mode)
 {
-       struct drm_display_mode *m;
+       const struct drm_display_mode *m;
        bool ok = false;
 
        list_for_each_entry(m, &connector->probed_modes, head) {
@@ -3361,7 +3361,7 @@ EXPORT_SYMBOL(drm_edid_to_speaker_allocation);
  * the sink doesn't support audio or video.
  */
 int drm_av_sync_delay(struct drm_connector *connector,
-                     struct drm_display_mode *mode)
+                     const struct drm_display_mode *mode)
 {
        int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
        int a, v;
@@ -3396,7 +3396,6 @@ EXPORT_SYMBOL(drm_av_sync_delay);
 /**
  * drm_select_eld - select one ELD from multiple HDMI/DP sinks
  * @encoder: the encoder just changed display mode
- * @mode: the adjusted display mode
  *
  * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
  * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
@@ -3404,8 +3403,7 @@ EXPORT_SYMBOL(drm_av_sync_delay);
  * Return: The connector associated with the first HDMI/DP sink that has ELD
  * attached to it.
  */
-struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
-                                    struct drm_display_mode *mode)
+struct drm_connector *drm_select_eld(struct drm_encoder *encoder)
 {
        struct drm_connector *connector;
        struct drm_device *dev = encoder->dev;
index c5605fe..698b8c3 100644 (file)
@@ -32,7 +32,7 @@ MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
        "from built-in data or /lib/firmware instead. ");
 
 #define GENERIC_EDIDS 6
-static const char *generic_edid_name[GENERIC_EDIDS] = {
+static const char * const generic_edid_name[GENERIC_EDIDS] = {
        "edid/800x600.bin",
        "edid/1024x768.bin",
        "edid/1280x1024.bin",
@@ -264,20 +264,43 @@ out:
 int drm_load_edid_firmware(struct drm_connector *connector)
 {
        const char *connector_name = connector->name;
-       char *edidname = edid_firmware, *last, *colon;
+       char *edidname, *last, *colon, *fwstr, *edidstr, *fallback = NULL;
        int ret;
        struct edid *edid;
 
-       if (*edidname == '\0')
+       if (edid_firmware[0] == '\0')
                return 0;
 
-       colon = strchr(edidname, ':');
-       if (colon != NULL) {
-               if (strncmp(connector_name, edidname, colon - edidname))
-                       return 0;
-               edidname = colon + 1;
-               if (*edidname == '\0')
+       /*
+        * If there are multiple edid files specified and separated
+        * by commas, search through the list looking for one that
+        * matches the connector.
+        *
+        * If there's one or more that don't't specify a connector, keep
+        * the last one found one as a fallback.
+        */
+       fwstr = kstrdup(edid_firmware, GFP_KERNEL);
+       edidstr = fwstr;
+
+       while ((edidname = strsep(&edidstr, ","))) {
+               colon = strchr(edidname, ':');
+               if (colon != NULL) {
+                       if (strncmp(connector_name, edidname, colon - edidname))
+                               continue;
+                       edidname = colon + 1;
+                       break;
+               }
+
+               if (*edidname != '\0') /* corner case: multiple ',' */
+                       fallback = edidname;
+       }
+
+       if (!edidname) {
+               if (!fallback) {
+                       kfree(fwstr);
                        return 0;
+               }
+               edidname = fallback;
        }
 
        last = edidname + strlen(edidname) - 1;
@@ -285,6 +308,8 @@ int drm_load_edid_firmware(struct drm_connector *connector)
                *last = '\0';
 
        edid = edid_load(connector, edidname, connector_name);
+       kfree(fwstr);
+
        if (IS_ERR_OR_NULL(edid))
                return 0;
 
index 418d299..64fc5ca 100644 (file)
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+
+static bool drm_fbdev_emulation = true;
+module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
+MODULE_PARM_DESC(fbdev_emulation,
+                "Enable legacy fbdev emulation [default=true]");
 
 static LIST_HEAD(kernel_fb_helper_list);
 
@@ -99,6 +106,9 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
        struct drm_connector *connector;
        int i;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&dev->mode_config.mutex);
        drm_for_each_connector(connector, dev) {
                struct drm_fb_helper_connector *fb_helper_connector;
@@ -129,6 +139,9 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_
        struct drm_fb_helper_connector **temp;
        struct drm_fb_helper_connector *fb_helper_connector;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
        if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
                temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
@@ -184,6 +197,9 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
        struct drm_fb_helper_connector *fb_helper_connector;
        int i, j;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
 
        for (i = 0; i < fb_helper->connector_count; i++) {
@@ -320,15 +336,83 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_debug_leave);
 
-static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
+static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
+{
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_plane *plane;
+       struct drm_atomic_state *state;
+       int i, ret;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       drm_for_each_plane(plane, dev) {
+               struct drm_plane_state *plane_state;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       ret = PTR_ERR(plane_state);
+                       goto fail;
+               }
+
+               ret = drm_atomic_plane_set_property(plane, plane_state,
+                               dev->mode_config.rotation_property,
+                               BIT(DRM_ROTATE_0));
+               if (ret != 0)
+                       goto fail;
+
+               /* disable non-primary: */
+               if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+                       continue;
+
+               ret = __drm_atomic_helper_disable_plane(plane, plane_state);
+               if (ret != 0)
+                       goto fail;
+       }
+
+       for(i = 0; i < fb_helper->crtc_count; i++) {
+               struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+
+               ret = __drm_atomic_helper_set_config(mode_set, state);
+               if (ret != 0)
+                       goto fail;
+       }
+
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
+               goto fail;
+
+       return 0;
+
+fail:
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       drm_atomic_state_free(state);
+
+       return ret;
+
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       goto retry;
+}
+
+static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
        struct drm_plane *plane;
-       bool error = false;
        int i;
 
        drm_warn_on_modeset_not_all_locked(dev);
 
+       if (fb_helper->atomic)
+               return restore_fbdev_mode_atomic(fb_helper);
+
        drm_for_each_plane(plane, dev) {
                if (plane->type != DRM_PLANE_TYPE_PRIMARY)
                        drm_plane_force_disable(plane);
@@ -348,14 +432,15 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
                if (crtc->funcs->cursor_set) {
                        ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
                        if (ret)
-                               error = true;
+                               return ret;
                }
 
                ret = drm_mode_set_config_internal(mode_set);
                if (ret)
-                       error = true;
+                       return ret;
        }
-       return error;
+
+       return 0;
 }
 
 /**
@@ -365,12 +450,18 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper)
  * This should be called from driver's drm ->lastclose callback
  * when implementing an fbcon on top of kms using this helper. This ensures that
  * the user isn't greeted with a black screen when e.g. X dies.
+ *
+ * RETURNS:
+ * Zero if everything went ok, negative error code otherwise.
  */
-bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
 {
        struct drm_device *dev = fb_helper->dev;
-       bool ret;
-       bool do_delayed = false;
+       bool do_delayed;
+       int ret;
+
+       if (!drm_fbdev_emulation)
+               return -ENODEV;
 
        drm_modeset_lock_all(dev);
        ret = restore_fbdev_mode(fb_helper);
@@ -588,6 +679,9 @@ int drm_fb_helper_init(struct drm_device *dev,
        struct drm_crtc *crtc;
        int i;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        if (!max_conn_count)
                return -EINVAL;
 
@@ -621,6 +715,8 @@ int drm_fb_helper_init(struct drm_device *dev,
                i++;
        }
 
+       fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
+
        return 0;
 out_free:
        drm_fb_helper_crtc_free(fb_helper);
@@ -710,6 +806,9 @@ EXPORT_SYMBOL(drm_fb_helper_release_fbi);
 
 void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
 {
+       if (!drm_fbdev_emulation)
+               return;
+
        if (!list_empty(&fb_helper->kernel_fb_list)) {
                list_del(&fb_helper->kernel_fb_list);
                if (list_empty(&kernel_fb_helper_list)) {
@@ -1118,6 +1217,57 @@ int drm_fb_helper_set_par(struct fb_info *info)
 }
 EXPORT_SYMBOL(drm_fb_helper_set_par);
 
+static int pan_display_atomic(struct fb_var_screeninfo *var,
+               struct fb_info *info)
+{
+       struct drm_fb_helper *fb_helper = info->par;
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_atomic_state *state;
+       int i, ret;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = dev->mode_config.acquire_ctx;
+retry:
+       for(i = 0; i < fb_helper->crtc_count; i++) {
+               struct drm_mode_set *mode_set;
+
+               mode_set = &fb_helper->crtc_info[i].mode_set;
+
+               mode_set->x = var->xoffset;
+               mode_set->y = var->yoffset;
+
+               ret = __drm_atomic_helper_set_config(mode_set, state);
+               if (ret != 0)
+                       goto fail;
+       }
+
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
+               goto fail;
+
+       info->var.xoffset = var->xoffset;
+       info->var.yoffset = var->yoffset;
+
+       return 0;
+
+fail:
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       drm_atomic_state_free(state);
+
+       return ret;
+
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       goto retry;
+}
+
 /**
  * drm_fb_helper_pan_display - implementation for ->fb_pan_display
  * @var: updated screen information
@@ -1141,6 +1291,11 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                return -EBUSY;
        }
 
+       if (fb_helper->atomic) {
+               ret = pan_display_atomic(var, info);
+               goto unlock;
+       }
+
        for (i = 0; i < fb_helper->crtc_count; i++) {
                modeset = &fb_helper->crtc_info[i].mode_set;
 
@@ -1155,6 +1310,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
                        }
                }
        }
+unlock:
        drm_modeset_unlock_all(dev);
        return ret;
 }
@@ -1930,6 +2086,9 @@ int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
        struct drm_device *dev = fb_helper->dev;
        int count = 0;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&dev->mode_config.mutex);
        count = drm_fb_helper_probe_connector_modes(fb_helper,
                                                    dev->mode_config.max_width,
@@ -1973,6 +2132,9 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
        struct drm_device *dev = fb_helper->dev;
        u32 max_width, max_height;
 
+       if (!drm_fbdev_emulation)
+               return 0;
+
        mutex_lock(&fb_helper->dev->mode_config.mutex);
        if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
                fb_helper->delayed_hotplug = true;
index 059af01..43cbda3 100644 (file)
@@ -73,7 +73,7 @@ int drm_authmagic(struct drm_device *dev, void *data,
 /* drm_sysfs.c */
 extern struct class *drm_class;
 
-struct class *drm_sysfs_create(struct module *owner, char *name);
+int drm_sysfs_init(void);
 void drm_sysfs_destroy(void);
 struct device *drm_sysfs_minor_alloc(struct drm_minor *minor);
 int drm_sysfs_connector_add(struct drm_connector *connector);
index 3427b11..04de6fd 100644 (file)
@@ -267,12 +267,12 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
        if (adj_end > end)
                adj_end = end;
 
-       if (flags & DRM_MM_CREATE_TOP)
-               adj_start = adj_end - size;
-
        if (mm->color_adjust)
                mm->color_adjust(hole_node, color, &adj_start, &adj_end);
 
+       if (flags & DRM_MM_CREATE_TOP)
+               adj_start = adj_end - size;
+
        if (alignment) {
                u64 tmp = adj_start;
                unsigned rem;
index fba321c..6675b14 100644 (file)
@@ -307,6 +307,8 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
        WARN_ON(ctx->contended);
 
        if (ctx->trylock_only) {
+               lockdep_assert_held(&ctx->ww_ctx);
+
                if (!ww_mutex_trylock(&lock->mutex))
                        return -EBUSY;
                else
index 5e5a07a..d384ebc 100644 (file)
@@ -426,7 +426,7 @@ int drm_plane_helper_commit(struct drm_plane *plane,
 
        if (plane_funcs->prepare_fb && plane_state->fb &&
            plane_state->fb != old_fb) {
-               ret = plane_funcs->prepare_fb(plane, plane_state->fb,
+               ret = plane_funcs->prepare_fb(plane,
                                              plane_state);
                if (ret)
                        goto out;
@@ -479,8 +479,8 @@ int drm_plane_helper_commit(struct drm_plane *plane,
                ret = 0;
        }
 
-       if (plane_funcs->cleanup_fb && old_fb)
-               plane_funcs->cleanup_fb(plane, old_fb, plane_state);
+       if (plane_funcs->cleanup_fb)
+               plane_funcs->cleanup_fb(plane, plane_state);
 out:
        if (plane_state) {
                if (plane->funcs->atomic_destroy_state)
index 0f6cd33..f08873f 100644 (file)
@@ -30,6 +30,8 @@ static struct device_type drm_sysfs_device_minor = {
        .name = "drm_minor"
 };
 
+struct class *drm_class;
+
 /**
  * __drm_class_suspend - internal DRM class suspend routine
  * @dev: Linux device to suspend
@@ -112,41 +114,34 @@ static CLASS_ATTR_STRING(version, S_IRUGO,
                CORE_DATE);
 
 /**
- * drm_sysfs_create - create a struct drm_sysfs_class structure
- * @owner: pointer to the module that is to "own" this struct drm_sysfs_class
- * @name: pointer to a string for the name of this class.
+ * 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.
  *
- * This is used to create DRM class pointer that can then be used
- * in calls to drm_sysfs_device_add().
+ * You must call drm_sysfs_destroy() to release the allocated resources.
  *
- * Note, the pointer created here is to be destroyed when finished by making a
- * call to drm_sysfs_destroy().
+ * Return: 0 on success, negative error code on failure.
  */
-struct class *drm_sysfs_create(struct module *owner, char *name)
+int drm_sysfs_init(void)
 {
-       struct class *class;
        int err;
 
-       class = class_create(owner, name);
-       if (IS_ERR(class)) {
-               err = PTR_ERR(class);
-               goto err_out;
-       }
-
-       class->pm = &drm_class_dev_pm_ops;
+       drm_class = class_create(THIS_MODULE, "drm");
+       if (IS_ERR(drm_class))
+               return PTR_ERR(drm_class);
 
-       err = class_create_file(class, &class_attr_version.attr);
-       if (err)
-               goto err_out_class;
+       drm_class->pm = &drm_class_dev_pm_ops;
 
-       class->devnode = drm_devnode;
-
-       return class;
+       err = class_create_file(drm_class, &class_attr_version.attr);
+       if (err) {
+               class_destroy(drm_class);
+               drm_class = NULL;
+               return err;
+       }
 
-err_out_class:
-       class_destroy(class);
-err_out:
-       return ERR_PTR(err);
+       drm_class->devnode = drm_devnode;
+       return 0;
 }
 
 /**
@@ -156,7 +151,7 @@ err_out:
  */
 void drm_sysfs_destroy(void)
 {
-       if ((drm_class == NULL) || (IS_ERR(drm_class)))
+       if (IS_ERR_OR_NULL(drm_class))
                return;
        class_remove_file(drm_class, &class_attr_version.attr);
        class_destroy(drm_class);
index 831d2e4..f0a5839 100644 (file)
@@ -105,7 +105,7 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
                atomic_inc(&exynos_crtc->pending_update);
        }
 
-       drm_atomic_helper_commit_planes(dev, state);
+       drm_atomic_helper_commit_planes(dev, state, false);
 
        exynos_atomic_wait_for_commit(state);
 
index 82be6b8..d78f8df 100644 (file)
@@ -190,14 +190,12 @@ set_failed:
 
 static void
 fsl_dcu_drm_plane_cleanup_fb(struct drm_plane *plane,
-                            struct drm_framebuffer *fb,
                             const struct drm_plane_state *new_state)
 {
 }
 
 static int
 fsl_dcu_drm_plane_prepare_fb(struct drm_plane *plane,
-                            struct drm_framebuffer *fb,
                             const struct drm_plane_state *new_state)
 {
        return 0;
index 51fa323..d9a72c9 100644 (file)
@@ -119,8 +119,8 @@ static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
        struct ch7006_encoder_params *params = &priv->params;
        struct ch7006_state *state = &priv->state;
        uint8_t *regs = state->regs;
-       struct ch7006_mode *mode = priv->mode;
-       struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+       const struct ch7006_mode *mode = priv->mode;
+       const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
        int start_active;
 
        ch7006_dbg(client, "\n");
@@ -226,7 +226,7 @@ static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
                                    struct drm_connector *connector)
 {
        struct ch7006_priv *priv = to_ch7006_priv(encoder);
-       struct ch7006_mode *mode;
+       const struct ch7006_mode *mode;
        int n = 0;
 
        for (mode = ch7006_modes; mode->mode.clock; mode++) {
index 9b83574..bb5f67f 100644 (file)
@@ -26,7 +26,7 @@
 
 #include "ch7006_priv.h"
 
-char *ch7006_tv_norm_names[] = {
+const char * const ch7006_tv_norm_names[] = {
        [TV_NORM_PAL] = "PAL",
        [TV_NORM_PAL_M] = "PAL-M",
        [TV_NORM_PAL_N] = "PAL-N",
@@ -46,7 +46,7 @@ char *ch7006_tv_norm_names[] = {
                .vtotal = 625,                                  \
                .hvirtual = 810
 
-struct ch7006_tv_norm_info ch7006_tv_norms[] = {
+const struct ch7006_tv_norm_info ch7006_tv_norms[] = {
        [TV_NORM_NTSC_M] = {
                NTSC_LIKE_TIMINGS,
                .black_level = 0.339 * fixed1,
@@ -142,7 +142,7 @@ struct ch7006_tv_norm_info ch7006_tv_norms[] = {
 
 #define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
 
-struct ch7006_mode ch7006_modes[] = {
+const struct ch7006_mode ch7006_modes[] = {
        MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
        MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
        MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
@@ -171,11 +171,11 @@ struct ch7006_mode ch7006_modes[] = {
        {}
 };
 
-struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
-                                      const struct drm_display_mode *drm_mode)
+const struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+                                            const struct drm_display_mode *drm_mode)
 {
        struct ch7006_priv *priv = to_ch7006_priv(encoder);
-       struct ch7006_mode *mode;
+       const struct ch7006_mode *mode;
 
        for (mode = ch7006_modes; mode->mode.clock; mode++) {
 
@@ -202,7 +202,7 @@ void ch7006_setup_levels(struct drm_encoder *encoder)
        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
        struct ch7006_priv *priv = to_ch7006_priv(encoder);
        uint8_t *regs = priv->state.regs;
-       struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+       const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
        int gain;
        int black_level;
 
@@ -233,8 +233,8 @@ void ch7006_setup_subcarrier(struct drm_encoder *encoder)
        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
        struct ch7006_priv *priv = to_ch7006_priv(encoder);
        struct ch7006_state *state = &priv->state;
-       struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
-       struct ch7006_mode *mode = priv->mode;
+       const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+       const struct ch7006_mode *mode = priv->mode;
        uint32_t subc_inc;
 
        subc_inc = round_fixed((mode->subc_coeff >> 8)
@@ -257,7 +257,7 @@ void ch7006_setup_pll(struct drm_encoder *encoder)
        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
        struct ch7006_priv *priv = to_ch7006_priv(encoder);
        uint8_t *regs = priv->state.regs;
-       struct ch7006_mode *mode = priv->mode;
+       const struct ch7006_mode *mode = priv->mode;
        int n, best_n = 0;
        int m, best_m = 0;
        int freq, best_freq = 0;
@@ -328,9 +328,9 @@ void ch7006_setup_properties(struct drm_encoder *encoder)
        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
        struct ch7006_priv *priv = to_ch7006_priv(encoder);
        struct ch7006_state *state = &priv->state;
-       struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
-       struct ch7006_mode *ch_mode = priv->mode;
-       struct drm_display_mode *mode = &ch_mode->mode;
+       const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+       const struct ch7006_mode *ch_mode = priv->mode;
+       const struct drm_display_mode *mode = &ch_mode->mode;
        uint8_t *regs = state->regs;
        int flicker, contrast, hpos, vpos;
        uint64_t scale, aspect;
index ce57784..dc6414a 100644 (file)
@@ -78,7 +78,7 @@ struct ch7006_state {
 
 struct ch7006_priv {
        struct ch7006_encoder_params params;
-       struct ch7006_mode *mode;
+       const struct ch7006_mode *mode;
 
        struct ch7006_state state;
        struct ch7006_state saved_state;
@@ -106,12 +106,12 @@ extern int ch7006_debug;
 extern char *ch7006_tv_norm;
 extern int ch7006_scale;
 
-extern char *ch7006_tv_norm_names[];
-extern struct ch7006_tv_norm_info ch7006_tv_norms[];
-extern struct ch7006_mode ch7006_modes[];
+extern const char * const ch7006_tv_norm_names[];
+extern const struct ch7006_tv_norm_info ch7006_tv_norms[];
+extern const struct ch7006_mode ch7006_modes[];
 
-struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
-                                      const struct drm_display_mode *drm_mode);
+const struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+                                            const struct drm_display_mode *drm_mode);
 
 void ch7006_setup_levels(struct drm_encoder *encoder);
 void ch7006_setup_subcarrier(struct drm_encoder *encoder);
index 89c1a8c..f73de0b 100644 (file)
@@ -404,7 +404,7 @@ void intel_audio_codec_enable(struct intel_encoder *intel_encoder)
        struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
        enum port port = intel_dig_port->port;
 
-       connector = drm_select_eld(encoder, mode);
+       connector = drm_select_eld(encoder);
        if (!connector)
                return;
 
index 8cc9264..6aa7d12 100644 (file)
@@ -13313,10 +13313,10 @@ static void intel_shared_dpll_init(struct drm_device *dev)
  */
 int
 intel_prepare_plane_fb(struct drm_plane *plane,
-                      struct drm_framebuffer *fb,
                       const struct drm_plane_state *new_state)
 {
        struct drm_device *dev = plane->dev;
+       struct drm_framebuffer *fb = new_state->fb;
        struct intel_plane *intel_plane = to_intel_plane(plane);
        struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        struct drm_i915_gem_object *old_obj = intel_fb_obj(plane->fb);
@@ -13354,19 +13354,18 @@ intel_prepare_plane_fb(struct drm_plane *plane,
  */
 void
 intel_cleanup_plane_fb(struct drm_plane *plane,
-                      struct drm_framebuffer *fb,
                       const struct drm_plane_state *old_state)
 {
        struct drm_device *dev = plane->dev;
-       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+       struct drm_i915_gem_object *obj = intel_fb_obj(old_state->fb);
 
-       if (WARN_ON(!obj))
+       if (!obj)
                return;
 
        if (plane->type != DRM_PLANE_TYPE_CURSOR ||
            !INTEL_INFO(dev)->cursor_needs_physical) {
                mutex_lock(&dev->struct_mutex);
-               intel_unpin_fb_obj(fb, old_state);
+               intel_unpin_fb_obj(old_state->fb, old_state);
                mutex_unlock(&dev->struct_mutex);
        }
 }
index 0a2e33f..7bb96d5 100644 (file)
@@ -974,6 +974,7 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
        switch (msg->request & ~DP_AUX_I2C_MOT) {
        case DP_AUX_NATIVE_WRITE:
        case DP_AUX_I2C_WRITE:
+       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
                txsize = msg->size ? HEADER_SIZE + msg->size : BARE_ADDRESS_SIZE;
                rxsize = 2; /* 0 or 1 data bytes */
 
index 2b9e6f9..bfd1204 100644 (file)
@@ -1038,10 +1038,8 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe);
 void intel_finish_page_flip_plane(struct drm_device *dev, int plane);
 void intel_check_page_flip(struct drm_device *dev, int pipe);
 int intel_prepare_plane_fb(struct drm_plane *plane,
-                          struct drm_framebuffer *fb,
                           const struct drm_plane_state *new_state);
 void intel_cleanup_plane_fb(struct drm_plane *plane,
-                           struct drm_framebuffer *fb,
                            const struct drm_plane_state *old_state);
 int intel_plane_atomic_get_property(struct drm_plane *plane,
                                    const struct drm_plane_state *state,
index 8c6a6fa..ab2b856 100644 (file)
@@ -689,6 +689,8 @@ int intel_fbdev_init(struct drm_device *dev)
                return ret;
        }
 
+       ifbdev->helper.atomic = true;
+
        dev_priv->fbdev = ifbdev;
        INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker);
 
index 0568ae6..590ceab 100644 (file)
@@ -1579,7 +1579,7 @@ intel_tv_init(struct drm_device *dev)
        struct intel_encoder *intel_encoder;
        struct intel_connector *intel_connector;
        u32 tv_dac_on, tv_dac_off, save_tv_dac;
-       char *tv_format_names[ARRAY_SIZE(tv_modes)];
+       const char *tv_format_names[ARRAY_SIZE(tv_modes)];
        int i, initial_mode = 0;
 
        if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
@@ -1677,7 +1677,7 @@ intel_tv_init(struct drm_device *dev)
 
        /* Create TV properties then attach current values */
        for (i = 0; i < ARRAY_SIZE(tv_modes); i++)
-               tv_format_names[i] = (char *)tv_modes[i].name;
+               tv_format_names[i] = tv_modes[i].name;
        drm_mode_create_tv_properties(dev,
                                      ARRAY_SIZE(tv_modes),
                                      tv_format_names);
index e9dee36..30d57e7 100644 (file)
@@ -99,22 +99,28 @@ static const struct drm_plane_funcs mdp4_plane_funcs = {
 };
 
 static int mdp4_plane_prepare_fb(struct drm_plane *plane,
-               struct drm_framebuffer *fb,
                const struct drm_plane_state *new_state)
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
        struct mdp4_kms *mdp4_kms = get_kms(plane);
+       struct drm_framebuffer *fb = new_state->fb;
+
+       if (!fb)
+               return 0;
 
        DBG("%s: prepare: FB[%u]", mdp4_plane->name, fb->base.id);
        return msm_framebuffer_prepare(fb, mdp4_kms->id);
 }
 
 static void mdp4_plane_cleanup_fb(struct drm_plane *plane,
-               struct drm_framebuffer *fb,
                const struct drm_plane_state *old_state)
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
        struct mdp4_kms *mdp4_kms = get_kms(plane);
+       struct drm_framebuffer *fb = old_state->fb;
+
+       if (!fb)
+               return;
 
        DBG("%s: cleanup: FB[%u]", mdp4_plane->name, fb->base.id);
        msm_framebuffer_cleanup(fb, mdp4_kms->id);
index 07fb62f..a0f5ff0 100644 (file)
@@ -250,22 +250,28 @@ static const struct drm_plane_funcs mdp5_plane_funcs = {
 };
 
 static int mdp5_plane_prepare_fb(struct drm_plane *plane,
-               struct drm_framebuffer *fb,
                const struct drm_plane_state *new_state)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
        struct mdp5_kms *mdp5_kms = get_kms(plane);
+       struct drm_framebuffer *fb = new_state->fb;
+
+       if (!new_state->fb)
+               return 0;
 
        DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id);
        return msm_framebuffer_prepare(fb, mdp5_kms->id);
 }
 
 static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
-               struct drm_framebuffer *fb,
                const struct drm_plane_state *old_state)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
        struct mdp5_kms *mdp5_kms = get_kms(plane);
+       struct drm_framebuffer *fb = old_state->fb;
+
+       if (!fb)
+               return;
 
        DBG("%s: cleanup: FB[%u]", mdp5_plane->name, fb->base.id);
        msm_framebuffer_cleanup(fb, mdp5_kms->id);
index 1ceb4f2..7eb253b 100644 (file)
@@ -125,7 +125,7 @@ static void complete_commit(struct msm_commit *c)
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state);
+       drm_atomic_helper_commit_planes(dev, state, false);
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
index 08c6f5e..903c473 100644 (file)
@@ -32,7 +32,7 @@
 #include "hw.h"
 #include "tvnv17.h"
 
-char *nv17_tv_norm_names[NUM_TV_NORMS] = {
+const char * const nv17_tv_norm_names[NUM_TV_NORMS] = {
        [TV_NORM_PAL] = "PAL",
        [TV_NORM_PAL_M] = "PAL-M",
        [TV_NORM_PAL_N] = "PAL-N",
index 459910b..1b07521 100644 (file)
@@ -85,7 +85,7 @@ struct nv17_tv_encoder {
 #define to_tv_enc(x) container_of(nouveau_encoder(x),          \
                                  struct nv17_tv_encoder, base)
 
-extern char *nv17_tv_norm_names[NUM_TV_NORMS];
+extern const char * const nv17_tv_norm_names[NUM_TV_NORMS];
 
 extern struct nv17_tv_norm_params {
        enum {
index 419c2e4..a5f9d8b 100644 (file)
@@ -96,7 +96,7 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit)
        dispc_runtime_get();
 
        drm_atomic_helper_commit_modeset_disables(dev, old_state);
-       drm_atomic_helper_commit_planes(dev, old_state);
+       drm_atomic_helper_commit_planes(dev, old_state, false);
        drm_atomic_helper_commit_modeset_enables(dev, old_state);
 
        omap_atomic_wait_for_completion(dev, old_state);
index 0989046..09e363b 100644 (file)
@@ -60,17 +60,19 @@ to_omap_plane_state(struct drm_plane_state *state)
 }
 
 static int omap_plane_prepare_fb(struct drm_plane *plane,
-                                struct drm_framebuffer *fb,
                                 const struct drm_plane_state *new_state)
 {
-       return omap_framebuffer_pin(fb);
+       if (!new_state->fb)
+               return 0;
+
+       return omap_framebuffer_pin(new_state->fb);
 }
 
 static void omap_plane_cleanup_fb(struct drm_plane *plane,
-                                 struct drm_framebuffer *fb,
                                  const struct drm_plane_state *old_state)
 {
-       omap_framebuffer_unpin(fb);
+       if (old_state->fb)
+               omap_framebuffer_unpin(old_state->fb);
 }
 
 static void omap_plane_atomic_update(struct drm_plane *plane,
index 9cd49c5..bd73b40 100644 (file)
@@ -179,6 +179,7 @@ radeon_dp_aux_transfer_atom(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
        switch (msg->request & ~DP_AUX_I2C_MOT) {
        case DP_AUX_NATIVE_WRITE:
        case DP_AUX_I2C_WRITE:
+       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
                /* The atom implementation only supports writes with a max payload of
                 * 12 bytes since it uses 4 bits for the total count (header + payload)
                 * in the parameter space.  The atom interface supports 16 byte
index 56518eb..ca12e8c 100644 (file)
@@ -456,7 +456,7 @@ static void rcar_du_atomic_complete(struct rcar_du_commit *commit)
        /* Apply the atomic update. */
        drm_atomic_helper_commit_modeset_disables(dev, old_state);
        drm_atomic_helper_commit_modeset_enables(dev, old_state);
-       drm_atomic_helper_commit_planes(dev, old_state);
+       drm_atomic_helper_commit_planes(dev, old_state, false);
 
        drm_atomic_helper_wait_for_vblanks(dev, old_state);
 
index 6f4af6a..9f85988 100644 (file)
@@ -59,7 +59,7 @@ static void sti_atomic_complete(struct sti_private *private,
         */
 
        drm_atomic_helper_commit_modeset_disables(drm, state);
-       drm_atomic_helper_commit_planes(drm, state);
+       drm_atomic_helper_commit_planes(drm, state, false);
        drm_atomic_helper_commit_modeset_enables(drm, state);
 
        drm_atomic_helper_wait_for_vblanks(drm, state);
index ddefb85..b4af4ab 100644 (file)
@@ -480,14 +480,12 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = {
 };
 
 static int tegra_plane_prepare_fb(struct drm_plane *plane,
-                                 struct drm_framebuffer *fb,
                                  const struct drm_plane_state *new_state)
 {
        return 0;
 }
 
 static void tegra_plane_cleanup_fb(struct drm_plane *plane,
-                                  struct drm_framebuffer *fb,
                                   const struct drm_plane_state *old_fb)
 {
 }
index 224a7dc..6aecb66 100644 (file)
@@ -119,6 +119,7 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
         */
        if (msg->size < 1) {
                switch (msg->request & ~DP_AUX_I2C_MOT) {
+               case DP_AUX_I2C_WRITE_STATUS_UPDATE:
                case DP_AUX_I2C_WRITE:
                case DP_AUX_I2C_READ:
                        value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY;
@@ -149,7 +150,7 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
 
                break;
 
-       case DP_AUX_I2C_STATUS:
+       case DP_AUX_I2C_WRITE_STATUS_UPDATE:
                if (msg->request & DP_AUX_I2C_MOT)
                        value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ;
                else
index 6d88cf1..2486bc2 100644 (file)
@@ -56,7 +56,7 @@ static void tegra_atomic_complete(struct tegra_drm *tegra,
         */
 
        drm_atomic_helper_commit_modeset_disables(drm, state);
-       drm_atomic_helper_commit_planes(drm, state);
+       drm_atomic_helper_commit_planes(drm, state, false);
        drm_atomic_helper_commit_modeset_enables(drm, state);
 
        drm_atomic_helper_wait_for_vblanks(drm, state);
index 2106066..b19a72f 100644 (file)
@@ -1,20 +1,31 @@
 /*
+ * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs
+ *
  * Copyright (c) 2010 Red Hat Inc.
  * Author : Dave Airlie <airlied@redhat.com>
  *
+ * Copyright (c) 2015 Lukas Wunner <lukas@wunner.de>
  *
- * Licensed under GPLv2
+ * 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, sublicense,
+ * 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:
  *
- * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
  *
- * Switcher interface - methods require for ATPX and DCM
- * - switchto - this throws the output MUX switch
- * - discrete_set_power - sets the power state for the discrete card
+ * 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 NONINFRINGEMENT.  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.
  *
- * GPU driver interface
- * - set_gpu_state - this should do the equiv of s/r for the card
- *                 - this should *not* set the discrete power state
- * - switch_check  - check if the device is in a position to switch now
  */
 
 #define pr_fmt(fmt) "vga_switcheroo: " fmt
 
 #include <linux/vgaarb.h>
 
+/**
+ * DOC: Overview
+ *
+ * vga_switcheroo is the Linux subsystem for laptop hybrid graphics.
+ * These come in two flavors:
+ *
+ * * muxed: Dual GPUs with a multiplexer chip to switch outputs between GPUs.
+ * * muxless: Dual GPUs but only one of them is connected to outputs.
+ *     The other one is merely used to offload rendering, its results
+ *     are copied over PCIe into the framebuffer. On Linux this is
+ *     supported with DRI PRIME.
+ *
+ * Hybrid graphics started to appear in the late Naughties and were initially
+ * all muxed. Newer laptops moved to a muxless architecture for cost reasons.
+ * A notable exception is the MacBook Pro which continues to use a mux.
+ * Muxes come with varying capabilities: Some switch only the panel, others
+ * can also switch external displays. Some switch all display pins at once
+ * while others can switch just the DDC lines. (To allow EDID probing
+ * for the inactive GPU.) Also, muxes are often used to cut power to the
+ * discrete GPU while it is not used.
+ *
+ * DRM drivers register GPUs with vga_switcheroo, these are heretoforth called
+ * clients. The mux is called the handler. Muxless machines also register a
+ * handler to control the power state of the discrete GPU, its ->switchto
+ * callback is a no-op for obvious reasons. The discrete GPU is often equipped
+ * with an HDA controller for the HDMI/DP audio signal, this will also
+ * register as a client so that vga_switcheroo can take care of the correct
+ * suspend/resume order when changing the discrete GPU's power state. In total
+ * there can thus be up to three clients: Two vga clients (GPUs) and one audio
+ * client (on the discrete GPU). The code is mostly prepared to support
+ * machines with more than two GPUs should they become available.
+ * The GPU to which the outputs are currently switched is called the
+ * active client in vga_switcheroo parlance. The GPU not in use is the
+ * inactive client.
+ */
+
+/**
+ * struct vga_switcheroo_client - registered client
+ * @pdev: client pci device
+ * @fb_info: framebuffer to which console is remapped on switching
+ * @pwr_state: current power state
+ * @ops: client callbacks
+ * @id: client identifier, see enum vga_switcheroo_client_id.
+ *     Determining the id requires the handler, so GPUs are initially
+ *     assigned -1 and later given their true id in vga_switcheroo_enable()
+ * @active: whether the outputs are currently switched to this client
+ * @driver_power_control: whether power state is controlled by the driver's
+ *     runtime pm. If true, writing ON and OFF to the vga_switcheroo debugfs
+ *     interface is a no-op so as not to interfere with runtime pm
+ * @list: client list
+ *
+ * Registered client. A client can be either a GPU or an audio device on a GPU.
+ * For audio clients, the @fb_info, @active and @driver_power_control members
+ * are bogus.
+ */
 struct vga_switcheroo_client {
        struct pci_dev *pdev;
        struct fb_info *fb_info;
@@ -44,10 +110,28 @@ struct vga_switcheroo_client {
        struct list_head list;
 };
 
+/*
+ * protects access to struct vgasr_priv
+ */
 static DEFINE_MUTEX(vgasr_mutex);
 
+/**
+ * struct vgasr_priv - vga_switcheroo private data
+ * @active: whether vga_switcheroo is enabled.
+ *     Prerequisite is the registration of two GPUs and a handler
+ * @delayed_switch_active: whether a delayed switch is pending
+ * @delayed_client_id: client to which a delayed switch is pending
+ * @debugfs_root: directory for vga_switcheroo debugfs interface
+ * @switch_file: file for vga_switcheroo debugfs interface
+ * @registered_clients: number of registered GPUs
+ *     (counting only vga clients, not audio clients)
+ * @clients: list of registered clients
+ * @handler: registered handler
+ *
+ * vga_switcheroo private data. Currently only one vga_switcheroo instance
+ * per system is supported.
+ */
 struct vgasr_priv {
-
        bool active;
        bool delayed_switch_active;
        enum vga_switcheroo_client_id delayed_client_id;
@@ -103,6 +187,15 @@ static void vga_switcheroo_enable(void)
        vgasr_priv.active = true;
 }
 
+/**
+ * vga_switcheroo_register_handler() - register handler
+ * @handler: handler callbacks
+ *
+ * Register handler. Enable vga_switcheroo if two vga clients have already
+ * registered.
+ *
+ * Return: 0 on success, -EINVAL if a handler was already registered.
+ */
 int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
 {
        mutex_lock(&vgasr_mutex);
@@ -121,6 +214,11 @@ int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
 }
 EXPORT_SYMBOL(vga_switcheroo_register_handler);
 
+/**
+ * vga_switcheroo_unregister_handler() - unregister handler
+ *
+ * Unregister handler. Disable vga_switcheroo.
+ */
 void vga_switcheroo_unregister_handler(void)
 {
        mutex_lock(&vgasr_mutex);
@@ -164,6 +262,19 @@ static int register_client(struct pci_dev *pdev,
        return 0;
 }
 
+/**
+ * vga_switcheroo_register_client - register vga client
+ * @pdev: client pci device
+ * @ops: client callbacks
+ * @driver_power_control: whether power state is controlled by the driver's
+ *     runtime pm
+ *
+ * Register vga client (GPU). Enable vga_switcheroo if another GPU and a
+ * handler have already registered. The power state of the client is assumed
+ * to be ON.
+ *
+ * Return: 0 on success, -ENOMEM on memory allocation error.
+ */
 int vga_switcheroo_register_client(struct pci_dev *pdev,
                                   const struct vga_switcheroo_client_ops *ops,
                                   bool driver_power_control)
@@ -174,6 +285,18 @@ int vga_switcheroo_register_client(struct pci_dev *pdev,
 }
 EXPORT_SYMBOL(vga_switcheroo_register_client);
 
+/**
+ * vga_switcheroo_register_audio_client - register audio client
+ * @pdev: client pci device
+ * @ops: client callbacks
+ * @id: client identifier, see enum vga_switcheroo_client_id
+ * @active: whether the audio device is fully initialized
+ *
+ * Register audio client (audio device on a GPU). The power state of the
+ * client is assumed to be ON.
+ *
+ * Return: 0 on success, -ENOMEM on memory allocation error.
+ */
 int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
                                         const struct vga_switcheroo_client_ops *ops,
                                         int id, bool active)
@@ -215,6 +338,15 @@ find_active_client(struct list_head *head)
        return NULL;
 }
 
+/**
+ * vga_switcheroo_get_client_state() - obtain power state of a given client
+ * @pdev: client pci device
+ *
+ * Obtain power state of a given client as seen from vga_switcheroo.
+ * The function is only called from hda_intel.c.
+ *
+ * Return: Power state.
+ */
 int vga_switcheroo_get_client_state(struct pci_dev *pdev)
 {
        struct vga_switcheroo_client *client;
@@ -228,6 +360,12 @@ int vga_switcheroo_get_client_state(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL(vga_switcheroo_get_client_state);
 
+/**
+ * vga_switcheroo_unregister_client() - unregister client
+ * @pdev: client pci device
+ *
+ * Unregister client. Disable vga_switcheroo if this is a vga client (GPU).
+ */
 void vga_switcheroo_unregister_client(struct pci_dev *pdev)
 {
        struct vga_switcheroo_client *client;
@@ -249,6 +387,14 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL(vga_switcheroo_unregister_client);
 
+/**
+ * vga_switcheroo_client_fb_set() - set framebuffer of a given client
+ * @pdev: client pci device
+ * @info: framebuffer
+ *
+ * Set framebuffer of a given client. The console will be remapped to this
+ * on switching.
+ */
 void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
                                 struct fb_info *info)
 {
@@ -262,6 +408,42 @@ void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
 }
 EXPORT_SYMBOL(vga_switcheroo_client_fb_set);
 
+/**
+ * DOC: Manual switching and manual power control
+ *
+ * In this mode of use, the file /sys/kernel/debug/vgaswitcheroo/switch
+ * can be read to retrieve the current vga_switcheroo state and commands
+ * can be written to it to change the state. The file appears as soon as
+ * two GPU drivers and one handler have registered with vga_switcheroo.
+ * The following commands are understood:
+ *
+ * * OFF: Power off the device not in use.
+ * * ON: Power on the device not in use.
+ * * IGD: Switch to the integrated graphics device.
+ *     Power on the integrated GPU if necessary, power off the discrete GPU.
+ *     Prerequisite is that no user space processes (e.g. Xorg, alsactl)
+ *     have opened device files of the GPUs or the audio client. If the
+ *     switch fails, the user may invoke lsof(8) or fuser(1) on /dev/dri/
+ *     and /dev/snd/controlC1 to identify processes blocking the switch.
+ * * DIS: Switch to the discrete graphics device.
+ * * DIGD: Delayed switch to the integrated graphics device.
+ *     This will perform the switch once the last user space process has
+ *     closed the device files of the GPUs and the audio client.
+ * * DDIS: Delayed switch to the discrete graphics device.
+ * * MIGD: Mux-only switch to the integrated graphics device.
+ *     Does not remap console or change the power state of either gpu.
+ *     If the integrated GPU is currently off, the screen will turn black.
+ *     If it is on, the screen will show whatever happens to be in VRAM.
+ *     Either way, the user has to blindly enter the command to switch back.
+ * * MDIS: Mux-only switch to the discrete graphics device.
+ *
+ * For GPUs whose power state is controlled by the driver's runtime pm,
+ * the ON and OFF commands are a no-op (see next section).
+ *
+ * For muxless machines, the IGD/DIS, DIGD/DDIS and MIGD/MDIS commands
+ * should not be used.
+ */
+
 static int vga_switcheroo_show(struct seq_file *m, void *v)
 {
        struct vga_switcheroo_client *client;
@@ -559,6 +741,16 @@ fail:
        return -1;
 }
 
+/**
+ * vga_switcheroo_process_delayed_switch() - helper for delayed switching
+ *
+ * Process a delayed switch if one is pending. DRM drivers should call this
+ * from their ->lastclose callback.
+ *
+ * Return: 0 on success. -EINVAL if no delayed switch is pending, if the client
+ * has unregistered in the meantime or if there are other clients blocking the
+ * switch. If the actual switch fails, an error is reported and 0 is returned.
+ */
 int vga_switcheroo_process_delayed_switch(void)
 {
        struct vga_switcheroo_client *client;
@@ -589,6 +781,39 @@ err:
 }
 EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
 
+/**
+ * DOC: Driver power control
+ *
+ * In this mode of use, the discrete GPU automatically powers up and down at
+ * the discretion of the driver's runtime pm. On muxed machines, the user may
+ * still influence the muxer state by way of the debugfs interface, however
+ * the ON and OFF commands become a no-op for the discrete GPU.
+ *
+ * This mode is the default on Nvidia HybridPower/Optimus and ATI PowerXpress.
+ * Specifying nouveau.runpm=0, radeon.runpm=0 or amdgpu.runpm=0 on the kernel
+ * command line disables it.
+ *
+ * When the driver decides to power up or down, it notifies vga_switcheroo
+ * thereof so that it can (a) power the audio device on the GPU up or down,
+ * and (b) update its internal power state representation for the device.
+ * This is achieved by vga_switcheroo_set_dynamic_switch().
+ *
+ * After the GPU has been suspended, the handler needs to be called to cut
+ * power to the GPU. Likewise it needs to reinstate power before the GPU
+ * can resume. This is achieved by vga_switcheroo_init_domain_pm_ops(),
+ * which augments the GPU's suspend/resume functions by the requisite
+ * calls to the handler.
+ *
+ * When the audio device resumes, the GPU needs to be woken. This is achieved
+ * by vga_switcheroo_init_domain_pm_optimus_hdmi_audio(), which augments the
+ * audio device's resume function.
+ *
+ * On muxed machines, if the mux is initially switched to the discrete GPU,
+ * the user ends up with a black screen when the GPU powers down after boot.
+ * As a workaround, the mux is forced to the integrated GPU on runtime suspend,
+ * cf. https://bugs.freedesktop.org/show_bug.cgi?id=75917
+ */
+
 static void vga_switcheroo_power_switch(struct pci_dev *pdev,
                                        enum vga_switcheroo_state state)
 {
@@ -607,8 +832,17 @@ static void vga_switcheroo_power_switch(struct pci_dev *pdev,
        vgasr_priv.handler->power_state(client->id, state);
 }
 
-/* force a PCI device to a certain state - mainly to turn off audio clients */
-
+/**
+ * vga_switcheroo_set_dynamic_switch() - helper for driver power control
+ * @pdev: client pci device
+ * @dynamic: new power state
+ *
+ * Helper for GPUs whose power state is controlled by the driver's runtime pm.
+ * When the driver decides to power up or down, it notifies vga_switcheroo
+ * thereof using this helper so that it can (a) power the audio device on
+ * the GPU up or down, and (b) update its internal power state representation
+ * for the device.
+ */
 void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev,
                                       enum vga_switcheroo_state dynamic)
 {
@@ -654,8 +888,18 @@ static int vga_switcheroo_runtime_resume(struct device *dev)
        return 0;
 }
 
-/* this version is for the case where the power switch is separate
-   to the device being powered down. */
+/**
+ * vga_switcheroo_init_domain_pm_ops() - helper for driver power control
+ * @dev: vga client device
+ * @domain: power domain
+ *
+ * Helper for GPUs whose power state is controlled by the driver's runtime pm.
+ * After the GPU has been suspended, the handler needs to be called to cut
+ * power to the GPU. Likewise it needs to reinstate power before the GPU
+ * can resume. To this end, this helper augments the suspend/resume functions
+ * by the requisite calls to the handler. It needs only be called on platforms
+ * where the power switch is separate to the device being powered down.
+ */
 int vga_switcheroo_init_domain_pm_ops(struct device *dev,
                                      struct dev_pm_domain *domain)
 {
@@ -709,6 +953,19 @@ static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev)
        return ret;
 }
 
+/**
+ * vga_switcheroo_init_domain_pm_optimus_hdmi_audio() - helper for driver
+ *     power control
+ * @dev: audio client device
+ * @domain: power domain
+ *
+ * Helper for GPUs whose power state is controlled by the driver's runtime pm.
+ * When the audio device resumes, the GPU needs to be woken. This helper
+ * augments the audio device's resume function to do that.
+ *
+ * Return: 0 on success, -EINVAL if no power management operations are
+ * defined for this device.
+ */
 int
 vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev,
                                                 struct dev_pm_domain *domain)
index 11266d1..8cba54a 100644 (file)
@@ -30,6 +30,8 @@
 
 #include <drm/drm_crtc.h>
 
+struct drm_atomic_state;
+
 int drm_atomic_helper_check_modeset(struct drm_device *dev,
                                struct drm_atomic_state *state);
 int drm_atomic_helper_check_planes(struct drm_device *dev,
@@ -55,7 +57,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                                     struct drm_atomic_state *state);
 void drm_atomic_helper_commit_planes(struct drm_device *dev,
-                                    struct drm_atomic_state *state);
+                                    struct drm_atomic_state *state,
+                                    bool active_only);
 void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
                                      struct drm_atomic_state *old_state);
 void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state);
@@ -72,7 +75,11 @@ int drm_atomic_helper_update_plane(struct drm_plane *plane,
                                   uint32_t src_x, uint32_t src_y,
                                   uint32_t src_w, uint32_t src_h);
 int drm_atomic_helper_disable_plane(struct drm_plane *plane);
+int __drm_atomic_helper_disable_plane(struct drm_plane *plane,
+               struct drm_plane_state *plane_state);
 int drm_atomic_helper_set_config(struct drm_mode_set *set);
+int __drm_atomic_helper_set_config(struct drm_mode_set *set,
+               struct drm_atomic_state *state);
 
 int drm_atomic_helper_crtc_set_property(struct drm_crtc *crtc,
                                        struct drm_property *property,
@@ -117,6 +124,9 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
                                           struct drm_connector_state *state);
 struct drm_connector_state *
 drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector);
+struct drm_atomic_state *
+drm_atomic_helper_duplicate_state(struct drm_device *dev,
+                                 struct drm_modeset_acquire_ctx *ctx);
 void
 __drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
                                            struct drm_connector_state *state);
index faaeff7..c0366e9 100644 (file)
@@ -210,8 +210,6 @@ struct drm_framebuffer {
        int flags;
        uint32_t pixel_format; /* fourcc format */
        struct list_head filp_head;
-       /* if you are using the helper */
-       void *helper_private;
 };
 
 struct drm_property_blob {
@@ -1390,7 +1388,7 @@ extern int drm_property_add_enum(struct drm_property *property, int index,
 extern int drm_mode_create_dvi_i_properties(struct drm_device *dev);
 extern int drm_mode_create_tv_properties(struct drm_device *dev,
                                         unsigned int num_modes,
-                                        char *modes[]);
+                                        const char * const modes[]);
 extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 extern int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
index 499e9f6..d0c8810 100644 (file)
@@ -46,7 +46,7 @@
 
 #define DP_AUX_I2C_WRITE               0x0
 #define DP_AUX_I2C_READ                        0x1
-#define DP_AUX_I2C_STATUS              0x2
+#define DP_AUX_I2C_WRITE_STATUS_UPDATE 0x2
 #define DP_AUX_I2C_MOT                 0x4
 #define DP_AUX_NATIVE_WRITE            0x8
 #define DP_AUX_NATIVE_READ             0x9
index 53c53c4..2af9769 100644 (file)
@@ -326,9 +326,8 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid);
 int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads);
 int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb);
 int drm_av_sync_delay(struct drm_connector *connector,
-                     struct drm_display_mode *mode);
-struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
-                                    struct drm_display_mode *mode);
+                     const struct drm_display_mode *mode);
+struct drm_connector *drm_select_eld(struct drm_encoder *encoder);
 int drm_load_edid_firmware(struct drm_connector *connector);
 
 int
index dbab462..87b090c 100644 (file)
@@ -104,6 +104,20 @@ struct drm_fb_helper_connector {
        struct drm_connector *connector;
 };
 
+/**
+ * struct drm_fb_helper - helper to emulate fbdev on top of kms
+ * @fb:  Scanout framebuffer object
+ * @dev:  DRM device
+ * @crtc_count: number of possible CRTCs
+ * @crtc_info: per-CRTC helper state (mode, x/y offset, etc)
+ * @connector_count: number of connected connectors
+ * @connector_info_alloc_count: size of connector_info
+ * @funcs: driver callbacks for fb helper
+ * @fbdev: emulated fbdev device info struct
+ * @pseudo_palette: fake palette of 16 colors
+ * @kernel_fb_list: list_head in kernel_fb_helper_list
+ * @delayed_hotplug: was there a hotplug while kms master active?
+ */
 struct drm_fb_helper {
        struct drm_framebuffer *fb;
        struct drm_device *dev;
@@ -120,6 +134,17 @@ struct drm_fb_helper {
        /* we got a hotplug but fbdev wasn't running the console
           delay until next set_par */
        bool delayed_hotplug;
+
+       /**
+        * @atomic:
+        *
+        * Use atomic updates for restore_fbdev_mode(), etc.  This defaults to
+        * true if driver has DRIVER_ATOMIC feature flag, but drivers can
+        * override it to true after drm_fb_helper_init() if they support atomic
+        * modeset but do not yet advertise DRIVER_ATOMIC (note that fb-helper
+        * does not require ASYNC commits).
+        */
+       bool atomic;
 };
 
 #ifdef CONFIG_DRM_FBDEV_EMULATION
@@ -136,7 +161,7 @@ int drm_fb_helper_set_par(struct fb_info *info);
 int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
                            struct fb_info *info);
 
-bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper);
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper);
 
 struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper);
 void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper);
@@ -226,10 +251,10 @@ static inline int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
        return 0;
 }
 
-static inline bool
+static inline int
 drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
 {
-       return true;
+       return 0;
 }
 
 static inline struct fb_info *
index 5dd18bf..94938d8 100644 (file)
@@ -43,19 +43,19 @@ struct drm_modeset_acquire_ctx {
 
        struct ww_acquire_ctx ww_ctx;
 
-       /**
+       /*
         * Contended lock: if a lock is contended you should only call
         * drm_modeset_backoff() which drops locks and slow-locks the
         * contended lock.
         */
        struct drm_modeset_lock *contended;
 
-       /**
+       /*
         * list of held locks (drm_modeset_lock)
         */
        struct list_head locked;
 
-       /**
+       /*
         * Trylock mode, use only for panic handlers!
         */
        bool trylock_only;
@@ -70,12 +70,12 @@ struct drm_modeset_acquire_ctx {
  * Used for locking CRTCs and other modeset resources.
  */
 struct drm_modeset_lock {
-       /**
+       /*
         * modeset lock
         */
        struct ww_mutex mutex;
 
-       /**
+       /*
         * Resources that are locked as part of an atomic update are added
         * to a list (so we know what to unlock at the end).
         */
index dda401b..5a7f9d4 100644 (file)
@@ -58,10 +58,8 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
  */
 struct drm_plane_helper_funcs {
        int (*prepare_fb)(struct drm_plane *plane,
-                         struct drm_framebuffer *fb,
                          const struct drm_plane_state *new_state);
        void (*cleanup_fb)(struct drm_plane *plane,
-                          struct drm_framebuffer *fb,
                           const struct drm_plane_state *old_state);
 
        int (*atomic_check)(struct drm_plane *plane,
index b483abd..fe90bfc 100644 (file)
@@ -1,10 +1,31 @@
 /*
+ * vga_switcheroo.h - Support for laptop with dual GPU using one set of outputs
+ *
  * Copyright (c) 2010 Red Hat Inc.
  * Author : Dave Airlie <airlied@redhat.com>
  *
- * Licensed under GPLv2
+ * Copyright (c) 2015 Lukas Wunner <lukas@wunner.de>
+ *
+ * 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, sublicense,
+ * 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 NONINFRINGEMENT.  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.
  *
- * vga_switcheroo.h - Support for laptop with dual GPU using one set of outputs
  */
 
 #ifndef _LINUX_VGA_SWITCHEROO_H_
 
 struct pci_dev;
 
+/**
+ * enum vga_switcheroo_state - client power state
+ * @VGA_SWITCHEROO_OFF: off
+ * @VGA_SWITCHEROO_ON: on
+ * @VGA_SWITCHEROO_INIT: client has registered with vga_switcheroo but
+ *     vga_switcheroo is not enabled, i.e. no second client or no handler
+ *     has registered. Only used in vga_switcheroo_get_client_state() which
+ *     in turn is only called from hda_intel.c
+ * @VGA_SWITCHEROO_NOT_FOUND: client has not registered with vga_switcheroo.
+ *     Only used in vga_switcheroo_get_client_state() which in turn is only
+ *     called from hda_intel.c
+ *
+ * Client power state.
+ */
 enum vga_switcheroo_state {
        VGA_SWITCHEROO_OFF,
        VGA_SWITCHEROO_ON,
@@ -22,20 +57,64 @@ enum vga_switcheroo_state {
        VGA_SWITCHEROO_NOT_FOUND,
 };
 
+/**
+ * enum vga_switcheroo_client_id - client identifier
+ * @VGA_SWITCHEROO_IGD: integrated graphics device
+ * @VGA_SWITCHEROO_DIS: discrete graphics device
+ * @VGA_SWITCHEROO_MAX_CLIENTS: currently no more than two GPUs are supported
+ *
+ * Client identifier. Audio clients use the same identifier & 0x100.
+ */
 enum vga_switcheroo_client_id {
        VGA_SWITCHEROO_IGD,
        VGA_SWITCHEROO_DIS,
        VGA_SWITCHEROO_MAX_CLIENTS,
 };
 
+/**
+ * struct vga_switcheroo_handler - handler callbacks
+ * @init: initialize handler.
+ *     Optional. This gets called when vga_switcheroo is enabled, i.e. when
+ *     two vga clients have registered. It allows the handler to perform
+ *     some delayed initialization that depends on the existence of the
+ *     vga clients. Currently only the radeon and amdgpu drivers use this.
+ *     The return value is ignored
+ * @switchto: switch outputs to given client.
+ *     Mandatory. For muxless machines this should be a no-op. Returning 0
+ *     denotes success, anything else failure (in which case the switch is
+ *     aborted)
+ * @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.
+ *     Mandatory
+ *
+ * Handler callbacks. The multiplexer itself. The @switchto and @get_client_id
+ * methods are mandatory, all others may be set to NULL.
+ */
 struct vga_switcheroo_handler {
+       int (*init)(void);
        int (*switchto)(enum vga_switcheroo_client_id id);
        int (*power_state)(enum vga_switcheroo_client_id id,
                           enum vga_switcheroo_state state);
-       int (*init)(void);
        int (*get_client_id)(struct pci_dev *pdev);
 };
 
+/**
+ * struct vga_switcheroo_client_ops - client callbacks
+ * @set_gpu_state: do the equivalent of suspend/resume for the card.
+ *     Mandatory. This should not cut power to the discrete GPU,
+ *     which is the job of the handler
+ * @reprobe: poll outputs.
+ *     Optional. This gets called after waking the GPU and switching
+ *     the outputs to it
+ * @can_switch: check if the device is in a position to switch now.
+ *     Mandatory. The client should return false if a user space process
+ *     has one of its device files open
+ *
+ * Client callbacks. A client can be either a GPU or an audio device on a GPU.
+ * The @set_gpu_state and @can_switch methods are mandatory, @reprobe may be
+ * set to NULL. For audio clients, the @reprobe member is bogus.
+ */
 struct vga_switcheroo_client_ops {
        void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state);
        void (*reprobe)(struct pci_dev *dev);
index 359107a..6c11ca4 100644 (file)
 
 struct drm_mode_modeinfo {
        __u32 clock;
-       __u16 hdisplay, hsync_start, hsync_end, htotal, hskew;
-       __u16 vdisplay, vsync_start, vsync_end, vtotal, vscan;
+       __u16 hdisplay;
+       __u16 hsync_start;
+       __u16 hsync_end;
+       __u16 htotal;
+       __u16 hskew;
+       __u16 vdisplay;
+       __u16 vsync_start;
+       __u16 vsync_end;
+       __u16 vtotal;
+       __u16 vscan;
 
        __u32 vrefresh;
 
@@ -124,8 +132,10 @@ struct drm_mode_card_res {
        __u32 count_crtcs;
        __u32 count_connectors;
        __u32 count_encoders;
-       __u32 min_width, max_width;
-       __u32 min_height, max_height;
+       __u32 min_width;
+       __u32 max_width;
+       __u32 min_height;
+       __u32 max_height;
 };
 
 struct drm_mode_crtc {
@@ -135,7 +145,8 @@ struct drm_mode_crtc {
        __u32 crtc_id; /**< Id */
        __u32 fb_id; /**< Id of framebuffer */
 
-       __u32 x, y; /**< Position on the frameuffer */
+       __u32 x; /**< x Position on the framebuffer */
+       __u32 y; /**< y Position on the framebuffer */
 
        __u32 gamma_size;
        __u32 mode_valid;
@@ -153,12 +164,16 @@ struct drm_mode_set_plane {
        __u32 flags; /* see above flags */
 
        /* Signed dest location allows it to be partially off screen */
-       __s32 crtc_x, crtc_y;
-       __u32 crtc_w, crtc_h;
+       __s32 crtc_x;
+       __s32 crtc_y;
+       __u32 crtc_w;
+       __u32 crtc_h;
 
        /* Source values are 16.16 fixed point */
-       __u32 src_x, src_y;
-       __u32 src_h, src_w;
+       __u32 src_x;
+       __u32 src_y;
+       __u32 src_h;
+       __u32 src_w;
 };
 
 struct drm_mode_get_plane {
@@ -244,7 +259,8 @@ struct drm_mode_get_connector {
        __u32 connector_type_id;
 
        __u32 connection;
-       __u32 mm_width, mm_height; /**< HxW in millimeters */
+       __u32 mm_width;  /**< width in millimeters */
+       __u32 mm_height; /**< height in millimeters */
        __u32 subpixel;
 
        __u32 pad;
@@ -327,7 +343,8 @@ struct drm_mode_get_blob {
 
 struct drm_mode_fb_cmd {
        __u32 fb_id;
-       __u32 width, height;
+       __u32 width;
+       __u32 height;
        __u32 pitch;
        __u32 bpp;
        __u32 depth;
@@ -340,7 +357,8 @@ struct drm_mode_fb_cmd {
 
 struct drm_mode_fb_cmd2 {
        __u32 fb_id;
-       __u32 width, height;
+       __u32 width;
+       __u32 height;
        __u32 pixel_format; /* fourcc code from drm_fourcc.h */
        __u32 flags; /* see above flags */