Skip to content

Commit 9cd0ab7

Browse files
committed
drm: apple: Extract modeset crtc's atomic_flush()
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>
1 parent 4a8fc9d commit 9cd0ab7

6 files changed

Lines changed: 124 additions & 61 deletions

File tree

drivers/gpu/drm/apple/apple_drv.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ static const struct drm_connector_funcs apple_connector_funcs = {
300300
static const struct drm_connector_helper_funcs apple_connector_helper_funcs = {
301301
.get_modes = dcp_get_modes,
302302
.mode_valid = dcp_mode_valid,
303+
.atomic_check = dcp_connector_atomic_check,
304+
303305
};
304306

305307
static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = {

drivers/gpu/drm/apple/dcp-internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ struct apple_dcp {
172172
u32 last_swap_id;
173173

174174
/* Current display mode */
175+
bool during_modeset;
175176
bool valid_mode;
176177
struct dcp_set_digital_out_mode_req mode;
177178

drivers/gpu/drm/apple/dcp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ void dcp_drm_crtc_vblank(struct apple_crtc *crtc);
5757
int dcp_get_modes(struct drm_connector *connector);
5858
int dcp_mode_valid(struct drm_connector *connector,
5959
struct drm_display_mode *mode);
60+
int dcp_connector_atomic_check(struct drm_connector *connector,
61+
struct drm_atomic_state *state);
6062
bool dcp_crtc_mode_fixup(struct drm_crtc *crtc,
6163
const struct drm_display_mode *mode,
6264
struct drm_display_mode *adjusted_mode);

drivers/gpu/drm/apple/iomfb.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,47 @@ int dcp_mode_valid(struct drm_connector *connector,
422422
}
423423
EXPORT_SYMBOL_GPL(dcp_mode_valid);
424424

425+
int dcp_connector_atomic_check(struct drm_connector *connector,
426+
struct drm_atomic_state *state)
427+
{
428+
struct apple_connector *apple_connector = to_apple_connector(connector);
429+
struct platform_device *pdev = apple_connector->dcp;
430+
struct apple_dcp *dcp = platform_get_drvdata(pdev);
431+
struct drm_crtc *crtc = &dcp->crtc->base;
432+
struct drm_crtc_state *crtc_state;
433+
int ret = -EIO;
434+
bool modeset;
435+
436+
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
437+
if (!crtc_state)
438+
return 0;
439+
440+
modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode;
441+
442+
if (!modeset)
443+
return 0;
444+
445+
/* ignore no mode, poweroff is handled elsewhere */
446+
if (crtc_state->mode.hdisplay == 0 && crtc_state->mode.vdisplay == 0)
447+
return 0;
448+
449+
switch (dcp->fw_compat) {
450+
case DCP_FIRMWARE_V_12_3:
451+
ret = iomfb_modeset_v12_3(dcp, crtc_state);
452+
break;
453+
case DCP_FIRMWARE_V_13_5:
454+
ret = iomfb_modeset_v13_3(dcp, crtc_state);
455+
break;
456+
default:
457+
WARN_ONCE(true, "Unexpected firmware version: %u\n",
458+
dcp->fw_compat);
459+
break;
460+
}
461+
462+
return ret;
463+
}
464+
EXPORT_SYMBOL_GPL(dcp_connector_atomic_check);
465+
425466
bool dcp_crtc_mode_fixup(struct drm_crtc *crtc,
426467
const struct drm_display_mode *mode,
427468
struct drm_display_mode *adjusted_mode)

drivers/gpu/drm/apple/iomfb_template.c

Lines changed: 76 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,13 @@ static void dcpep_cb_hotplug(struct apple_dcp *dcp, u64 *connected)
10141014
if (dcp->main_display)
10151015
return;
10161016

1017+
if (dcp->during_modeset) {
1018+
dev_info(dcp->dev,
1019+
"cb_hotplug() ignored during modeset connected:%llu\n",
1020+
*connected);
1021+
return;
1022+
}
1023+
10171024
dev_info(dcp->dev, "cb_hotplug() connected:%llu, valid_mode:%d\n",
10181025
*connected, dcp->valid_mode);
10191026

@@ -1178,6 +1185,75 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data,
11781185
}
11791186
}
11801187

1188+
int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp,
1189+
struct drm_crtc_state *crtc_state)
1190+
{
1191+
struct dcp_display_mode *mode;
1192+
struct dcp_wait_cookie *cookie;
1193+
int ret;
1194+
1195+
mode = lookup_mode(dcp, &crtc_state->mode);
1196+
if (!mode) {
1197+
dev_err(dcp->dev, "no match for " DRM_MODE_FMT "\n",
1198+
DRM_MODE_ARG(&crtc_state->mode));
1199+
return -EIO;
1200+
}
1201+
1202+
dev_info(dcp->dev,
1203+
"set_digital_out_mode(color:%d timing:%d) " DRM_MODE_FMT "\n",
1204+
mode->color_mode_id, mode->timing_mode_id,
1205+
DRM_MODE_ARG(&crtc_state->mode));
1206+
dcp->mode = (struct dcp_set_digital_out_mode_req){
1207+
.color_mode_id = mode->color_mode_id,
1208+
.timing_mode_id = mode->timing_mode_id
1209+
};
1210+
1211+
cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
1212+
if (!cookie) {
1213+
return -ENOMEM;
1214+
}
1215+
1216+
init_completion(&cookie->done);
1217+
kref_init(&cookie->refcount);
1218+
/* increase refcount to ensure the receiver has a reference */
1219+
kref_get(&cookie->refcount);
1220+
1221+
dcp->during_modeset = true;
1222+
1223+
dcp_set_digital_out_mode(dcp, false, &dcp->mode,
1224+
complete_set_digital_out_mode, cookie);
1225+
1226+
/*
1227+
* The DCP firmware has an internal timeout of ~8 seconds for
1228+
* modesets. Add an extra 500ms to safe side that the modeset
1229+
* call has returned.
1230+
*/
1231+
dev_dbg(dcp->dev, "%s - wait for modeset", __func__);
1232+
ret = wait_for_completion_timeout(&cookie->done,
1233+
msecs_to_jiffies(8500));
1234+
1235+
kref_put(&cookie->refcount, release_wait_cookie);
1236+
dcp->during_modeset = false;
1237+
dev_info(dcp->dev, "set_digital_out_mode finished:%d\n", ret);
1238+
1239+
if (ret == 0) {
1240+
dev_info(dcp->dev, "set_digital_out_mode timed out\n");
1241+
return -EIO;
1242+
} else if (ret < 0) {
1243+
dev_info(dcp->dev,
1244+
"waiting on set_digital_out_mode failed:%d\n", ret);
1245+
return -EIO;
1246+
1247+
} else if (ret > 0) {
1248+
dev_dbg(dcp->dev,
1249+
"set_digital_out_mode finished with %d to spare\n",
1250+
jiffies_to_msecs(ret));
1251+
}
1252+
dcp->valid_mode = true;
1253+
1254+
return 0;
1255+
}
1256+
11811257
void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state)
11821258
{
11831259
struct drm_plane *plane;
@@ -1186,13 +1262,10 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru
11861262
struct DCP_FW_NAME(dcp_swap_submit_req) *req = &DCP_FW_UNION(dcp->swap);
11871263
int plane_idx, l;
11881264
int has_surface = 0;
1189-
bool modeset;
11901265
dev_dbg(dcp->dev, "%s", __func__);
11911266

11921267
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
11931268

1194-
modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode;
1195-
11961269
/* Reset to defaults */
11971270
memset(req, 0, sizeof(*req));
11981271
for (l = 0; l < SWAP_SURFACES; l++)
@@ -1305,64 +1378,6 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru
13051378
l += 1;
13061379
}
13071380

