Skip to content

Commit 4f6e3d6

Browse files
Boris Brezillonanholt
authored andcommitted
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 <[email protected]> Signed-off-by: Eric Anholt <[email protected]> Reviewed-by: Eric Anholt <[email protected]>
1 parent 75cccac commit 4f6e3d6

File tree

1 file changed

+92
-82
lines changed

1 file changed

+92
-82
lines changed

drivers/gpu/drm/vc4/vc4_hdmi.c

Lines changed: 92 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "linux/of_address.h"
5252
#include "linux/of_gpio.h"
5353
#include "linux/of_platform.h"
54+
#include "linux/pm_runtime.h"
5455
#include "linux/rational.h"
5556
#include "sound/dmaengine_pcm.h"
5657
#include "sound/pcm_drm_eld.h"
@@ -449,13 +450,38 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
449450
vc4_hdmi_set_spd_infoframe(encoder);
450451
}
451452

452-
static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
453-
struct drm_display_mode *unadjusted_mode,
454-
struct drm_display_mode *mode)
453+
static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
454+
{
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)
455479
{
480+
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
456481
struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
457482
struct drm_device *dev = encoder->dev;
458483
struct vc4_dev *vc4 = to_vc4_dev(dev);
484+
struct vc4_hdmi *hdmi = vc4->hdmi;
459485
bool debug_dump_regs = false;
460486
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
461487
bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
@@ -475,6 +501,64 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
475501
interlaced,
476502
VC4_HDMI_VERTB_VBP));
477503
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);
478562

479563
if (debug_dump_regs) {
480564
DRM_INFO("HDMI regs before:\n");
@@ -483,9 +567,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
483567

484568
HD_WRITE(VC4_HD_VID_CTL, 0);
485569

486-
clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 *
487-
((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
488-
489570
HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
490571
HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
491572
VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
@@ -559,28 +640,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
559640
DRM_INFO("HDMI regs after:\n");
560641
vc4_hdmi_dump_regs(dev);
561642
}
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);
584643

585644
HD_WRITE(VC4_HD_VID_CTL,
586645
HD_READ(VC4_HD_VID_CTL) |
@@ -646,7 +705,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
646705
}
647706

648707
static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
649-
.mode_set = vc4_hdmi_encoder_mode_set,
650708
.disable = vc4_hdmi_encoder_disable,
651709
.enable = vc4_hdmi_encoder_enable,
652710
};
@@ -1147,33 +1205,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
11471205
return -EPROBE_DEFER;
11481206
}
11491207

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-
11771208
/* Only use the GPIO HPD pin if present in the DT, otherwise
11781209
* we'll use the HDMI core's register.
11791210
*/
@@ -1185,33 +1216,15 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
11851216
&hpd_gpio_flags);
11861217
if (hdmi->hpd_gpio < 0) {
11871218
ret = hdmi->hpd_gpio;
1188-
goto err_unprepare_hsm;
1219+
goto err_put_i2c;
11891220
}
11901221

11911222
hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
11921223
}
11931224

11941225
vc4->hdmi = hdmi;
11951226

1196-
/* HDMI core must be enabled. */
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-
}
1227+
pm_runtime_enable(dev);
12151228

12161229
drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
12171230
DRM_MODE_ENCODER_TMDS, NULL);
@@ -1231,10 +1244,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
12311244

12321245
err_destroy_encoder:
12331246
vc4_hdmi_encoder_destroy(hdmi->encoder);
1234-
err_unprepare_hsm:
1235-
clk_disable_unprepare(hdmi->hsm_clock);
1236-
err_unprepare_pix:
1237-
clk_disable_unprepare(hdmi->pixel_clock);
1247+
pm_runtime_disable(dev);
12381248
err_put_i2c:
12391249
put_device(&hdmi->ddc->dev);
12401250

@@ -1253,8 +1263,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
12531263
vc4_hdmi_connector_destroy(hdmi->connector);
12541264
vc4_hdmi_encoder_destroy(hdmi->encoder);
12551265

1256-
clk_disable_unprepare(hdmi->pixel_clock);
1257-
clk_disable_unprepare(hdmi->hsm_clock);
1266+
pm_runtime_disable(dev);
1267+
12581268
put_device(&hdmi->ddc->dev);
12591269

12601270
vc4->hdmi = NULL;

0 commit comments

Comments
 (0)