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 ...
This commit is contained in:
commit
2d4df13c0f
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,64 +1702,10 @@ 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);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
primary_state = drm_atomic_get_plane_state(state, crtc->primary);
|
||||
if (IS_ERR(primary_state)) {
|
||||
ret = PTR_ERR(primary_state);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!set->mode) {
|
||||
WARN_ON(set->fb);
|
||||
WARN_ON(set->num_connectors);
|
||||
|
||||
ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
|
||||
if (ret != 0)
|
||||
goto fail;
|
||||
|
||||
crtc_state->active = false;
|
||||
|
||||
ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
|
||||
if (ret != 0)
|
||||
goto fail;
|
||||
|
||||
drm_atomic_set_fb_for_plane(primary_state, NULL);
|
||||
|
||||
goto commit;
|
||||
}
|
||||
|
||||
WARN_ON(!set->fb);
|
||||
WARN_ON(!set->num_connectors);
|
||||
|
||||
ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
|
||||
ret = __drm_atomic_helper_set_config(set, state);
|
||||
if (ret != 0)
|
||||
goto fail;
|
||||
|
||||
crtc_state->active = true;
|
||||
|
||||
ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
|
||||
if (ret != 0)
|
||||
goto fail;
|
||||
drm_atomic_set_fb_for_plane(primary_state, set->fb);
|
||||
primary_state->crtc_x = 0;
|
||||
primary_state->crtc_y = 0;
|
||||
primary_state->crtc_h = set->mode->vdisplay;
|
||||
primary_state->crtc_w = set->mode->hdisplay;
|
||||
primary_state->src_x = set->x << 16;
|
||||
primary_state->src_y = set->y << 16;
|
||||
primary_state->src_h = set->mode->vdisplay << 16;
|
||||
primary_state->src_w = set->mode->hdisplay << 16;
|
||||
|
||||
commit:
|
||||
ret = update_output_state(state, set);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = drm_atomic_commit(state);
|
||||
if (ret != 0)
|
||||
goto fail;
|
||||
|
@ -1725,6 +1734,73 @@ backoff:
|
|||
}
|
||||
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);
|
||||
WARN_ON(set->num_connectors);
|
||||
|
||||
ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
crtc_state->active = false;
|
||||
|
||||
ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
drm_atomic_set_fb_for_plane(primary_state, NULL);
|
||||
|
||||
goto commit;
|
||||
}
|
||||
|
||||
WARN_ON(!set->fb);
|
||||
WARN_ON(!set->num_connectors);
|
||||
|
||||
ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
crtc_state->active = true;
|
||||
|
||||
ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
drm_atomic_set_fb_for_plane(primary_state, set->fb);
|
||||
primary_state->crtc_x = 0;
|
||||
primary_state->crtc_y = 0;
|
||||
primary_state->crtc_h = set->mode->vdisplay;
|
||||
primary_state->crtc_w = set->mode->hdisplay;
|
||||
primary_state->src_x = set->x << 16;
|
||||
primary_state->src_y = set->y << 16;
|
||||
primary_state->src_h = set->mode->vdisplay << 16;
|
||||
primary_state->src_w = set->mode->hdisplay << 16;
|
||||
|
||||
commit:
|
||||
ret = update_output_state(state, set);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_crtc_set_property - helper for crtc properties
|
||||
* @crtc: DRM crtc
|
||||
|
@ -2332,6 +2408,84 @@ drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
|
||||
|
||||
/**
|
||||
* drm_atomic_helper_duplicate_state - duplicate an atomic state object
|
||||
* @dev: DRM device
|
||||
* @ctx: lock acquisition context
|
||||
*
|
||||
* Makes a copy of the current atomic state by looping over all objects and
|
||||
* duplicating their respective states.
|
||||
*
|
||||
* Note that this treats atomic state as persistent between save and restore.
|
||||
* Drivers must make sure that this is possible and won't result in confusion
|
||||
* or erroneous behaviour.
|
||||
*
|
||||
* Note that if callers haven't already acquired all modeset locks this might
|
||||
* return -EDEADLK, which must be handled by calling drm_modeset_backoff().
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to the copy of the atomic state object on success or an
|
||||
* ERR_PTR()-encoded error code on failure.
|
||||
*/
|
||||
struct drm_atomic_state *
|
||||
drm_atomic_helper_duplicate_state(struct drm_device *dev,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_connector *conn;
|
||||
struct drm_plane *plane;
|
||||
struct drm_crtc *crtc;
|
||||
int err = 0;
|
||||
|
||||
state = drm_atomic_state_alloc(dev);
|
||||
if (!state)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
state->acquire_ctx = ctx;
|
||||
|
||||
drm_for_each_crtc(crtc, dev) {
|
||||
struct drm_crtc_state *crtc_state;
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
||||
if (IS_ERR(crtc_state)) {
|
||||
err = PTR_ERR(crtc_state);
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
|
||||
drm_for_each_plane(plane, dev) {
|
||||
struct drm_plane_state *plane_state;
|
||||
|
||||
plane_state = drm_atomic_get_plane_state(state, plane);
|
||||
if (IS_ERR(plane_state)) {
|
||||
err = PTR_ERR(plane_state);
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
|
||||
drm_for_each_connector(conn, dev) {
|
||||
struct drm_connector_state *conn_state;
|
||||
|
||||
conn_state = drm_atomic_get_connector_state(state, conn);
|
||||
if (IS_ERR(conn_state)) {
|
||||
err = PTR_ERR(conn_state);
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
|
||||
/* clear the acquire context so that it isn't accidentally reused */
|
||||
state->acquire_ctx = NULL;
|
||||
|
||||
free:
|
||||
if (err < 0) {
|
||||
drm_atomic_state_free(state);
|
||||
state = ERR_PTR(err);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
|
||||
|
||||
/**
|
||||
* __drm_atomic_helper_connector_destroy_state - release connector state
|
||||
* @connector: connector object
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
@ -104,13 +104,61 @@ static void store_vblank(struct drm_device *dev, int crtc,
|
|||
smp_wmb();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/* Deal with counter wrap */
|
||||
diff = cur_vblank - vblank->last;
|
||||
if (cur_vblank < vblank->last) {
|
||||
diff += dev->max_vblank_count + 1;
|
||||
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;
|
||||
|
||||
DRM_DEBUG("last_vblank[%u]=0x%x, cur_vblank=0x%x => diff=0x%x\n",
|
||||
pipe, vblank->last, cur_vblank, diff);
|
||||
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);
|
||||
|
||||
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
|
||||
|
@ -192,26 +258,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
|
||||
|
@ -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.
|
||||
/*
|
||||
* 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().
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 DRM class pointer that can then be used
|
||||
* in calls to drm_sysfs_device_add().
|
||||
* This is used to create the DRM class, which is the implicit parent of any
|
||||
* other top-level DRM sysfs objects.
|
||||
*
|
||||
* Note, the pointer created here is to be destroyed when finished by making a
|
||||
* call to drm_sysfs_destroy().
|
||||
* You must call drm_sysfs_destroy() to release the allocated resources.
|
||||
*
|
||||
* 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;
|
||||
drm_class = class_create(THIS_MODULE, "drm");
|
||||
if (IS_ERR(drm_class))
|
||||
return PTR_ERR(drm_class);
|
||||
|
||||
drm_class->pm = &drm_class_dev_pm_ops;
|
||||
|
||||
err = class_create_file(drm_class, &class_attr_version.attr);
|
||||
if (err) {
|
||||
class_destroy(drm_class);
|
||||
drm_class = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
class->pm = &drm_class_dev_pm_ops;
|
||||
|
||||
err = class_create_file(class, &class_attr_version.attr);
|
||||
if (err)
|
||||
goto err_out_class;
|
||||
|
||||
class->devnode = drm_devnode;
|
||||
|
||||
return class;
|
||||
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue