Skip to content

Commit

Permalink
drm: apple: Extract modeset crtc's atomic_flush()
Browse files Browse the repository at this point in the history
Triggering modesets from drm_connector_helper_funcs.atomic_check is more
in line with DRM/KMS' design and allows returning errors from failed
modesets.
Ignore hotplug callbacks from DCP during modeset. DCP always does
disconnected -> connected on (at least the initial) modeset. Shield drm
helpers from this.
This improves reliability with externel (dptx based) displays.

Signed-off-by: Janne Grunau <j@jannau.net>
  • Loading branch information
jannau authored and marcan committed Nov 23, 2023
1 parent ddd996d commit 99d7bb8
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 61 deletions.
2 changes: 2 additions & 0 deletions drivers/gpu/drm/apple/apple_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ static const struct drm_connector_funcs apple_connector_funcs = {
static const struct drm_connector_helper_funcs apple_connector_helper_funcs = {
.get_modes = dcp_get_modes,
.mode_valid = dcp_mode_valid,
.atomic_check = dcp_connector_atomic_check,

};

static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = {
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/apple/dcp-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ struct apple_dcp {
u32 last_swap_id;

/* Current display mode */
bool during_modeset;
bool valid_mode;
struct dcp_set_digital_out_mode_req mode;

Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/apple/dcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc);
int dcp_get_modes(struct drm_connector *connector);
int dcp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode);
int dcp_connector_atomic_check(struct drm_connector *connector,
struct drm_atomic_state *state);
bool dcp_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
Expand Down
41 changes: 41 additions & 0 deletions drivers/gpu/drm/apple/iomfb.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,47 @@ int dcp_mode_valid(struct drm_connector *connector,
}
EXPORT_SYMBOL_GPL(dcp_mode_valid);

int dcp_connector_atomic_check(struct drm_connector *connector,
struct drm_atomic_state *state)
{
struct apple_connector *apple_connector = to_apple_connector(connector);
struct platform_device *pdev = apple_connector->dcp;
struct apple_dcp *dcp = platform_get_drvdata(pdev);
struct drm_crtc *crtc = &dcp->crtc->base;
struct drm_crtc_state *crtc_state;
int ret = -EIO;
bool modeset;

crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (!crtc_state)
return 0;

modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode;

if (!modeset)
return 0;

/* ignore no mode, poweroff is handled elsewhere */
if (crtc_state->mode.hdisplay == 0 && crtc_state->mode.vdisplay == 0)
return 0;

switch (dcp->fw_compat) {
case DCP_FIRMWARE_V_12_3:
ret = iomfb_modeset_v12_3(dcp, crtc_state);
break;
case DCP_FIRMWARE_V_13_5:
ret = iomfb_modeset_v13_3(dcp, crtc_state);
break;
default:
WARN_ONCE(true, "Unexpected firmware version: %u\n",
dcp->fw_compat);
break;
}

return ret;
}
EXPORT_SYMBOL_GPL(dcp_connector_atomic_check);

bool dcp_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
Expand Down
137 changes: 76 additions & 61 deletions drivers/gpu/drm/apple/iomfb_template.c
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,13 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected)
if (dcp->main_display)
return;

if (dcp->during_modeset) {
dev_info(dcp->dev,
"cb_hotplug() ignored during modeset connected:%llu\n",
*connected);
return;
}

dev_info(dcp->dev, "cb_hotplug() connected:%llu, valid_mode:%d\n",
*connected, dcp->valid_mode);

Expand Down Expand Up @@ -1178,6 +1185,75 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data,
}
}

