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