Disable PIN 1 of APIC timer
Can be useful to work around chipset bugs.
+ dis_ucode_ldr [X86] Disable the microcode loader.
+
dma_debug=off If the kernel is compiled with DMA_API_DEBUG support,
this option disables the debugging code at boot.
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
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]
<bus_id>,<clkrate>
i8042.debug [HW] Toggle i8042 debug mode
+ i8042.unmask_kbd_data
+ [HW] Enable printing of interrupt data from the KBD port
+ (disabled by default, and as a pre-condition
+ requires that i8042.debug=1 be enabled)
i8042.direct [HW] Put keyboard port into non-translated mode
i8042.dumbkbd [HW] Pretend that controller can only read data from
keyboard and cannot control its state
The default parameter value of '0' causes the kernel
not to attempt recovery of lost locks.
+ nfs4.layoutstats_timer =
+ [NFSv4.2] Change the rate at which the kernel sends
+ layoutstats to the pNFS metadata server.
+
+ Setting this to value to 0 causes the kernel to use
+ whatever value is the default set by the layout
+ driver. A non-zero value sets the minimum interval
+ in seconds between layoutstats transmissions.
+
nfsd.nfs4_disable_idmapping=
[NFSv4] When set to the default of '1', the NFSv4
server will return only numeric uids and gids to
in a given burst of a callback-flood test.
rcutorture.fqs_duration= [KNL]
- Set duration of force_quiescent_state bursts.
+ Set duration of force_quiescent_state bursts
+ in microseconds.
rcutorture.fqs_holdoff= [KNL]
- Set holdoff time within force_quiescent_state bursts.
+ Set holdoff time within force_quiescent_state bursts
+ in microseconds.
rcutorture.fqs_stutter= [KNL]
- Set wait time between force_quiescent_state bursts.
+ Set wait time between force_quiescent_state bursts
+ in seconds.
+
+ rcutorture.gp_cond= [KNL]
+ Use conditional/asynchronous update-side
+ primitives, if available.
rcutorture.gp_exp= [KNL]
- Use expedited update-side primitives.
+ Use expedited update-side primitives, if available.
rcutorture.gp_normal= [KNL]
- Use normal (non-expedited) update-side primitives.
- If both gp_exp and gp_normal are set, do both.
- If neither gp_exp nor gp_normal are set, still
- do both.
+ Use normal (non-expedited) asynchronous
+ update-side primitives, if available.
+
+ rcutorture.gp_sync= [KNL]
+ Use normal (non-expedited) synchronous
+ update-side primitives, if available. If all
+ of rcutorture.gp_cond=, rcutorture.gp_exp=,
+ rcutorture.gp_normal=, and rcutorture.gp_sync=
+ are zero, rcutorture acts as if is interpreted
+ they are all non-zero.
rcutorture.n_barrier_cbs= [KNL]
Set callbacks/threads for rcu_barrier() testing.
Set time (s) between CPU-hotplug operations, or
zero to disable CPU-hotplug testing.
- rcutorture.torture_runnable= [BOOT]
- Start rcutorture running at boot time.
-
rcutorture.shuffle_interval= [KNL]
Set task-shuffle interval (s). Shuffling tasks
allows some CPUs to go into dyntick-idle mode
Test RCU's dyntick-idle handling. See also the
rcutorture.shuffle_interval parameter.
+ rcutorture.torture_runnable= [BOOT]
+ Start rcutorture running at boot time.
+
rcutorture.torture_type= [KNL]
Specify the RCU implementation to test.
plus one apbt timer for broadcast timer.
x86_intel_mid_timer=apbt_only | lapic_and_apbt
+ xen_512gb_limit [KNL,X86-64,XEN]
+ Restricts the kernel running paravirtualized under Xen
+ to use only up to 512 GB of RAM. The reason to do so is
+ crash analysis tools and Xen tools for doing domain
+ save/restore/migration must be enabled to handle larger
+ domains.
+
xen_emul_unplug= [HW,X86,XEN]
Unplug Xen emulated devices
Format: [unplug0,][unplug1]
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
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;
}
copied_props++;
}
- if (obj->type == DRM_MODE_OBJECT_PLANE && count_props) {
+ if (obj->type == DRM_MODE_OBJECT_PLANE && count_props &&
+ !(arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)) {
plane = obj_to_plane(obj);
plane_mask |= (1 << drm_plane_index(plane));
plane->old_fb = plane->fb;
}
if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
+ /*
+ * Unlike commit, check_only does not clean up state.
+ * Below we call drm_atomic_state_free for it.
+ */
ret = drm_atomic_check_only(state);
- /* _check_only() does not free state, unlike _commit() */
- if (!ret)
- drm_atomic_state_free(state);
} else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
ret = drm_atomic_async_commit(state);
} else {
plane->old_fb = NULL;
}
+ if (ret && arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+ /*
+ * TEST_ONLY and PAGE_FLIP_EVENT are mutually exclusive,
+ * if they weren't, this code should be called on success
+ * for TEST_ONLY too.
+ */
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ if (!crtc_state->event)
+ continue;
+
+ destroy_vblank_event(dev, file_priv,
+ crtc_state->event);
+ }
+ }
+
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
drm_modeset_backoff(&ctx);
goto retry;
}
- if (ret) {
- if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
- for_each_crtc_in_state(state, crtc, crtc_state, i) {
- if (!crtc_state->event)
- continue;
-
- destroy_vblank_event(dev, file_priv,
- crtc_state->event);
- }
- }
-
+ if (ret || arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)
drm_atomic_state_free(state);
- }
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
* 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.
*/
* This function shuts down all the outputs that need to be shut down and
* prepares them (if required) with the new mode.
*
- * For compatability with legacy crtc helpers this should be called before
+ * For compatibility with legacy crtc helpers this should be called before
* drm_atomic_helper_commit_planes(), which is what the default commit function
* does. But drivers with different needs can group the modeset commits together
* and do the plane commits at the end. This is useful for drivers doing runtime
* This function enables all the outputs with the new configuration which had to
* be turned off for the update.
*
- * For compatability with legacy crtc helpers this should be called after
+ * For compatibility with legacy crtc helpers this should be called after
* drm_atomic_helper_commit_planes(), which is what the default commit function
* does. But drivers with different needs can group the modeset commits together
* and do the plane commits at the end. This is useful for drivers doing runtime
* 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.
*/
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);
* 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
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;
}
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);
}
}
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
* 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;
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);
}
if (!funcs || !funcs->atomic_flush)
continue;
+ if (active_only && !crtc->state->active)
+ continue;
+
funcs->atomic_flush(crtc, old_crtc_state);
}
}
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);
*
* 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,
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)
}
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)
{
{
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);
state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
retry:
- crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
+ ret = __drm_atomic_helper_set_config(set, state);
+ if (ret != 0)
goto fail;
- }
- primary_state = drm_atomic_get_plane_state(state, crtc->primary);
- if (IS_ERR(primary_state)) {
- ret = PTR_ERR(primary_state);
+ ret = drm_atomic_commit(state);
+ if (ret != 0)
goto fail;
- }
+
+ /* Driver takes ownership of state on successful commit. */
+ return 0;
+fail:
+ if (ret == -EDEADLK)
+ goto backoff;
+
+ drm_atomic_state_free(state);
+
+ return ret;
+backoff:
+ drm_atomic_state_clear(state);
+ drm_atomic_legacy_backoff(state);
+
+ /*
+ * Someone might have exchanged the framebuffer while we dropped locks
+ * in the backoff code. We need to fix up the fb refcount tracking the
+ * core does for us.
+ */
+ crtc->primary->old_fb = crtc->primary->fb;
+
+ goto retry;
+}
+EXPORT_SYMBOL(drm_atomic_helper_set_config);
+
+/* just used from fb-helper and atomic-helper: */
+int __drm_atomic_helper_set_config(struct drm_mode_set *set,
+ struct drm_atomic_state *state)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_plane_state *primary_state;
+ struct drm_crtc *crtc = set->crtc;
+ int ret;
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ primary_state = drm_atomic_get_plane_state(state, crtc->primary);
+ if (IS_ERR(primary_state))
+ return PTR_ERR(primary_state);
if (!set->mode) {
WARN_ON(set->fb);
ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
if (ret != 0)
- goto fail;
+ return ret;
crtc_state->active = false;
ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
if (ret != 0)
- goto fail;
+ return ret;
drm_atomic_set_fb_for_plane(primary_state, NULL);
ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
if (ret != 0)
- goto fail;
+ return ret;
crtc_state->active = true;
ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
if (ret != 0)
- goto fail;
+ return ret;
+
drm_atomic_set_fb_for_plane(primary_state, set->fb);
primary_state->crtc_x = 0;
primary_state->crtc_y = 0;
commit:
ret = update_output_state(state, set);
if (ret)
- goto fail;
-
- ret = drm_atomic_commit(state);
- if (ret != 0)
- goto fail;
+ return ret;
- /* Driver takes ownership of state on successful commit. */
return 0;
-fail:
- if (ret == -EDEADLK)
- goto backoff;
-
- drm_atomic_state_free(state);
-
- return ret;
-backoff:
- drm_atomic_state_clear(state);
- drm_atomic_legacy_backoff(state);
-
- /*
- * Someone might have exchanged the framebuffer while we dropped locks
- * in the backoff code. We need to fix up the fb refcount tracking the
- * core does for us.
- */
- crtc->primary->old_fb = crtc->primary->fb;
-
- goto retry;
}
-EXPORT_SYMBOL(drm_atomic_helper_set_config);
/**
* drm_atomic_helper_crtc_set_property - helper for crtc properties
EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
/**
+ * drm_atomic_helper_duplicate_state - duplicate an atomic state object
+ * @dev: DRM device
+ * @ctx: lock acquisition context
+ *
+ * Makes a copy of the current atomic state by looping over all objects and
+ * duplicating their respective states.
+ *
+ * Note that this treats atomic state as persistent between save and restore.
+ * Drivers must make sure that this is possible and won't result in confusion
+ * or erroneous behaviour.
+ *
+ * Note that if callers haven't already acquired all modeset locks this might
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
+ *
+ * Returns:
+ * A pointer to the copy of the atomic state object on success or an
+ * ERR_PTR()-encoded error code on failure.
+ */
+struct drm_atomic_state *
+drm_atomic_helper_duplicate_state(struct drm_device *dev,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ struct drm_atomic_state *state;
+ struct drm_connector *conn;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+ int err = 0;
+
+ state = drm_atomic_state_alloc(dev);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ state->acquire_ctx = ctx;
+
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state)) {
+ err = PTR_ERR(crtc_state);
+ goto free;
+ }
+ }
+
+ drm_for_each_plane(plane, dev) {
+ struct drm_plane_state *plane_state;
+
+ plane_state = drm_atomic_get_plane_state(state, plane);
+ if (IS_ERR(plane_state)) {
+ err = PTR_ERR(plane_state);
+ goto free;
+ }
+ }
+
+ drm_for_each_connector(conn, dev) {
+ struct drm_connector_state *conn_state;
+
+ conn_state = drm_atomic_get_connector_state(state, conn);
+ if (IS_ERR(conn_state)) {
+ err = PTR_ERR(conn_state);
+ goto free;
+ }
+ }
+
+ /* clear the acquire context so that it isn't accidentally reused */
+ state->acquire_ctx = NULL;
+
+free:
+ if (err < 0) {
+ drm_atomic_state_free(state);
+ state = ERR_PTR(err);
+ }
+
+ return state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_duplicate_state);
+
+/**
* __drm_atomic_helper_connector_destroy_state - release connector state
* @connector: connector object
* @state: connector state object to release
}
EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate);
+ #define AUX_RETRY_INTERVAL 500 /* us */
+
/**
* DOC: dp helpers
*
return -EIO;
case DP_AUX_NATIVE_REPLY_DEFER:
- usleep_range(400, 500);
+ usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
break;
}
}
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
+ #define AUX_CMD_LEN 4
+ #define AUX_ADDRESS_LEN 20
+ #define AUX_REPLY_PAD_LEN 4
+ #define AUX_LENGTH_LEN 8
+
+ /*
+ * Calculate the duration of the AUX request/reply in usec. Gives the
+ * "best" case estimate, ie. successful while as short as possible.
+ */
+ static int drm_dp_aux_req_duration(const struct drm_dp_aux_msg *msg)
+ {
+ int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
+ AUX_CMD_LEN + AUX_ADDRESS_LEN + AUX_LENGTH_LEN;
+
+ if ((msg->request & DP_AUX_I2C_READ) == 0)
+ len += msg->size * 8;
+
+ return len;
+ }
+
+ static int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg)
+ {
+ int len = AUX_PRECHARGE_LEN + AUX_SYNC_LEN + AUX_STOP_LEN +
+ AUX_CMD_LEN + AUX_REPLY_PAD_LEN;
+
+ /*
+ * For read we expect what was asked. For writes there will
+ * be 0 or 1 data bytes. Assume 0 for the "best" case.
+ */
+ if (msg->request & DP_AUX_I2C_READ)
+ len += msg->size * 8;
+
+ return len;
+ }
+
+ #define I2C_START_LEN 1
+ #define I2C_STOP_LEN 1
+ #define I2C_ADDR_LEN 9 /* ADDRESS + R/W + ACK/NACK */
+ #define I2C_DATA_LEN 9 /* DATA + ACK/NACK */
+
+ /*
+ * Calculate the length of the i2c transfer in usec, assuming
+ * the i2c bus speed is as specified. Gives the the "worst"
+ * case estimate, ie. successful while as long as possible.
+ * Doesn't account the the "MOT" bit, and instead assumes each
+ * message includes a START, ADDRESS and STOP. Neither does it
+ * account for additional random variables such as clock stretching.
+ */
+ static int drm_dp_i2c_msg_duration(const struct drm_dp_aux_msg *msg,
+ int i2c_speed_khz)
+ {
+ /* AUX bitrate is 1MHz, i2c bitrate as specified */
+ return DIV_ROUND_UP((I2C_START_LEN + I2C_ADDR_LEN +
+ msg->size * I2C_DATA_LEN +
+ I2C_STOP_LEN) * 1000, i2c_speed_khz);
+ }
+
+ /*
+ * Deterine how many retries should be attempted to successfully transfer
+ * the specified message, based on the estimated durations of the
+ * i2c and AUX transfers.
+ */
+ static int drm_dp_i2c_retry_count(const struct drm_dp_aux_msg *msg,
+ int i2c_speed_khz)
+ {
+ int aux_time_us = drm_dp_aux_req_duration(msg) +
+ drm_dp_aux_reply_duration(msg);
+ int i2c_time_us = drm_dp_i2c_msg_duration(msg, i2c_speed_khz);
+
+ return DIV_ROUND_UP(i2c_time_us, aux_time_us + AUX_RETRY_INTERVAL);
+ }
+
+ /*
+ * FIXME currently assumes 10 kHz as some real world devices seem
+ * to require it. We should query/set the speed via DPCD if supported.
+ */
+ static int dp_aux_i2c_speed_khz __read_mostly = 10;
+ module_param_unsafe(dp_aux_i2c_speed_khz, int, 0644);
+ MODULE_PARM_DESC(dp_aux_i2c_speed_khz,
+ "Assumed speed of the i2c bus in kHz, (1-400, default 10)");
+
/*
* Transfer a single I2C-over-AUX message and handle various error conditions,
* retrying the transaction as appropriate. It is assumed that the
{
unsigned int retry, defer_i2c;
int ret;
-
/*
* DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
* is required to retry at least seven times upon receiving AUX_DEFER
* before giving up the AUX transaction.
+ *
+ * We also try to account for the i2c bus speed.
*/
- for (retry = 0, defer_i2c = 0; retry < (7 + defer_i2c); retry++) {
+ int max_retries = max(7, drm_dp_i2c_retry_count(msg, dp_aux_i2c_speed_khz));
+
+ for (retry = 0, defer_i2c = 0; retry < (max_retries + defer_i2c); retry++) {
mutex_lock(&aux->hw_mutex);
ret = aux->transfer(aux, msg);
mutex_unlock(&aux->hw_mutex);
* For now just defer for long enough to hopefully be
* safe for all use-cases.
*/
- usleep_range(500, 600);
+ usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
continue;
default:
* 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:
aux->i2c_defer_count++;
if (defer_i2c < 7)
defer_i2c++;
- usleep_range(400, 500);
+ usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+ drm_dp_i2c_msg_write_status_update(msg);
++
continue;
default:
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.
*
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.
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
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;
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;
struct drm_connector *connector;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_audio_component *acomp = dev_priv->audio_component;
+ 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;
if (dev_priv->display.audio_codec_enable)
dev_priv->display.audio_codec_enable(connector, intel_encoder, mode);
+
+ if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify)
+ acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port);
}
/**
* The disable sequences must be performed before disabling the transcoder or
* port.
*/
- void intel_audio_codec_disable(struct intel_encoder *encoder)
+ void intel_audio_codec_disable(struct intel_encoder *intel_encoder)
{
- struct drm_device *dev = encoder->base.dev;
+ struct drm_encoder *encoder = &intel_encoder->base;
+ struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_audio_component *acomp = dev_priv->audio_component;
+ struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
+ enum port port = intel_dig_port->port;
if (dev_priv->display.audio_codec_disable)
- dev_priv->display.audio_codec_disable(encoder);
+ dev_priv->display.audio_codec_disable(intel_encoder);
+
+ if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify)
+ acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, (int) port);
}
/**
struct device *hda_dev, void *data)
{
struct i915_audio_component *acomp = data;
+ struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
if (WARN_ON(acomp->ops || acomp->dev))
return -EEXIST;
+ drm_modeset_lock_all(dev_priv->dev);
acomp->ops = &i915_audio_component_ops;
acomp->dev = i915_dev;
+ dev_priv->audio_component = acomp;
+ drm_modeset_unlock_all(dev_priv->dev);
return 0;
}
struct device *hda_dev, void *data)
{
struct i915_audio_component *acomp = data;
+ struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
+ drm_modeset_lock_all(dev_priv->dev);
acomp->ops = NULL;
acomp->dev = NULL;
+ dev_priv->audio_component = NULL;
+ drm_modeset_unlock_all(dev_priv->dev);
}
static const struct component_ops i915_audio_component_bind_ops = {
connector->base.name);
if (connector->get_hw_state(connector)) {
- struct drm_encoder *encoder = &connector->encoder->base;
+ struct intel_encoder *encoder = connector->encoder;
struct drm_connector_state *conn_state = connector->base.state;
I915_STATE_WARN(!crtc,
I915_STATE_WARN(!crtc->state->active,
"connector is active, but attached crtc isn't\n");
- if (!encoder)
+ if (!encoder || encoder->type == INTEL_OUTPUT_DP_MST)
return;
- I915_STATE_WARN(conn_state->best_encoder != encoder,
+ I915_STATE_WARN(conn_state->best_encoder != &encoder->base,
"atomic encoder doesn't match attached encoder\n");
- I915_STATE_WARN(conn_state->crtc != encoder->crtc,
+ I915_STATE_WARN(conn_state->crtc != encoder->base.crtc,
"attached encoder crtc differs from connector crtc\n");
} else {
I915_STATE_WARN(crtc && crtc->state->active,
*/
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);
*/
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);
}
}