diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2017-04-11 12:39:25 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2017-04-18 15:27:32 -0400 |
commit | 4f6e3d66ac522dec9733d433ad00e8e77747c372 (patch) | |
tree | e89c1b87a96b8c56355a472ddf321da3c600f355 /drivers/gpu/drm/vc4/vc4_hdmi.c | |
parent | 75cccac4028cede6d064699abae903c9e338004f (diff) |
drm/vc4: Add runtime PM support to the HDMI encoder driver
The HDMI driver is currently enabling all clocks at probe time and
keeps the power-domain connected to the HDMI encoder enabled.
Move all activation code to vc4_hdmi_encoder_enable() and make sure
the clks and power domain are released when the HDMI encoder is not used
by adding deactivation steps in vc4_hdmi_encoder_disable().
Note that the sequencing imposed by the IP requires that we move
vc4_hdmi_encoder_mode_set() code into vc4_hdmi_encoder_enable().
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hdmi.c | 174 |
1 files changed, 92 insertions, 82 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index e9cbe269710b..3c2723f6345c 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #include "linux/of_address.h" | 51 | #include "linux/of_address.h" |
52 | #include "linux/of_gpio.h" | 52 | #include "linux/of_gpio.h" |
53 | #include "linux/of_platform.h" | 53 | #include "linux/of_platform.h" |
54 | #include "linux/pm_runtime.h" | ||
54 | #include "linux/rational.h" | 55 | #include "linux/rational.h" |
55 | #include "sound/dmaengine_pcm.h" | 56 | #include "sound/dmaengine_pcm.h" |
56 | #include "sound/pcm_drm_eld.h" | 57 | #include "sound/pcm_drm_eld.h" |
@@ -449,13 +450,38 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) | |||
449 | vc4_hdmi_set_spd_infoframe(encoder); | 450 | vc4_hdmi_set_spd_infoframe(encoder); |
450 | } | 451 | } |
451 | 452 | ||
452 | static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, | 453 | static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) |
453 | struct drm_display_mode *unadjusted_mode, | 454 | { |
454 | struct drm_display_mode *mode) | 455 | struct drm_device *dev = encoder->dev; |
456 | struct vc4_dev *vc4 = to_vc4_dev(dev); | ||
457 | struct vc4_hdmi *hdmi = vc4->hdmi; | ||
458 | int ret; | ||
459 | |||
460 | HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0); | ||
461 | |||
462 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); | ||
463 | HD_WRITE(VC4_HD_VID_CTL, | ||
464 | HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); | ||
465 | |||
466 | HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST); | ||
467 | udelay(1); | ||
468 | HD_WRITE(VC4_HD_M_CTL, 0); | ||
469 | |||
470 | clk_disable_unprepare(hdmi->hsm_clock); | ||
471 | clk_disable_unprepare(hdmi->pixel_clock); | ||
472 | |||
473 | ret = pm_runtime_put(&hdmi->pdev->dev); | ||
474 | if (ret < 0) | ||
475 | DRM_ERROR("Failed to release power domain: %d\n", ret); | ||
476 | } | ||
477 | |||
478 | static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) | ||
455 | { | 479 | { |
480 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; | ||
456 | struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); | 481 | struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); |
457 | struct drm_device *dev = encoder->dev; | 482 | struct drm_device *dev = encoder->dev; |
458 | struct vc4_dev *vc4 = to_vc4_dev(dev); | 483 | struct vc4_dev *vc4 = to_vc4_dev(dev); |
484 | struct vc4_hdmi *hdmi = vc4->hdmi; | ||
459 | bool debug_dump_regs = false; | 485 | bool debug_dump_regs = false; |
460 | bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; | 486 | bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; |
461 | bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; | 487 | bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; |
@@ -475,6 +501,64 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, | |||
475 | interlaced, | 501 | interlaced, |
476 | VC4_HDMI_VERTB_VBP)); | 502 | VC4_HDMI_VERTB_VBP)); |
477 | u32 csc_ctl; | 503 | u32 csc_ctl; |
504 | int ret; | ||
505 | |||
506 | ret = pm_runtime_get_sync(&hdmi->pdev->dev); | ||
507 | if (ret < 0) { | ||
508 | DRM_ERROR("Failed to retain power domain: %d\n", ret); | ||
509 | return; | ||
510 | } | ||
511 | |||
512 | /* This is the rate that is set by the firmware. The number | ||
513 | * needs to be a bit higher than the pixel clock rate | ||
514 | * (generally 148.5Mhz). | ||
515 | */ | ||
516 | ret = clk_set_rate(hdmi->hsm_clock, 163682864); | ||
517 | if (ret) { | ||
518 | DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); | ||
519 | return; | ||
520 | } | ||
521 | |||
522 | ret = clk_set_rate(hdmi->pixel_clock, | ||
523 | mode->clock * 1000 * | ||
524 | ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1)); | ||
525 | if (ret) { | ||
526 | DRM_ERROR("Failed to set pixel clock rate: %d\n", ret); | ||
527 | return; | ||
528 | } | ||
529 | |||
530 | ret = clk_prepare_enable(hdmi->pixel_clock); | ||
531 | if (ret) { | ||
532 | DRM_ERROR("Failed to turn on pixel clock: %d\n", ret); | ||
533 | return; | ||
534 | } | ||
535 | |||
536 | ret = clk_prepare_enable(hdmi->hsm_clock); | ||
537 | if (ret) { | ||
538 | DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n", | ||
539 | ret); | ||
540 | clk_disable_unprepare(hdmi->pixel_clock); | ||
541 | return; | ||
542 | } | ||
543 | |||
544 | HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST); | ||
545 | udelay(1); | ||
546 | HD_WRITE(VC4_HD_M_CTL, 0); | ||
547 | |||
548 | HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE); | ||
549 | |||
550 | HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, | ||
551 | VC4_HDMI_SW_RESET_HDMI | | ||
552 | VC4_HDMI_SW_RESET_FORMAT_DETECT); | ||
553 | |||
554 | HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0); | ||
555 | |||
556 | /* PHY should be in reset, like | ||
557 | * vc4_hdmi_encoder_disable() does. | ||
558 | */ | ||
559 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); | ||
560 | |||
561 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); | ||
478 | 562 | ||
479 | if (debug_dump_regs) { | 563 | if (debug_dump_regs) { |
480 | DRM_INFO("HDMI regs before:\n"); | 564 | DRM_INFO("HDMI regs before:\n"); |
@@ -483,9 +567,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, | |||
483 | 567 | ||
484 | HD_WRITE(VC4_HD_VID_CTL, 0); | 568 | HD_WRITE(VC4_HD_VID_CTL, 0); |
485 | 569 | ||
486 | clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 * | ||
487 | ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1)); | ||
488 | |||
489 | HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, | 570 | HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, |
490 | HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | | 571 | HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | |
491 | VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT | | 572 | VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT | |
@@ -559,28 +640,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, | |||
559 | DRM_INFO("HDMI regs after:\n"); | 640 | DRM_INFO("HDMI regs after:\n"); |
560 | vc4_hdmi_dump_regs(dev); | 641 | vc4_hdmi_dump_regs(dev); |
561 | } | 642 | } |
562 | } | ||
563 | |||
564 | static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) | ||
565 | { | ||
566 | struct drm_device *dev = encoder->dev; | ||
567 | struct vc4_dev *vc4 = to_vc4_dev(dev); | ||
568 | |||
569 | HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0); | ||
570 | |||
571 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); | ||
572 | HD_WRITE(VC4_HD_VID_CTL, | ||
573 | HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE); | ||
574 | } | ||
575 | |||
576 | static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) | ||
577 | { | ||
578 | struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); | ||
579 | struct drm_device *dev = encoder->dev; | ||
580 | struct vc4_dev *vc4 = to_vc4_dev(dev); | ||
581 | int ret; | ||
582 | |||
583 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); | ||
584 | 643 | ||
585 | HD_WRITE(VC4_HD_VID_CTL, | 644 | HD_WRITE(VC4_HD_VID_CTL, |
586 | HD_READ(VC4_HD_VID_CTL) | | 645 | HD_READ(VC4_HD_VID_CTL) | |
@@ -646,7 +705,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) | |||
646 | } | 705 | } |
647 | 706 | ||
648 | static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { | 707 | static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { |
649 | .mode_set = vc4_hdmi_encoder_mode_set, | ||
650 | .disable = vc4_hdmi_encoder_disable, | 708 | .disable = vc4_hdmi_encoder_disable, |
651 | .enable = vc4_hdmi_encoder_enable, | 709 | .enable = vc4_hdmi_encoder_enable, |
652 | }; | 710 | }; |
@@ -1147,33 +1205,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) | |||
1147 | return -EPROBE_DEFER; | 1205 | return -EPROBE_DEFER; |
1148 | } | 1206 | } |
1149 | 1207 | ||
1150 | /* Enable the clocks at startup. We can't quite recover from | ||
1151 | * turning off the pixel clock during disable/enables yet, so | ||
1152 | * it's always running. | ||
1153 | */ | ||
1154 | ret = clk_prepare_enable(hdmi->pixel_clock); | ||
1155 | if (ret) { | ||
1156 | DRM_ERROR("Failed to turn on pixel clock: %d\n", ret); | ||
1157 | goto err_put_i2c; | ||
1158 | } | ||
1159 | |||
1160 | /* This is the rate that is set by the firmware. The number | ||
1161 | * needs to be a bit higher than the pixel clock rate | ||
1162 | * (generally 148.5Mhz). | ||
1163 | */ | ||
1164 | ret = clk_set_rate(hdmi->hsm_clock, 163682864); | ||
1165 | if (ret) { | ||
1166 | DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); | ||
1167 | goto err_unprepare_pix; | ||
1168 | } | ||
1169 | |||
1170 | ret = clk_prepare_enable(hdmi->hsm_clock); | ||
1171 | if (ret) { | ||
1172 | DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n", | ||
1173 | ret); | ||
1174 | goto err_unprepare_pix; | ||
1175 | } | ||
1176 | |||
1177 | /* Only use the GPIO HPD pin if present in the DT, otherwise | 1208 | /* Only use the GPIO HPD pin if present in the DT, otherwise |
1178 | * we'll use the HDMI core's register. | 1209 | * we'll use the HDMI core's register. |
1179 | */ | 1210 | */ |
@@ -1185,7 +1216,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) | |||
1185 | &hpd_gpio_flags); | 1216 | &hpd_gpio_flags); |
1186 | if (hdmi->hpd_gpio < 0) { | 1217 | if (hdmi->hpd_gpio < 0) { |
1187 | ret = hdmi->hpd_gpio; | 1218 | ret = hdmi->hpd_gpio; |
1188 | goto err_unprepare_hsm; | 1219 | goto err_put_i2c; |
1189 | } | 1220 | } |
1190 | 1221 | ||
1191 | hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW; | 1222 | hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW; |
@@ -1193,25 +1224,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) | |||
1193 | 1224 | ||
1194 | vc4->hdmi = hdmi; | 1225 | vc4->hdmi = hdmi; |
1195 | 1226 | ||
1196 | /* HDMI core must be enabled. */ | 1227 | pm_runtime_enable(dev); |
1197 | if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) { | ||
1198 | HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST); | ||
1199 | udelay(1); | ||
1200 | HD_WRITE(VC4_HD_M_CTL, 0); | ||
1201 | |||
1202 | HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE); | ||
1203 | |||
1204 | HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, | ||
1205 | VC4_HDMI_SW_RESET_HDMI | | ||
1206 | VC4_HDMI_SW_RESET_FORMAT_DETECT); | ||
1207 | |||
1208 | HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0); | ||
1209 | |||
1210 | /* PHY should be in reset, like | ||
1211 | * vc4_hdmi_encoder_disable() does. | ||
1212 | */ | ||
1213 | HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); | ||
1214 | } | ||
1215 | 1228 | ||
1216 | drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs, | 1229 | drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs, |
1217 | DRM_MODE_ENCODER_TMDS, NULL); | 1230 | DRM_MODE_ENCODER_TMDS, NULL); |
@@ -1231,10 +1244,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) | |||
1231 | 1244 | ||
1232 | err_destroy_encoder: | 1245 | err_destroy_encoder: |
1233 | vc4_hdmi_encoder_destroy(hdmi->encoder); | 1246 | vc4_hdmi_encoder_destroy(hdmi->encoder); |
1234 | err_unprepare_hsm: | 1247 | pm_runtime_disable(dev); |
1235 | clk_disable_unprepare(hdmi->hsm_clock); | ||
1236 | err_unprepare_pix: | ||
1237 | clk_disable_unprepare(hdmi->pixel_clock); | ||
1238 | err_put_i2c: | 1248 | err_put_i2c: |
1239 | put_device(&hdmi->ddc->dev); | 1249 | put_device(&hdmi->ddc->dev); |
1240 | 1250 | ||
@@ -1253,8 +1263,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, | |||
1253 | vc4_hdmi_connector_destroy(hdmi->connector); | 1263 | vc4_hdmi_connector_destroy(hdmi->connector); |
1254 | vc4_hdmi_encoder_destroy(hdmi->encoder); | 1264 | vc4_hdmi_encoder_destroy(hdmi->encoder); |
1255 | 1265 | ||
1256 | clk_disable_unprepare(hdmi->pixel_clock); | 1266 | pm_runtime_disable(dev); |
1257 | clk_disable_unprepare(hdmi->hsm_clock); | 1267 | |
1258 | put_device(&hdmi->ddc->dev); | 1268 | put_device(&hdmi->ddc->dev); |
1259 | 1269 | ||
1260 | vc4->hdmi = NULL; | 1270 | vc4->hdmi = NULL; |