|
@@ -1037,6 +1037,10 @@ static void handle_hpd_rx_irq(void *param)
|
|
!is_mst_root_connector) {
|
|
!is_mst_root_connector) {
|
|
/* Downstream Port status changed. */
|
|
/* Downstream Port status changed. */
|
|
if (dc_link_detect(dc_link, DETECT_REASON_HPDRX)) {
|
|
if (dc_link_detect(dc_link, DETECT_REASON_HPDRX)) {
|
|
|
|
+
|
|
|
|
+ if (aconnector->fake_enable)
|
|
|
|
+ aconnector->fake_enable = false;
|
|
|
|
+
|
|
amdgpu_dm_update_connector_after_detect(aconnector);
|
|
amdgpu_dm_update_connector_after_detect(aconnector);
|
|
|
|
|
|
|
|
|
|
@@ -2012,30 +2016,32 @@ static void update_stream_scaling_settings(const struct drm_display_mode *mode,
|
|
dst.width = stream->timing.h_addressable;
|
|
dst.width = stream->timing.h_addressable;
|
|
dst.height = stream->timing.v_addressable;
|
|
dst.height = stream->timing.v_addressable;
|
|
|
|
|
|
- rmx_type = dm_state->scaling;
|
|
|
|
- if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
|
|
|
|
- if (src.width * dst.height <
|
|
|
|
- src.height * dst.width) {
|
|
|
|
- /* height needs less upscaling/more downscaling */
|
|
|
|
- dst.width = src.width *
|
|
|
|
- dst.height / src.height;
|
|
|
|
- } else {
|
|
|
|
- /* width needs less upscaling/more downscaling */
|
|
|
|
- dst.height = src.height *
|
|
|
|
- dst.width / src.width;
|
|
|
|
|
|
+ if (dm_state) {
|
|
|
|
+ rmx_type = dm_state->scaling;
|
|
|
|
+ if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
|
|
|
|
+ if (src.width * dst.height <
|
|
|
|
+ src.height * dst.width) {
|
|
|
|
+ /* height needs less upscaling/more downscaling */
|
|
|
|
+ dst.width = src.width *
|
|
|
|
+ dst.height / src.height;
|
|
|
|
+ } else {
|
|
|
|
+ /* width needs less upscaling/more downscaling */
|
|
|
|
+ dst.height = src.height *
|
|
|
|
+ dst.width / src.width;
|
|
|
|
+ }
|
|
|
|
+ } else if (rmx_type == RMX_CENTER) {
|
|
|
|
+ dst = src;
|
|
}
|
|
}
|
|
- } else if (rmx_type == RMX_CENTER) {
|
|
|
|
- dst = src;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- dst.x = (stream->timing.h_addressable - dst.width) / 2;
|
|
|
|
- dst.y = (stream->timing.v_addressable - dst.height) / 2;
|
|
|
|
|
|
+ dst.x = (stream->timing.h_addressable - dst.width) / 2;
|
|
|
|
+ dst.y = (stream->timing.v_addressable - dst.height) / 2;
|
|
|
|
|
|
- if (dm_state->underscan_enable) {
|
|
|
|
- dst.x += dm_state->underscan_hborder / 2;
|
|
|
|
- dst.y += dm_state->underscan_vborder / 2;
|
|
|
|
- dst.width -= dm_state->underscan_hborder;
|
|
|
|
- dst.height -= dm_state->underscan_vborder;
|
|
|
|
|
|
+ if (dm_state->underscan_enable) {
|
|
|
|
+ dst.x += dm_state->underscan_hborder / 2;
|
|
|
|
+ dst.y += dm_state->underscan_vborder / 2;
|
|
|
|
+ dst.width -= dm_state->underscan_hborder;
|
|
|
|
+ dst.height -= dm_state->underscan_vborder;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
stream->src = src;
|
|
stream->src = src;
|
|
@@ -2360,12 +2366,7 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
|
|
|
|
|
|
if (aconnector == NULL) {
|
|
if (aconnector == NULL) {
|
|
DRM_ERROR("aconnector is NULL!\n");
|
|
DRM_ERROR("aconnector is NULL!\n");
|
|
- goto drm_connector_null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (dm_state == NULL) {
|
|
|
|
- DRM_ERROR("dm_state is NULL!\n");
|
|
|
|
- goto dm_state_null;
|
|
|
|
|
|
+ return stream;
|
|
}
|
|
}
|
|
|
|
|
|
drm_connector = &aconnector->base;
|
|
drm_connector = &aconnector->base;
|
|
@@ -2377,18 +2378,18 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
|
|
*/
|
|
*/
|
|
if (aconnector->mst_port) {
|
|
if (aconnector->mst_port) {
|
|
dm_dp_mst_dc_sink_create(drm_connector);
|
|
dm_dp_mst_dc_sink_create(drm_connector);
|
|
- goto mst_dc_sink_create_done;
|
|
|
|
|
|
+ return stream;
|
|
}
|
|
}
|
|
|
|
|
|
if (create_fake_sink(aconnector))
|
|
if (create_fake_sink(aconnector))
|
|
- goto stream_create_fail;
|
|
|
|
|
|
+ return stream;
|
|
}
|
|
}
|
|
|
|
|
|
stream = dc_create_stream_for_sink(aconnector->dc_sink);
|
|
stream = dc_create_stream_for_sink(aconnector->dc_sink);
|
|
|
|
|
|
if (stream == NULL) {
|
|
if (stream == NULL) {
|
|
DRM_ERROR("Failed to create stream for sink!\n");
|
|
DRM_ERROR("Failed to create stream for sink!\n");
|
|
- goto stream_create_fail;
|
|
|
|
|
|
+ return stream;
|
|
}
|
|
}
|
|
|
|
|
|
list_for_each_entry(preferred_mode, &aconnector->base.modes, head) {
|
|
list_for_each_entry(preferred_mode, &aconnector->base.modes, head) {
|
|
@@ -2414,9 +2415,12 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
|
|
} else {
|
|
} else {
|
|
decide_crtc_timing_for_drm_display_mode(
|
|
decide_crtc_timing_for_drm_display_mode(
|
|
&mode, preferred_mode,
|
|
&mode, preferred_mode,
|
|
- dm_state->scaling != RMX_OFF);
|
|
|
|
|
|
+ dm_state ? (dm_state->scaling != RMX_OFF) : false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (!dm_state)
|
|
|
|
+ drm_mode_set_crtcinfo(&mode, 0);
|
|
|
|
+
|
|
fill_stream_properties_from_drm_display_mode(stream,
|
|
fill_stream_properties_from_drm_display_mode(stream,
|
|
&mode, &aconnector->base);
|
|
&mode, &aconnector->base);
|
|
update_stream_scaling_settings(&mode, dm_state, stream);
|
|
update_stream_scaling_settings(&mode, dm_state, stream);
|
|
@@ -2426,10 +2430,8 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
|
|
drm_connector,
|
|
drm_connector,
|
|
aconnector->dc_sink);
|
|
aconnector->dc_sink);
|
|
|
|
|
|
-stream_create_fail:
|
|
|
|
-dm_state_null:
|
|
|
|
-drm_connector_null:
|
|
|
|
-mst_dc_sink_create_done:
|
|
|
|
|
|
+ update_stream_signal(stream);
|
|
|
|
+
|
|
return stream;
|
|
return stream;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2497,6 +2499,27 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
|
|
return &state->base;
|
|
return &state->base;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+static inline int dm_set_vblank(struct drm_crtc *crtc, bool enable)
|
|
|
|
+{
|
|
|
|
+ enum dc_irq_source irq_source;
|
|
|
|
+ struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
|
|
|
|
+ struct amdgpu_device *adev = crtc->dev->dev_private;
|
|
|
|
+
|
|
|
|
+ irq_source = IRQ_TYPE_VBLANK + acrtc->otg_inst;
|
|
|
|
+ return dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int dm_enable_vblank(struct drm_crtc *crtc)
|
|
|
|
+{
|
|
|
|
+ return dm_set_vblank(crtc, true);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void dm_disable_vblank(struct drm_crtc *crtc)
|
|
|
|
+{
|
|
|
|
+ dm_set_vblank(crtc, false);
|
|
|
|
+}
|
|
|
|
+
|
|
/* Implemented only the options currently availible for the driver */
|
|
/* Implemented only the options currently availible for the driver */
|
|
static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
|
|
static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
|
|
.reset = dm_crtc_reset_state,
|
|
.reset = dm_crtc_reset_state,
|
|
@@ -2506,6 +2529,8 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
|
|
.page_flip = drm_atomic_helper_page_flip,
|
|
.page_flip = drm_atomic_helper_page_flip,
|
|
.atomic_duplicate_state = dm_crtc_duplicate_state,
|
|
.atomic_duplicate_state = dm_crtc_duplicate_state,
|
|
.atomic_destroy_state = dm_crtc_destroy_state,
|
|
.atomic_destroy_state = dm_crtc_destroy_state,
|
|
|
|
+ .enable_vblank = dm_enable_vblank,
|
|
|
|
+ .disable_vblank = dm_disable_vblank,
|
|
};
|
|
};
|
|
|
|
|
|
static enum drm_connector_status
|
|
static enum drm_connector_status
|
|
@@ -2800,7 +2825,7 @@ int amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
|
|
goto fail;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
|
|
- stream = dc_create_stream_for_sink(dc_sink);
|
|
|
|
|
|
+ stream = create_stream_for_sink(aconnector, mode, NULL);
|
|
if (stream == NULL) {
|
|
if (stream == NULL) {
|
|
DRM_ERROR("Failed to create stream for sink!\n");
|
|
DRM_ERROR("Failed to create stream for sink!\n");
|
|
goto fail;
|
|
goto fail;
|
|
@@ -3060,6 +3085,9 @@ static int dm_plane_atomic_check(struct drm_plane *plane,
|
|
if (!dm_plane_state->dc_state)
|
|
if (!dm_plane_state->dc_state)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
+ if (!fill_rects_from_plane_state(state, dm_plane_state->dc_state))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
if (dc_validate_plane(dc, dm_plane_state->dc_state) == DC_OK)
|
|
if (dc_validate_plane(dc, dm_plane_state->dc_state) == DC_OK)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -4632,8 +4660,6 @@ static int dm_update_planes_state(struct dc *dc,
|
|
bool pflip_needed = !state->allow_modeset;
|
|
bool pflip_needed = !state->allow_modeset;
|
|
int ret = 0;
|
|
int ret = 0;
|
|
|
|
|
|
- if (pflip_needed)
|
|
|
|
- return ret;
|
|
|
|
|
|
|
|
/* Add new planes */
|
|
/* Add new planes */
|
|
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
|
|
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
|
|
@@ -4648,6 +4674,8 @@ static int dm_update_planes_state(struct dc *dc,
|
|
|
|
|
|
/* Remove any changed/removed planes */
|
|
/* Remove any changed/removed planes */
|
|
if (!enable) {
|
|
if (!enable) {
|
|
|
|
+ if (pflip_needed)
|
|
|
|
+ continue;
|
|
|
|
|
|
if (!old_plane_crtc)
|
|
if (!old_plane_crtc)
|
|
continue;
|
|
continue;
|
|
@@ -4679,6 +4707,7 @@ static int dm_update_planes_state(struct dc *dc,
|
|
*lock_and_validation_needed = true;
|
|
*lock_and_validation_needed = true;
|
|
|
|
|
|
} else { /* Add new planes */
|
|
} else { /* Add new planes */
|
|
|
|
+ struct dc_plane_state *dc_new_plane_state;
|
|
|
|
|
|
if (drm_atomic_plane_disabling(plane->state, new_plane_state))
|
|
if (drm_atomic_plane_disabling(plane->state, new_plane_state))
|
|
continue;
|
|
continue;
|
|
@@ -4692,38 +4721,50 @@ static int dm_update_planes_state(struct dc *dc,
|
|
if (!dm_new_crtc_state->stream)
|
|
if (!dm_new_crtc_state->stream)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
|
|
+ if (pflip_needed)
|
|
|
|
+ continue;
|
|
|
|
|
|
WARN_ON(dm_new_plane_state->dc_state);
|
|
WARN_ON(dm_new_plane_state->dc_state);
|
|
|
|
|
|
- dm_new_plane_state->dc_state = dc_create_plane_state(dc);
|
|
|
|
-
|
|
|
|
- DRM_DEBUG_DRIVER("Enabling DRM plane: %d on DRM crtc %d\n",
|
|
|
|
- plane->base.id, new_plane_crtc->base.id);
|
|
|
|
-
|
|
|
|
- if (!dm_new_plane_state->dc_state) {
|
|
|
|
|
|
+ dc_new_plane_state = dc_create_plane_state(dc);
|
|
|
|
+ if (!dc_new_plane_state) {
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ DRM_DEBUG_DRIVER("Enabling DRM plane: %d on DRM crtc %d\n",
|
|
|
|
+ plane->base.id, new_plane_crtc->base.id);
|
|
|
|
+
|
|
ret = fill_plane_attributes(
|
|
ret = fill_plane_attributes(
|
|
new_plane_crtc->dev->dev_private,
|
|
new_plane_crtc->dev->dev_private,
|
|
- dm_new_plane_state->dc_state,
|
|
|
|
|
|
+ dc_new_plane_state,
|
|
new_plane_state,
|
|
new_plane_state,
|
|
new_crtc_state);
|
|
new_crtc_state);
|
|
- if (ret)
|
|
|
|
|
|
+ if (ret) {
|
|
|
|
+ dc_plane_state_release(dc_new_plane_state);
|
|
return ret;
|
|
return ret;
|
|
|
|
+ }
|
|
|
|
|
|
-
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Any atomic check errors that occur after this will
|
|
|
|
+ * not need a release. The plane state will be attached
|
|
|
|
+ * to the stream, and therefore part of the atomic
|
|
|
|
+ * state. It'll be released when the atomic state is
|
|
|
|
+ * cleaned.
|
|
|
|
+ */
|
|
if (!dc_add_plane_to_context(
|
|
if (!dc_add_plane_to_context(
|
|
dc,
|
|
dc,
|
|
dm_new_crtc_state->stream,
|
|
dm_new_crtc_state->stream,
|
|
- dm_new_plane_state->dc_state,
|
|
|
|
|
|
+ dc_new_plane_state,
|
|
dm_state->context)) {
|
|
dm_state->context)) {
|
|
|
|
|
|
|
|
+ dc_plane_state_release(dc_new_plane_state);
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ dm_new_plane_state->dc_state = dc_new_plane_state;
|
|
|
|
+
|
|
/* Tell DC to do a full surface update every time there
|
|
/* Tell DC to do a full surface update every time there
|
|
* is a plane change. Inefficient, but works for now.
|
|
* is a plane change. Inefficient, but works for now.
|
|
*/
|
|
*/
|
|
@@ -4737,6 +4778,30 @@ static int dm_update_planes_state(struct dc *dc,
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int dm_atomic_check_plane_state_fb(struct drm_atomic_state *state,
|
|
|
|
+ struct drm_crtc *crtc)
|
|
|
|
+{
|
|
|
|
+ struct drm_plane *plane;
|
|
|
|
+ struct drm_crtc_state *crtc_state;
|
|
|
|
+
|
|
|
|
+ WARN_ON(!drm_atomic_get_new_crtc_state(state, crtc));
|
|
|
|
+
|
|
|
|
+ drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) {
|
|
|
|
+ struct drm_plane_state *plane_state =
|
|
|
|
+ drm_atomic_get_plane_state(state, plane);
|
|
|
|
+
|
|
|
|
+ if (IS_ERR(plane_state))
|
|
|
|
+ return -EDEADLK;
|
|
|
|
+
|
|
|
|
+ crtc_state = drm_atomic_get_crtc_state(plane_state->state, crtc);
|
|
|
|
+ if (crtc->primary == plane && crtc_state->active) {
|
|
|
|
+ if (!plane_state->fb)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
|
static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
|
struct drm_atomic_state *state)
|
|
struct drm_atomic_state *state)
|
|
{
|
|
{
|
|
@@ -4760,6 +4825,10 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
|
goto fail;
|
|
goto fail;
|
|
|
|
|
|
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
|
|
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
|
|
|
|
+ ret = dm_atomic_check_plane_state_fb(state, crtc);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto fail;
|
|
|
|
+
|
|
if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
|
|
if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
|
|
!new_crtc_state->color_mgmt_changed)
|
|
!new_crtc_state->color_mgmt_changed)
|
|
continue;
|
|
continue;
|