int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp,
struct drm_crtc_state *crtc_state)
{
struct dcp_display_mode *mode;
struct dcp_wait_cookie *cookie;
int ret;

mode = lookup_mode(dcp, &crtc_state->mode);
if (!mode) {
dev_err(dcp->dev, "no match for " DRM_MODE_FMT "\n",
DRM_MODE_ARG(&crtc_state->mode));
return -EIO;
}

dev_info(dcp->dev,
"set_digital_out_mode(color:%d timing:%d) " DRM_MODE_FMT "\n",
mode->color_mode_id, mode->timing_mode_id,
DRM_MODE_ARG(&crtc_state->mode));
dcp->mode = (struct dcp_set_digital_out_mode_req){
.color_mode_id = mode->color_mode_id,
.timing_mode_id = mode->timing_mode_id
};

cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
if (!cookie) {
return -ENOMEM;
}

init_completion(&cookie->done);
kref_init(&cookie->refcount);
/* increase refcount to ensure the receiver has a reference */
kref_get(&cookie->refcount);

dcp->during_modeset = true;

dcp_set_digital_out_mode(dcp, false, &dcp->mode,
complete_set_digital_out_mode, cookie);

/*
* The DCP firmware has an internal timeout of ~8 seconds for
* modesets. Add an extra 500ms to safe side that the modeset
* call has returned.
*/
dev_dbg(dcp->dev, "%s - wait for modeset", __func__);
ret = wait_for_completion_timeout(&cookie->done,
msecs_to_jiffies(8500));

kref_put(&cookie->refcount, release_wait_cookie);
dcp->during_modeset = false;
dev_info(dcp->dev, "set_digital_out_mode finished:%d\n", ret);

if (ret == 0) {
dev_info(dcp->dev, "set_digital_out_mode timed out\n");
return -EIO;
} else if (ret < 0) {
dev_info(dcp->dev,
"waiting on set_digital_out_mode failed:%d\n", ret);
return -EIO;

} else if (ret > 0) {
dev_dbg(dcp->dev,
"set_digital_out_mode finished with %d to spare\n",
jiffies_to_msecs(ret));
}
dcp->valid_mode = true;

return 0;
}

void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state)
{
struct drm_plane *plane;
Expand All @@ -1186,13 +1262,10 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru
struct DCP_FW_NAME(dcp_swap_submit_req) *req = &DCP_FW_UNION(dcp->swap);
int plane_idx, l;
int has_surface = 0;
bool modeset;
dev_dbg(dcp->dev, "%s", __func__);

crtc_state = drm_atomic_get_new_crtc_state(state, crtc);

modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode;

/* Reset to defaults */
memset(req, 0, sizeof(*req));
for (l = 0; l < SWAP_SURFACES; l++)
Expand Down Expand Up @@ -1305,64 +1378,6 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru
l += 1;
}

if (modeset) {
struct dcp_display_mode *mode;
struct dcp_wait_cookie *cookie;
int ret;

mode = lookup_mode(dcp, &crtc_state->mode);
if (!mode) {
dev_warn(dcp->dev, "no match for " DRM_MODE_FMT,
DRM_MODE_ARG(&crtc_state->mode));
schedule_work(&dcp->vblank_wq);
return;
}

dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)",
mode->color_mode_id, mode->timing_mode_id);
dcp->mode = (struct dcp_set_digital_out_mode_req){
.color_mode_id = mode->color_mode_id,
.timing_mode_id = mode->timing_mode_id
};

cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
if (!cookie) {
schedule_work(&dcp->vblank_wq);
return;
}

init_completion(&cookie->done);
kref_init(&cookie->refcount);
/* increase refcount to ensure the receiver has a reference */
kref_get(&cookie->refcount);

dcp_set_digital_out_mode(dcp, false, &dcp->mode,
complete_set_digital_out_mode, cookie);

/*
* The DCP firmware has an internal timeout of ~8 seconds for
* modesets. Add an extra 500ms to safe side that the modeset
* call has returned.
*/
dev_dbg(dcp->dev, "%s - wait for modeset", __func__);
ret = wait_for_completion_timeout(&cookie->done,
msecs_to_jiffies(8500));

kref_put(&cookie->refcount, release_wait_cookie);

if (ret == 0) {
dev_info(dcp->dev, "set_digital_out_mode timed out");
schedule_work(&dcp->vblank_wq);
return;
} else if (ret > 0) {
dev_dbg(dcp->dev,
"set_digital_out_mode finished with %d to spare",
jiffies_to_msecs(ret));
}

dcp->valid_mode = true;
}

if (!has_surface && !crtc_state->color_mgmt_changed) {
if (crtc_state->enable && crtc_state->active &&
!crtc_state->planes_changed) {
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/apple/iomfb_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ struct DCP_FW_NAME(dcp_map_reg_resp) {

struct apple_dcp;

int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp,
struct drm_crtc_state *crtc_state);
void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state);
void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp);
void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp);
Expand Down

0 comments on commit 99d7bb8

Please sign in to comment.