1308-
if (modeset) {
1309-
struct dcp_display_mode *mode;
1310-
struct dcp_wait_cookie *cookie;
1311-
int ret;
1312-
1313-
mode = lookup_mode(dcp, &crtc_state->mode);
1314-
if (!mode) {
1315-
dev_warn(dcp->dev, "no match for " DRM_MODE_FMT,
1316-
DRM_MODE_ARG(&crtc_state->mode));
1317-
schedule_work(&dcp->vblank_wq);
1318-
return;
1319-
}
1320-
1321-
dev_info(dcp->dev, "set_digital_out_mode(color:%d timing:%d)",
1322-
mode->color_mode_id, mode->timing_mode_id);
1323-
dcp->mode = (struct dcp_set_digital_out_mode_req){
1324-
.color_mode_id = mode->color_mode_id,
1325-
.timing_mode_id = mode->timing_mode_id
1326-
};
1327-
1328-
cookie = kzalloc(sizeof(*cookie), GFP_KERNEL);
1329-
if (!cookie) {
1330-
schedule_work(&dcp->vblank_wq);
1331-
return;
1332-
}
1333-
1334-
init_completion(&cookie->done);
1335-
kref_init(&cookie->refcount);
1336-
/* increase refcount to ensure the receiver has a reference */
1337-
kref_get(&cookie->refcount);
1338-
1339-
dcp_set_digital_out_mode(dcp, false, &dcp->mode,
1340-
complete_set_digital_out_mode, cookie);
1341-
1342-
/*
1343-
* The DCP firmware has an internal timeout of ~8 seconds for
1344-
* modesets. Add an extra 500ms to safe side that the modeset
1345-
* call has returned.
1346-
*/
1347-
dev_dbg(dcp->dev, "%s - wait for modeset", __func__);
1348-
ret = wait_for_completion_timeout(&cookie->done,
1349-
msecs_to_jiffies(8500));
1350-
1351-
kref_put(&cookie->refcount, release_wait_cookie);
1352-
1353-
if (ret == 0) {
1354-
dev_info(dcp->dev, "set_digital_out_mode timed out");
1355-
schedule_work(&dcp->vblank_wq);
1356-
return;
1357-
} else if (ret > 0) {
1358-
dev_dbg(dcp->dev,
1359-
"set_digital_out_mode finished with %d to spare",
1360-
jiffies_to_msecs(ret));
1361-
}
1362-
1363-
dcp->valid_mode = true;
1364-
}
1365-
13661381
if (!has_surface && !crtc_state->color_mgmt_changed) {
13671382
if (crtc_state->enable && crtc_state->active &&
13681383
!crtc_state->planes_changed) {

drivers/gpu/drm/apple/iomfb_template.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ struct DCP_FW_NAME(dcp_map_reg_resp) {
172172

173173
struct apple_dcp;
174174

175+
int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp,
176+
struct drm_crtc_state *crtc_state);
175177
void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state);
176178
void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp);
177179
void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp);

0 commit comments

Comments
 (0)