diff options
author | Dave Airlie <airlied@redhat.com> | 2015-09-30 08:35:45 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2015-09-30 08:35:45 +1000 |
commit | 2d4df13c0f9ef56452b1d9a9016cb3946e17bfe5 (patch) | |
tree | 9f5381966edbf51cafd23b7806ed754f4f3e8bd1 | |
parent | 9ffecb10283508260936b96022d4ee43a7798b4c (diff) | |
parent | cf6483050e9bf13979415d9fd388554d8c8f3477 (diff) |
Merge tag 'topic/drm-misc-2015-09-25' of git://anongit.freedesktop.org/drm-intel into drm-next
Another attempt at drm-misc for 4.4 ...
- better atomic helpers for runtime pm drivers
- atomic fbdev
- dp aux i2c STATUS_UPDATE handling (for short i2c replies from the sink)
- bunch of constify patches
- inital kerneldoc for vga switcheroo
- some vblank code cleanups from Ville and Thierry
- various polish all over
* tag 'topic/drm-misc-2015-09-25' of git://anongit.freedesktop.org/drm-intel: (57 commits)
drm/irq: Add drm_crtc_vblank_count_and_time()
drm/irq: Rename drm_crtc -> crtc
drm: drm_atomic_crtc_get_property should be static
drm/gma500: Remove DP_LINK_STATUS_SIZE redefinition
vga_switcheroo: Set active attribute to false for audio clients
drm/core: Preserve the fb id on close.
drm/core: Preserve the framebuffer after removing it.
drm: Use vblank timestamps to guesstimate how many vblanks were missed
drm: store_vblank() is never called with NULL timestamp
drm: Clean up drm_calc_vbltimestamp_from_scanoutpos() vbl_status
drm: Limit the number of .get_vblank_counter() retries
drm: Pass flags to drm_update_vblank_count()
drm/i915: Fix vblank count variable types
drm: Kill pixeldur_ns
drm: Stop using linedur_ns and pixeldur_ns for vblank timestamps
drm: Move timestamping constants into drm_vblank_crtc
drm/fbdev: Update legacy plane->fb refcounting for atomic restore
drm: fix kernel-doc warnings in drm_crtc.h
vga_switcheroo: Sort headers alphabetically
drm: Spell vga_switcheroo consistently
...
66 files changed, 1303 insertions, 497 deletions
diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 9ddf8c6cb887..30401f927156 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -3646,7 +3646,7 @@ void (*postclose) (struct drm_device *, struct drm_file *);</synopsis> plane properties to default value, so that a subsequent open of the device will not inherit state from the previous user. It can also be used to execute delayed power switching state changes, e.g. in - conjunction with the vga-switcheroo infrastructure. Beyond that KMS + conjunction with the vga_switcheroo infrastructure. Beyond that KMS drivers should not do any further cleanup. Only legacy UMS drivers might need to clean up device state so that the vga console or an independent fbdev driver could take over. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 22a4b687ea5b..c6dd5f350684 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -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] diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index e3d70772b531..9b34a3410c32 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -745,7 +745,8 @@ bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc, * */ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, - int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) { u32 vbl = 0, position = 0; int vbl_start, vbl_end, vtotal, ret = 0; @@ -781,7 +782,7 @@ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl } else { /* No: Fake something reasonable which gives at least ok results. */ - vbl_start = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; + vbl_start = mode->crtc_vdisplay; vbl_end = 0; } @@ -797,7 +798,7 @@ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl /* Inside "upper part" of vblank area? Apply corrective offset if so: */ if (in_vbl && (*vpos >= vbl_start)) { - vtotal = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; + vtotal = mode->crtc_vtotal; *vpos = *vpos - vtotal; } @@ -819,8 +820,8 @@ int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl * We only do this if DRM_CALLED_FROM_VBLIRQ. */ if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) { - vbl_start = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; - vtotal = adev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; + vbl_start = mode->crtc_vdisplay; + vtotal = mode->crtc_vtotal; if (vbl_start - *vpos < vtotal / 100) { *vpos -= vtotal; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 8c735f544b66..275f1c3dbba0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -682,7 +682,7 @@ int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, vblank_time, flags, - drmcrtc, &drmcrtc->hwmode); + &drmcrtc->hwmode); } const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 64efe5b52e65..2b03425f9740 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -543,7 +543,8 @@ void amdgpu_encoder_set_active_device(struct drm_encoder *encoder); int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, int *vpos, int *hpos, ktime_t *stime, - ktime_t *etime); + ktime_t *etime, + const struct drm_display_mode *mode); int amdgpu_framebuffer_init(struct drm_device *dev, struct amdgpu_framebuffer *rfb, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c index be9fa8220499..36fda86b3518 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c @@ -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); } diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index f7d5166f89b2..7bb3845d9974 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -438,7 +438,8 @@ EXPORT_SYMBOL(drm_atomic_crtc_set_property); * consistent behavior you must call this function rather than the * driver hook directly. */ -int drm_atomic_crtc_get_property(struct drm_crtc *crtc, +static int +drm_atomic_crtc_get_property(struct drm_crtc *crtc, const struct drm_crtc_state *state, struct drm_property *property, uint64_t *val) { @@ -663,6 +664,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 +754,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; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index aecb5d69bc2d..87a2a446d2b7 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -42,14 +42,14 @@ * 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 @@ -2333,6 +2409,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 * @state: connector state object to release diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 33d877c65ced..e600a5fb2b60 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -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; @@ -3310,14 +3320,11 @@ int drm_mode_rmfb(struct drm_device *dev, if (!found) goto fail_lookup; - /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ - __drm_framebuffer_unregister(dev, fb); - list_del_init(&fb->filp_head); mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&file_priv->fbs_lock); - drm_framebuffer_remove(fb); + drm_framebuffer_unreference(fb); return 0; @@ -3484,7 +3491,6 @@ out_err1: */ void drm_fb_release(struct drm_file *priv) { - struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; /* @@ -3498,16 +3504,10 @@ void drm_fb_release(struct drm_file *priv) * at it any more. */ list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { - - mutex_lock(&dev->mode_config.fb_lock); - /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ - __drm_framebuffer_unregister(dev, fb); - mutex_unlock(&dev->mode_config.fb_lock); - list_del_init(&fb->filp_head); - /* This will also drop the fpriv->fbs reference. */ - drm_framebuffer_remove(fb); + /* This drops the fpriv->fbs reference. */ + drm_framebuffer_unreference(fb); } } @@ -5732,7 +5732,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, diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 291734e87fca..9535c5b60387 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -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; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 53d09a19f7e1..9ad823fcde87 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -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; } diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 05bb7311ac5d..d895556be4f0 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -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; diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index c5605fe4907e..698b8c3b09d9 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -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; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 418d299f3b12..abe9793d548d 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -38,6 +38,13 @@ #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,96 @@ 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->old_fb = plane->fb; + + 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); + +fail: + drm_for_each_plane(plane, dev) { + if (ret == 0) { + struct drm_framebuffer *new_fb = plane->state->fb; + if (new_fb) + drm_framebuffer_reference(new_fb); + plane->fb = new_fb; + plane->crtc = plane->state->crtc; + + if (plane->old_fb) + drm_framebuffer_unreference(plane->old_fb); + } + plane->old_fb = NULL; + } + + if (ret == -EDEADLK) + goto backoff; + + if (ret != 0) + 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 +445,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 +463,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 +692,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 +728,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 +819,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 +1230,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 +1304,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 +1323,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } } } +unlock: drm_modeset_unlock_all(dev); return ret; } @@ -1930,6 +2099,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 +2145,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; diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 059af01bd07a..43cbda3306ac 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -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); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 22d207e211e7..ed2394e1720b 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -74,22 +74,22 @@ module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); -static void store_vblank(struct drm_device *dev, int crtc, +static void store_vblank(struct drm_device *dev, unsigned int pipe, u32 vblank_count_inc, - struct timeval *t_vblank) + struct timeval *t_vblank, u32 last) { - struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; u32 tslot; assert_spin_locked(&dev->vblank_time_lock); - if (t_vblank) { - /* All writers hold the spinlock, but readers are serialized by - * the latching of vblank->count below. - */ - tslot = vblank->count + vblank_count_inc; - vblanktimestamp(dev, crtc, tslot) = *t_vblank; - } + vblank->last = last; + + /* All writers hold the spinlock, but readers are serialized by + * the latching of vblank->count below. + */ + tslot = vblank->count + vblank_count_inc; + vblanktimestamp(dev, pipe, tslot) = *t_vblank; /* * vblank timestamp updates are protected on the write side with @@ -105,12 +105,60 @@ static void store_vblank(struct drm_device *dev, int crtc, } /** + * drm_reset_vblank_timestamp - reset the last timestamp to the last vblank + * @dev: DRM device + * @pipe: index of CRTC for which to reset the timestamp + * + * Reset the stored timestamp for the current vblank count to correspond + * to the last vblank occurred. + * + * Only to be called from drm_vblank_on(). + * + * Note: caller must hold dev->vbl_lock since this reads & writes + * device vblank fields. + */ +static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe) +{ + u32 cur_vblank; + bool rc; + struct timeval t_vblank; + int count = DRM_TIMESTAMP_MAXRETRIES; + + spin_lock(&dev->vblank_time_lock); + + /* + * sample the current counter to avoid random jumps + * when drm_vblank_enable() applies the diff + */ + do { + cur_vblank = dev->driver->get_vblank_counter(dev, pipe); + rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0); + } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0); + + /* + * Only reinitialize corresponding vblank timestamp if high-precision query + * available and didn't fail. Otherwise reinitialize delayed at next vblank + * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. + */ + if (!rc) + t_vblank = (struct timeval) {0, 0}; + + /* + * +1 to make sure user will never see the same + * vblank counter value before and after a modeset + */ + store_vblank(dev, pipe, 1, &t_vblank, cur_vblank); + + spin_unlock(&dev->vblank_time_lock); +} + +/** * drm_update_vblank_count - update the master vblank counter * @dev: DRM device * @pipe: counter to update * * Call back into the driver to update the appropriate vblank counter - * (specified by @crtc). Deal with wraparound, if it occurred, and + * (specified by @pipe). Deal with wraparound, if it occurred, and * update the last read value so we can deal with wraparound on the next * call if necessary. * @@ -120,12 +168,15 @@ static void store_vblank(struct drm_device *dev, int crtc, * Note: caller must hold dev->vbl_lock since this reads & writes * device vblank fields. */ -static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe) +static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, + unsigned long flags) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; u32 cur_vblank, diff; bool rc; struct timeval t_vblank; + int count = DRM_TIMESTAMP_MAXRETRIES; + int framedur_ns = vblank->framedur_ns; /* * Interrupts were disabled prior to this call, so deal with counter @@ -141,23 +192,43 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe) */ do { cur_vblank = dev->driver->get_vblank_counter(dev, pipe); - rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0); - } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe)); + rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags); + } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0); + + if (dev->max_vblank_count != 0) { + /* trust the hw counter when it's around */ + diff = (cur_vblank - vblank->last) & dev->max_vblank_count; + } else if (rc && framedur_ns) { + const struct timeval *t_old; + u64 diff_ns; - /* Deal with counter wrap */ - diff = cur_vblank - vblank->last; - if (cur_vblank < vblank->last) { - diff += dev->max_vblank_count + 1; + t_old = &vblanktimestamp(dev, pipe, vblank->count); + diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old); + + /* + * Figure out how many vblanks we've missed based + * on the difference in the timestamps and the + * frame/field duration. + */ + diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns); - DRM_DEBUG("last_vblank[%u]=0x%x, cur_vblank=0x%x => diff=0x%x\n", - pipe, vblank->last, cur_vblank, diff); + if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ) + DRM_DEBUG("crtc %u: Redundant vblirq ignored." + " diff_ns = %lld, framedur_ns = %d)\n", + pipe, (long long) diff_ns, framedur_ns); + } else { + /* some kind of default for drivers w/o accurate vbl timestamping */ + diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0; } - DRM_DEBUG("updating vblank count on crtc %u, missed %d\n", - pipe, diff); + DRM_DEBUG("updating vblank count on crtc %u:" + " current=%u, diff=%u, hw=%u hw_last=%u\n", + pipe, vblank->count, diff, cur_vblank, vblank->last); - if (diff == 0) + if (diff == 0) { + WARN_ON_ONCE(cur_vblank != vblank->last); return; + } /* * Only reinitialize corresponding vblank timestamp if high-precision query @@ -167,7 +238,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe) if (!rc) t_vblank = (struct timeval) {0, 0}; - store_vblank(dev, pipe, diff, &t_vblank); + store_vblank(dev, pipe, diff, &t_vblank, cur_vblank); } /* @@ -180,11 +251,6 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; unsigned long irqflags; - u32 vblcount; - s64 diff_ns; - bool vblrc; - struct timeval tvblank; - int count = DRM_TIMESTAMP_MAXRETRIES; /* Prevent vblank irq processing while disabling vblank irqs, * so no updates of timestamps or count can happen after we've @@ -193,26 +259,6 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe) spin_lock_irqsave(&dev->vblank_time_lock, irqflags); /* - * If the vblank interrupt was already disabled update the count - * and timestamp to maintain the appearance that the counter - * has been ticking all along until this time. This makes the - * count account for the entire time between drm_vblank_on() and - * drm_vblank_off(). - * - * But only do this if precise vblank timestamps are available. - * Otherwise we might read a totally bogus timestamp since drivers - * lacking precise timestamp support rely upon sampling the system clock - * at vblank interrupt time. Which obviously won't work out well if the - * vblank interrupt is disabled. - */ - if (!vblank->enabled && - drm_get_last_vbltimestamp(dev, pipe, &tvblank, 0)) { - drm_update_vblank_count(dev, pipe); - spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); - return; - } - - /* * Only disable vblank interrupts if they're enabled. This avoids * calling the ->disable_vblank() operation in atomic context with the * hardware potentially runtime suspended. @@ -222,47 +268,13 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe) vblank->enabled = false; } - /* No further vblank irq's will be processed after - * this point. Get current hardware vblank count and - * vblank timestamp, repeat until they are consistent. - * - * FIXME: There is still a race condition here and in - * drm_update_vblank_count() which can cause off-by-one - * reinitialization of software vblank counter. If gpu - * vblank counter doesn't increment exactly at the leading - * edge of a vblank interval, then we can lose 1 count if - * we happen to execute between start of vblank and the - * delayed gpu counter increment. - */ - do { - vblank->last = dev->driver->get_vblank_counter(dev, pipe); - vblrc = drm_get_last_vbltimestamp(dev, pipe, &tvblank, 0); - } while (vblank->last != dev->driver->get_vblank_counter(dev, pipe) && (--count) && vblrc); - - if (!count) - vblrc = 0; - - /* Compute time difference to stored timestamp of last vblank - * as updated by last invocation of drm_handle_vblank() in vblank irq. - */ - vblcount = vblank->count; - diff_ns = timeval_to_ns(&tvblank) - - timeval_to_ns(&vblanktimestamp(dev, pipe, vblcount)); - - /* If there is at least 1 msec difference between the last stored - * timestamp and tvblank, then we are currently executing our - * disable inside a new vblank interval, the tvblank timestamp - * corresponds to this new vblank interval and the irq handler - * for this vblank didn't run yet and won't run due to our disable. - * Therefore we need to do the job of drm_handle_vblank() and - * increment the vblank counter by one to account for this vblank. - * - * Skip this step if there isn't any high precision timestamp - * available. In that case we can't account for this and just - * hope for the best. + /* + * Always update the count and timestamp to maintain the + * appearance that the counter has been ticking all along until + * this time. This makes the count account for the entire time + * between drm_vblank_on() and drm_vblank_off(). */ - if (vblrc && (abs64(diff_ns) > 1000000)) - store_vblank(dev, pipe, 1, &tvblank); + drm_update_vblank_count(dev, pipe, 0); spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); } @@ -603,7 +615,8 @@ int drm_control(struct drm_device *dev, void *data, void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode) { - int linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; + struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)]; + int linedur_ns = 0, framedur_ns = 0; int dotclock = mode->crtc_clock; /* Valid dotclock? */ @@ -612,10 +625,9 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, /* * Convert scanline length in pixels and video - * dot clock to line duration, frame duration - * and pixel duration in nanoseconds: + * dot clock to line duration and frame duration + * in nanoseconds: */ - pixeldur_ns = 1000000 / dotclock; linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock); framedur_ns = div_u64((u64) frame_size * 1000000, dotclock); @@ -628,16 +640,14 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc, DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n", crtc->base.id); - crtc->pixeldur_ns = pixeldur_ns; - crtc->linedur_ns = linedur_ns; - crtc->framedur_ns = framedur_ns; + vblank->linedur_ns = linedur_ns; + vblank->framedur_ns = framedur_ns; DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n", crtc->base.id, mode->crtc_htotal, mode->crtc_vtotal, mode->crtc_vdisplay); - DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d, pixeldur %d\n", - crtc->base.id, dotclock, framedur_ns, - linedur_ns, pixeldur_ns); + DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n", + crtc->base.id, dotclock, framedur_ns, linedur_ns); } EXPORT_SYMBOL(drm_calc_timestamping_constants); @@ -651,7 +661,6 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); * @flags: Flags to pass to driver: * 0 = Default, * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler - * @refcrtc: CRTC which defines scanout timing * @mode: mode which defines the scanout timings * * Implements calculation of exact vblank timestamps from given drm_display_mode @@ -692,15 +701,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int *max_error, struct timeval *vblank_time, unsigned flags, - const struct drm_crtc *refcrtc, const struct drm_display_mode *mode) { struct timeval tv_etime; ktime_t stime, etime; - int vbl_status; + unsigned int vbl_status; + int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD; int vpos, hpos, i; - int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns; - bool invbl; + int delta_ns, duration_ns; if (pipe >= dev->num_crtcs) { DRM_ERROR("Invalid crtc %u\n", pipe); @@ -713,15 +721,10 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, return -EIO; } - /* Durations of frames, lines, pixels in nanoseconds. */ - framedur_ns = refcrtc->framedur_ns; - linedur_ns = refcrtc->linedur_ns; - pixeldur_ns = refcrtc->pixeldur_ns; - /* If mode timing undefined, just return as no-op: * Happens during initial modesetting of a crtc. */ - if (framedur_ns == 0) { + if (mode->crtc_clock == 0) { DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe); return -EAGAIN; } @@ -738,12 +741,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, * Get vertical and horizontal scanout position vpos, hpos, * and bounding timestamps stime, etime, pre/post query. */ - vbl_status = dev->driver->get_scanout_position(dev, pipe, flags, &vpos, - &hpos, &stime, &etime); + vbl_status = dev->driver->get_scanout_position(dev, pipe, flags, + &vpos, &hpos, + &stime, &etime, + mode); /* Return as no-op if scanout query unsupported or failed. */ if (!(vbl_status & DRM_SCANOUTPOS_VALID)) { - DRM_DEBUG("crtc %u : scanoutpos query failed [%d].\n", + DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n", pipe, vbl_status); return -EIO; } @@ -770,13 +775,15 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, * within vblank area, counting down the number of lines until * start of scanout. */ - invbl = vbl_status & DRM_SCANOUTPOS_IN_VBLANK; + if (vbl_status & DRM_SCANOUTPOS_IN_VBLANK) + ret |= DRM_VBLANKTIME_IN_VBLANK; /* Convert scanout position into elapsed time at raw_time query * since start of scanout at first display scanline. delta_ns * can be negative if start of scanout hasn't happened yet. */ - delta_ns = vpos * linedur_ns + hpos * pixeldur_ns; + delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos), + mode->crtc_clock); if (!drm_timestamp_monotonic) etime = ktime_mono_to_real(etime); @@ -792,17 +799,13 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, etime = ktime_sub_ns(etime, delta_ns); *vblank_time = ktime_to_timeval(etime); - DRM_DEBUG("crtc %u : v %d p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", - pipe, (int)vbl_status, hpos, vpos, + DRM_DEBUG("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", + pipe, vbl_status, hpos, vpos, (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, duration_ns/1000, i); - vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD; - if (invbl) - vbl_status |= DRM_VBLANKTIME_IN_VBLANK; - - return vbl_status; + return ret; } EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); @@ -914,11 +917,14 @@ EXPORT_SYMBOL(drm_crtc_vblank_count); * vblank events since the system was booted, including lost events due to * modesetting activity. Returns corresponding system timestamp of the time * of the vblank interval that corresponds to the current vblank counter value. + * + * This is the legacy version of drm_crtc_vblank_count_and_time(). */ u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, struct timeval *vblanktime) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int count = DRM_TIMESTAMP_MAXRETRIES; u32 cur_vblank; if (WARN_ON(pipe >= dev->num_crtcs)) @@ -934,12 +940,33 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, smp_rmb(); *vblanktime = vblanktimestamp(dev, pipe, cur_vblank); smp_rmb(); - } while (cur_vblank != vblank->count); + } while (cur_vblank != vblank->count && --count > 0); return cur_vblank; } EXPORT_SYMBOL(drm_vblank_count_and_time); +/** + * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value + * and the system timestamp corresponding to that vblank counter value + * @crtc: which counter to retrieve + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. Returns corresponding system timestamp of the time + * of the vblank interval that corresponds to the current vblank counter value. + * + * This is the native KMS version of drm_vblank_count_and_time(). + */ +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, + struct timeval *vblanktime) +{ + return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc), + vblanktime); +} +EXPORT_SYMBOL(drm_crtc_vblank_count_and_time); + static void send_vblank_event(struct drm_device *dev, struct drm_pending_vblank_event *e, unsigned long seq, struct timeval *now) @@ -1033,7 +1060,7 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) atomic_dec(&vblank->refcount); else { vblank->enabled = true; - drm_update_vblank_count(dev, pipe); + drm_update_vblank_count(dev, pipe, 0); } } @@ -1154,8 +1181,8 @@ EXPORT_SYMBOL(drm_crtc_vblank_put); * @dev: DRM device * @pipe: CRTC index * - * This waits for one vblank to pass on @crtc, using the irq driver interfaces. - * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. + * This waits for one vblank to pass on @pipe, using the irq driver interfaces. + * It is a failure to call this when the vblank irq for @pipe is disabled, e.g. * due to lack of driver support or because the crtc is off. */ void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe) @@ -1276,7 +1303,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_off); /** * drm_crtc_vblank_reset - reset vblank state to off on a CRTC - * @drm_crtc: CRTC in question + * @crtc: CRTC in question * * Drivers can use this function to reset the vblank state to off at load time. * Drivers should use this together with the drm_crtc_vblank_off() and @@ -1284,12 +1311,12 @@ EXPORT_SYMBOL(drm_crtc_vblank_off); * drm_crtc_vblank_off() is that this function doesn't save the vblank counter * and hence doesn't need to call any driver hooks. */ -void drm_crtc_vblank_reset(struct drm_crtc *drm_crtc) +void drm_crtc_vblank_reset(struct drm_crtc *crtc) { - struct drm_device *dev = drm_crtc->dev; + struct drm_device *dev = crtc->dev; unsigned long irqflags; - int crtc = drm_crtc_index(drm_crtc); - struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; spin_lock_irqsave(&dev->vbl_lock, irqflags); /* @@ -1333,16 +1360,8 @@ void drm_vblank_on(struct drm_device *dev, unsigned int pipe) vblank->inmodeset = 0; } - /* - * sample the current counter to avoid random jumps - * when drm_vblank_enable() applies the diff - * - * -1 to make sure user will never see the same - * vblank counter value before and after a modeset - */ - vblank->last = - (dev->driver->get_vblank_counter(dev, pipe) - 1) & - dev->max_vblank_count; + drm_reset_vblank_timestamp(dev, pipe); + /* * re-enable interrupts if there are users left, or the * user wishes vblank interrupts to be enabled all the time. @@ -1725,9 +1744,6 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - u32 vblcount; - s64 diff_ns; - struct timeval tvblank; unsigned long irqflags; if (WARN_ON_ONCE(!dev->num_crtcs)) @@ -1751,32 +1767,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) return false; } - /* Fetch corresponding timestamp for this vblank interval from - * driver and store it in proper slot of timestamp ringbuffer. - */ - - /* Get current timestamp and count. */ - vblcount = vblank->count; - drm_get_last_vbltimestamp(dev, pipe, &tvblank, DRM_CALLED_FROM_VBLIRQ); - - /* Compute time difference to timestamp of last vblank */ - diff_ns = timeval_to_ns(&tvblank) - - timeval_to_ns(&vblanktimestamp(dev, pipe, vblcount)); - - /* Update vblank timestamp and count if at least - * DRM_REDUNDANT_VBLIRQ_THRESH_NS nanoseconds - * difference between last stored timestamp and current - * timestamp. A smaller difference means basically - * identical timestamps. Happens if this vblank has - * been already processed and this is a redundant call, - * e.g., due to spurious vblank interrupts. We need to - * ignore those for accounting. - */ - if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) - store_vblank(dev, pipe, 1, &tvblank); - else - DRM_DEBUG("crtc %u: Redundant vblirq ignored. diff_ns = %d\n", - pipe, (int) diff_ns); + drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ); spin_unlock(&dev->vblank_time_lock); diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 3427b115e2bb..04de6fd88f8c 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -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; diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index fba321ca4344..6675b1428410 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -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 diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 5e5a07af02c8..d384ebcf0aaf 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -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) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 0f6cd33b531f..f08873f6489c 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -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); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 831d2e4cacf9..f0a5839bd226 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -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); diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c index d1e300dcd544..51daaea40b4d 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c @@ -191,14 +191,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; diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 0fafb8e2483a..17cea400ae32 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -247,7 +247,6 @@ i2c_dp_aux_add_bus(struct i2c_adapter *adapter) #define wait_for(COND, MS) _wait_for(COND, MS, 1) -#define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) #define DP_LINK_CONFIGURATION_SIZE 9 diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index 51fa32392029..d9a72c96e56c 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -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++) { diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c index 9b83574141a6..bb5f67f10edb 100644 --- a/drivers/gpu/drm/i2c/ch7006_mode.c +++ b/drivers/gpu/drm/i2c/ch7006_mode.c @@ -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; diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h index ce577841f931..dc6414af5d79 100644 --- a/drivers/gpu/drm/i2c/ch7006_priv.h +++ b/drivers/gpu/drm/i2c/ch7006_priv.h @@ -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); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 39d73dbc1c47..88d064e80783 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -674,12 +674,12 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, unsigned int flags, int *vpos, int *hpos, - ktime_t *stime, ktime_t *etime) + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - const struct drm_display_mode *mode = &intel_crtc->base.hwmode; int position; int vbl_start, vbl_end, hsync_start, htotal, vtotal; bool in_vbl = true; @@ -836,7 +836,6 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe, /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error, vblank_time, flags, - crtc, &crtc->hwmode); } diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 2a5c76faf9f8..e35997ebb331 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -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; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index cf418be7d30a..0379f14271de 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -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); } } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0a2e33fbf20d..7bb96d5850de 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -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 */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 2b9e6f9775c5..354432fdfcf0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -834,8 +834,8 @@ struct intel_unpin_work { u32 flip_count; u32 gtt_offset; struct drm_i915_gem_request *flip_queued_req; - int flip_queued_vblank; - int flip_ready_vblank; + u32 flip_queued_vblank; + u32 flip_ready_vblank; bool enable_stall_check; }; @@ -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, diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 8c6a6fa46005..ab2b856d91c3 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -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); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 0568ae6ec9dd..590ceabe2d5e 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -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); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index e9dee367b597..30d57e74c42f 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -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); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 07fb62fea6dc..a0f5ff0ce8dc 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -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); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 1ceb4f22dd89..7eb253bc24df 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -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); diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c index 08c6f5e50610..903c473d266f 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c +++ b/drivers/gpu/drm/nouveau/dispnv04/tvmodesnv17.c @@ -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", diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h index 459910b6bb32..1b07521cde0d 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.h @@ -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 { diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index cc6c228e11c8..a82c3cbe3127 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -103,6 +103,7 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, .base.head = nouveau_crtc(crtc)->index, }; struct nouveau_display *disp = nouveau_display(crtc->dev); + struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)]; int ret, retry = 1; do { @@ -116,7 +117,7 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, break; } - if (retry) ndelay(crtc->linedur_ns); + if (retry) ndelay(vblank->linedur_ns); } while (retry--); *hpos = args.scan.hline; @@ -132,7 +133,8 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos, int nouveau_display_scanoutpos(struct drm_device *dev, int head, unsigned int flags, - int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) { struct drm_crtc *crtc; @@ -155,7 +157,7 @@ nouveau_display_vblstamp(struct drm_device *dev, int head, int *max_error, list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (nouveau_crtc(crtc)->index == head) { return drm_calc_vbltimestamp_from_scanoutpos(dev, - head, max_error, time, flags, crtc, + head, max_error, time, flags, &crtc->hwmode); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index a6213e2425c5..4182d21538c5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -68,7 +68,8 @@ void nouveau_display_resume(struct drm_device *dev, bool runtime); int nouveau_display_vblank_enable(struct drm_device *, int); void nouveau_display_vblank_disable(struct drm_device *, int); int nouveau_display_scanoutpos(struct drm_device *, int, unsigned int, - int *, int *, ktime_t *, ktime_t *); + int *, int *, ktime_t *, ktime_t *, + const struct drm_display_mode *); int nouveau_display_vblstamp(struct drm_device *, int, int *, struct timeval *, unsigned); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 419c2e49adf5..d685e23449ce 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -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); @@ -753,7 +753,7 @@ static void dev_lastclose(struct drm_device *dev) { int i; - /* we don't support vga-switcheroo.. so just make sure the fbdev + /* we don't support vga_switcheroo.. so just make sure the fbdev * mode is active */ struct omap_drm_private *priv = dev->dev_private; diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 098904696a5c..09e363bb55f2 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -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, diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 9cd49c584263..bd73b4069069 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -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 diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index d2e9e9efc159..0503af748d99 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -323,7 +323,8 @@ void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id) */ if (update_pending && (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, 0, - &vpos, &hpos, NULL, NULL)) && + &vpos, &hpos, NULL, NULL, + &rdev->mode_info.crtcs[crtc_id]->base.hwmode)) && ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) || (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) { /* crtc didn't flip in this target vblank interval, @@ -1799,7 +1800,8 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, * */ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, - int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) + int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode) { u32 stat_crtc = 0, vbl = 0, position = 0; int vbl_start, vbl_end, vtotal, ret = 0; @@ -1914,7 +1916,7 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl } else { /* No: Fake something reasonable which gives at least ok results. */ - vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; + vbl_start = mode->crtc_vdisplay; vbl_end = 0; } @@ -1930,7 +1932,7 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl /* Inside "upper part" of vblank area? Apply corrective offset if so: */ if (in_vbl && (*vpos >= vbl_start)) { - vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; + vtotal = mode->crtc_vtotal; *vpos = *vpos - vtotal; } @@ -1952,8 +1954,8 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int fl * We only do this if DRM_CALLED_FROM_VBLIRQ. */ if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) { - vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay; - vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal; + vbl_start = mode->crtc_vdisplay; + vtotal = mode->crtc_vtotal; if (vbl_start - *vpos < vtotal / 100) { *vpos -= vtotal; diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 5751446677d3..e30c1d73b4ca 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -126,8 +126,9 @@ struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, int flags); extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, - int *vpos, int *hpos, ktime_t *stime, - ktime_t *etime); + int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode); extern bool radeon_is_px(struct drm_device *dev); extern const struct drm_ioctl_desc radeon_ioctls_kms[]; extern int radeon_max_kms_ioctl; diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 4a119c255ba9..fd9da282b29c 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -841,7 +841,7 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, /* Helper routine in DRM core does all the work: */ return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, vblank_time, flags, - drmcrtc, &drmcrtc->hwmode); + &drmcrtc->hwmode); } #define KMS_INVALID_IOCTL(name) \ diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index aecc3e3dec0c..2317d04f8a09 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -876,8 +876,9 @@ extern void radeon_cursor_reset(struct drm_crtc *crtc); extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags, - int *vpos, int *hpos, ktime_t *stime, - ktime_t *etime); + int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode); extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev); extern struct edid * diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 05751f3f8444..10f4c12e439e 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1733,7 +1733,9 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev) */ for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { if (rdev->pm.active_crtcs & (1 << crtc)) { - vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, &vpos, &hpos, NULL, NULL); + vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, + &vpos, &hpos, NULL, NULL, + &rdev->mode_info.crtcs[crtc]->base.hwmode); if ((vbl_status & DRM_SCANOUTPOS_VALID) && !(vbl_status & DRM_SCANOUTPOS_IN_VBLANK)) in_vbl = false; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 56518eb1269a..ca12e8ca5552 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -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); diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 6f4af6a8ba1b..9f85988a43ce 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -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); diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index ddefb85dc4f7..b4af4ab9ce6b 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -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) { } diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index 224a7dc8e4ed..6aecb6647313 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -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 diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 6d88cf1fcd1c..2486bc24bff6 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -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); diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 21060668fd25..86c03b53e7bf 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -1,38 +1,102 @@ /* + * 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/module.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#include <linux/fs.h> +#include <linux/console.h> #include <linux/debugfs.h> #include <linux/fb.h> - +#include <linux/fs.h> +#include <linux/module.h> #include <linux/pci.h> -#include <linux/console.h> -#include <linux/vga_switcheroo.h> #include <linux/pm_runtime.h> - +#include <linux/seq_file.h> +#include <linux/uaccess.h> #include <linux/vgaarb.h> +#include <linux/vga_switcheroo.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 +108,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 +185,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 +212,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 +260,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,11 +283,22 @@ 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 + * + * 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) + int id) { - return register_client(pdev, ops, id | ID_BIT_AUDIO, active, false); + return register_client(pdev, ops, id | ID_BIT_AUDIO, false, false); } EXPORT_SYMBOL(vga_switcheroo_register_audio_client); @@ -210,11 +330,20 @@ find_active_client(struct list_head *head) struct vga_switcheroo_client *client; list_for_each_entry(client, head, list) - if (client->active && client_is_vga(client)) + if (client->active) return client; 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 +357,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 +384,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 +405,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 +738,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 +778,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 +829,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 +885,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 +950,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) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 8b5ce7c5d9bb..d0251ac44a50 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -482,6 +482,7 @@ struct drm_driver { * scanout position query. Can be NULL to skip timestamp. * \param *etime Target location for timestamp taken immediately after * scanout position query. Can be NULL to skip timestamp. + * \param mode Current display timings. * * Returns vpos as a positive number while in active scanout area. * Returns vpos as a negative number inside vblank, counting the number @@ -499,8 +500,9 @@ struct drm_driver { */ int (*get_scanout_position) (struct drm_device *dev, int crtc, unsigned int flags, - int *vpos, int *hpos, ktime_t *stime, - ktime_t *etime); + int *vpos, int *hpos, + ktime_t *stime, ktime_t *etime, + const struct drm_display_mode *mode); /** * Called by \c drm_get_last_vbltimestamp. Should return a precise @@ -701,6 +703,8 @@ struct drm_vblank_crtc { u32 last_wait; /* Last vblank seqno waited per CRTC */ unsigned int inmodeset; /* Display driver is setting mode */ unsigned int pipe; /* crtc index */ + int framedur_ns; /* frame/field duration in ns */ + int linedur_ns; /* line duration in ns */ bool enabled; /* so we don't call enable more than once per disable */ }; @@ -928,6 +932,8 @@ extern u32 drm_vblank_count(struct drm_device *dev, int pipe); extern u32 drm_crtc_vblank_count(struct drm_crtc *crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, struct timeval *vblanktime); +extern u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, + struct timeval *vblanktime); extern void drm_send_vblank_event(struct drm_device *dev, unsigned int pipe, struct drm_pending_vblank_event *e); extern void drm_crtc_send_vblank_event(struct drm_crtc *crtc, @@ -951,7 +957,6 @@ extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, unsigned int pipe, int *max_error, struct timeval *vblank_time, unsigned flags, - const struct drm_crtc *refcrtc, const struct drm_display_mode *mode); extern void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 11266d147a29..8cba54a2a0a0 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -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); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index faaeff7db684..683f1421a825 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -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 { @@ -415,9 +413,6 @@ struct drm_crtc_funcs { * @funcs: CRTC control functions * @gamma_size: size of gamma ramp * @gamma_store: gamma ramp values - * @framedur_ns: precise frame timing - * @linedur_ns: precise line timing - * @pixeldur_ns: precise pixel timing * @helper_private: mid-layer private data * @properties: property tracking for this CRTC * @state: current atomic state for this CRTC @@ -470,9 +465,6 @@ struct drm_crtc { uint32_t gamma_size; uint16_t *gamma_store; - /* Constants needed for precise vblank and swap timestamping. */ - int framedur_ns, linedur_ns, pixeldur_ns; - /* if you are using the helper */ const void *helper_private; @@ -913,7 +905,6 @@ struct drm_bridge_funcs { * @next: the next bridge in the encoder chain * @of_node: device node pointer to the bridge * @list: to keep track of all added bridges - * @base: base mode object * @funcs: control functions * @driver_private: pointer to the bridge driver's internal context */ @@ -1390,7 +1381,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); diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 499e9f625aef..d0c88107996a 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -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 diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 53c53c459b15..2af97691e878 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -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 diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index dbab4622b58f..87b090c4b730 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -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 * diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index 5dd18bfdf601..94938d89347c 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -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). */ diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index dda401bf910e..5a7f9d4efb1d 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -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, diff --git a/include/linux/fb.h b/include/linux/fb.h index bc9afa74ee11..be40dbaed11e 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -156,7 +156,7 @@ struct fb_cursor_user { #define FB_EVENT_GET_REQ 0x0D /* Unbind from the console if possible */ #define FB_EVENT_FB_UNBIND 0x0E -/* CONSOLE-SPECIFIC: remap all consoles to new fb - for vga switcheroo */ +/* CONSOLE-SPECIFIC: remap all consoles to new fb - for vga_switcheroo */ #define FB_EVENT_REMAP_ALL_CONSOLE 0x0F /* A hardware display blank early change occured */ #define FB_EARLY_EVENT_BLANK 0x10 diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index b483abd34493..376499197717 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -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_ @@ -14,6 +35,20 @@ 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); @@ -49,7 +128,7 @@ int vga_switcheroo_register_client(struct pci_dev *dev, bool driver_power_control); int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, - int id, bool active); + int id); void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info); @@ -75,7 +154,7 @@ static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_i static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, - int id, bool active) { return 0; } + int id) { return 0; } static inline void vga_switcheroo_unregister_handler(void) {} static inline int vga_switcheroo_process_delayed_switch(void) { return 0; } static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; } diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 359107ab629e..6c11ca401de8 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -105,8 +105,16 @@ 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 */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c38c68f57938..e819013959d9 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1143,8 +1143,7 @@ static int register_vga_switcheroo(struct azx *chip) * is there any machine with two switchable HDMI audio controllers? */ err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, - VGA_SWITCHEROO_DIS, - hda->probe_continued); + VGA_SWITCHEROO_DIS); if (err < 0) return err; hda->vga_switcheroo_registered = 1; |