Skip to content

Commit 8d00685

Browse files
mairacanalpelwell
authored andcommitted
[BACKPORTED] drm/v3d: Introduce Runtime Power Management
Commit 90a64ad ("drm/v3d: Get rid of pm code") removed the last bits of power management code that V3D had, which were actually never hooked. Therefore, currently, the GPU clock is enabled during probe and only disabled when removing the driver. Implement proper power management using the kernel's Runtime PM framework. Signed-off-by: Maíra Canal <mcanal@igalia.com>
1 parent eb3ace8 commit 8d00685

File tree

8 files changed

+198
-59
lines changed

8 files changed

+198
-59
lines changed

drivers/gpu/drm/v3d/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ v3d-y := \
1414
v3d_sched.o \
1515
v3d_sysfs.o \
1616
v3d_submit.o \
17-
v3d_gemfs.o
17+
v3d_gemfs.o \
18+
v3d_power.o
1819

1920
v3d-$(CONFIG_DEBUG_FS) += v3d_debugfs.o
2021

drivers/gpu/drm/v3d/v3d_debugfs.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
9696
struct drm_debugfs_entry *entry = m->private;
9797
struct drm_device *dev = entry->dev;
9898
struct v3d_dev *v3d = to_v3d_dev(dev);
99-
int i, core;
99+
int i, core, ret;
100+
101+
ret = v3d_pm_runtime_get(v3d);
102+
if (ret)
103+
return ret;
100104

101105
for (i = 0; i < ARRAY_SIZE(v3d_hub_reg_defs); i++) {
102106
const struct v3d_reg_def *def = &v3d_hub_reg_defs[i];
@@ -138,6 +142,8 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
138142
}
139143
}
140144

145+
v3d_pm_runtime_put(v3d);
146+
141147
return 0;
142148
}
143149

@@ -147,7 +153,11 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
147153
struct drm_device *dev = entry->dev;
148154
struct v3d_dev *v3d = to_v3d_dev(dev);
149155
u32 ident0, ident1, ident2, ident3, cores;
150-
int core;
156+
int core, ret;
157+
158+
ret = v3d_pm_runtime_get(v3d);
159+
if (ret)
160+
return ret;
151161

152162
ident0 = V3D_READ(V3D_HUB_IDENT0);
153163
ident1 = V3D_READ(V3D_HUB_IDENT1);
@@ -206,6 +216,8 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
206216
}
207217
}
208218

219+
v3d_pm_runtime_put(v3d);
220+
209221
return 0;
210222
}
211223

@@ -233,6 +245,11 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
233245
uint32_t cycles;
234246
int core = 0;
235247
int measure_ms = 1000;
248+
int ret;
249+
250+
ret = v3d_pm_runtime_get(v3d);
251+
if (ret)
252+
return ret;
236253

237254
if (v3d->ver >= V3D_GEN_41) {
238255
int cycle_count_reg = V3D_PCTR_CYCLE_COUNT(v3d->ver);
@@ -252,6 +269,8 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
252269
msleep(measure_ms);
253270
cycles = V3D_CORE_READ(core, V3D_PCTR_0_PCTR0);
254271

272+
v3d_pm_runtime_put(v3d);
273+
255274
seq_printf(m, "cycles: %d (%d.%d Mhz)\n",
256275
cycles,
257276
cycles / (measure_ms * 1000),

drivers/gpu/drm/v3d/v3d_drv.c

Lines changed: 38 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
6363
[DRM_V3D_PARAM_V3D_CORE0_IDENT1] = V3D_CTL_IDENT1,
6464
[DRM_V3D_PARAM_V3D_CORE0_IDENT2] = V3D_CTL_IDENT2,
6565
};
66+
int ret;
6667

6768
if (args->pad != 0)
6869
return -EINVAL;
@@ -79,12 +80,19 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
7980
if (args->value != 0)
8081
return -EINVAL;
8182

83+
ret = v3d_pm_runtime_get(v3d);
84+
if (ret)
85+
return ret;
86+
8287
if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 &&
8388
args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) {
8489
args->value = V3D_CORE_READ(0, offset);
8590
} else {
8691
args->value = V3D_READ(offset);
8792
}
93+
94+
v3d_pm_runtime_put(v3d);
95+
8896
return 0;
8997
}
9098

@@ -291,36 +299,6 @@ static const struct of_device_id v3d_of_match[] = {
291299
};
292300
MODULE_DEVICE_TABLE(of, v3d_of_match);
293301

294-
static void
295-
v3d_idle_sms(struct v3d_dev *v3d)
296-
{
297-
if (v3d->ver < V3D_GEN_71)
298-
return;
299-
300-
V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF);
301-
302-
if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
303-
V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) {
304-
DRM_ERROR("Failed to power up SMS\n");
305-
}
306-
307-
v3d_reset_sms(v3d);
308-
}
309-
310-
static void
311-
v3d_power_off_sms(struct v3d_dev *v3d)
312-
{
313-
if (v3d->ver < V3D_GEN_71)
314-
return;
315-
316-
V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF);
317-
318-
if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
319-
V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) {
320-
DRM_ERROR("Failed to power off SMS\n");
321-
}
322-
}
323-
324302
static int
325303
map_regs(struct v3d_dev *v3d, void __iomem **regs, const char *name)
326304
{
@@ -404,19 +382,26 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
404382
if (ret)
405383
goto dma_free;
406384

407-
ret = clk_prepare_enable(v3d->clk);
408-
if (ret) {
409-
dev_err(&pdev->dev, "Couldn't enable the V3D clock\n");
385+
ret = devm_pm_runtime_enable(dev);
386+
if (ret)
410387
goto gem_destroy;
411-
}
412388

413-
v3d_idle_sms(v3d);
389+
ret = pm_runtime_resume_and_get(dev);
390+
if (ret)
391+
goto gem_destroy;
392+
393+
/* If PM is disabled, we need to call v3d_power_resume() manually. */
394+
if (!IS_ENABLED(CONFIG_PM)) {
395+
ret = v3d_power_resume(dev);
396+
if (ret)
397+
goto gem_destroy;
398+
}
414399

415400
mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO);
416401
mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH));
417402
ret = dma_set_mask_and_coherent(dev, mask);
418403
if (ret)
419-
goto clk_disable;
404+
goto runtime_pm_put;
420405

421406
dma_set_max_seg_size(&pdev->dev, UINT_MAX);
422407

@@ -437,26 +422,27 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
437422
v3d->rev = V3D_GET_FIELD(ident3, V3D_HUB_IDENT3_IPREV);
438423

439424
v3d_init_hw_state(v3d);
440-
v3d_mmu_set_page_table(v3d);
441-
v3d_irq_enable(v3d);
425+
426+
pm_runtime_set_autosuspend_delay(dev, 50);
427+
pm_runtime_use_autosuspend(dev);
442428

443429
ret = drm_dev_register(drm, 0);
444430
if (ret)
445-
goto irq_disable;
431+
goto runtime_pm_put;
446432

447433
ret = v3d_sysfs_init(dev);
448434
if (ret)
449435
goto drm_unregister;
450436

437+
pm_runtime_mark_last_busy(dev);
438+
pm_runtime_put_autosuspend(dev);
439+
451440
return 0;
452441

453442
drm_unregister:
454443
drm_dev_unregister(drm);
455-
irq_disable:
456-
v3d_irq_disable(v3d);
457-
clk_disable:
458-
v3d_power_off_sms(v3d);
459-
clk_disable_unprepare(v3d->clk);
444+
runtime_pm_put:
445+
pm_runtime_put_sync_suspend(dev);
460446
gem_destroy:
461447
v3d_gem_destroy(drm);
462448
dma_free:
@@ -474,21 +460,27 @@ static void v3d_platform_drm_remove(struct platform_device *pdev)
474460

475461
drm_dev_unregister(drm);
476462

477-
v3d_power_off_sms(v3d);
463+
pm_runtime_suspend(dev);
478464

479-
clk_disable_unprepare(v3d->clk);
465+
/* If PM is disabled, we need to call v3d_power_suspend() manually. */
466+
if (!IS_ENABLED(CONFIG_PM))
467+
v3d_power_suspend(dev);
480468

481469
v3d_gem_destroy(drm);
482470

483471
dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr);
484472
}
485473

474+
static DEFINE_RUNTIME_DEV_PM_OPS(v3d_pm_ops, v3d_power_suspend,
475+
v3d_power_resume, NULL);
476+
486477
static struct platform_driver v3d_platform_driver = {
487478
.probe = v3d_platform_drm_probe,
488479
.remove = v3d_platform_drm_remove,
489480
.driver = {
490481
.name = "v3d",
491482
.of_match_table = v3d_of_match,
483+
.pm = pm_ptr(&v3d_pm_ops),
492484
},
493485
};
494486

drivers/gpu/drm/v3d/v3d_drv.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <linux/delay.h>
55
#include <linux/mutex.h>
6+
#include <linux/pm_runtime.h>
67
#include <linux/spinlock_types.h>
78
#include <linux/workqueue.h>
89

@@ -330,6 +331,8 @@ struct v3d_job {
330331

331332
/* Callback for the freeing of the job on refcount going to 0. */
332333
void (*free)(struct kref *ref);
334+
335+
bool has_pm_ref;
333336
};
334337

335338
struct v3d_bin_job {
@@ -608,6 +611,20 @@ int v3d_mmu_set_page_table(struct v3d_dev *v3d);
608611
void v3d_mmu_insert_ptes(struct v3d_bo *bo);
609612
void v3d_mmu_remove_ptes(struct v3d_bo *bo);
610613

614+
/* v3d_power.c */
615+
int v3d_power_suspend(struct device *dev);
616+
int v3d_power_resume(struct device *dev);
617+
618+
static __always_inline int v3d_pm_runtime_get(struct v3d_dev *v3d)
619+
{
620+
return pm_runtime_resume_and_get(v3d->drm.dev);
621+
}
622+
623+
static __always_inline int v3d_pm_runtime_put(struct v3d_dev *v3d)
624+
{
625+
return pm_runtime_put_autosuspend(v3d->drm.dev);
626+
}
627+
611628
/* v3d_sched.c */
612629
void v3d_timestamp_query_info_free(struct v3d_timestamp_query_info *query_info,
613630
unsigned int count);

drivers/gpu/drm/v3d/v3d_mmu.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ static bool v3d_mmu_is_aligned(u32 page, u32 page_address, size_t alignment)
3737

3838
int v3d_mmu_flush_all(struct v3d_dev *v3d)
3939
{
40-
int ret;
40+
int ret = 0;
41+
42+
/* Flush the PTs only if we're already awake */
43+
if (!pm_runtime_get_if_active(v3d->drm.dev))
44+
return 0;
4145

4246
V3D_WRITE(V3D_MMUC_CONTROL, V3D_MMUC_CONTROL_FLUSH |
4347
V3D_MMUC_CONTROL_ENABLE);
@@ -46,7 +50,7 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d)
4650
V3D_MMUC_CONTROL_FLUSHING), 100);
4751
if (ret) {
4852
dev_err(v3d->drm.dev, "MMUC flush wait idle failed\n");
49-
return ret;
53+
goto pm_put;
5054
}
5155

5256
V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL) |
@@ -57,6 +61,8 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d)
5761
if (ret)
5862
dev_err(v3d->drm.dev, "MMU TLB clear wait idle failed\n");
5963

64+
pm_put:
65+
v3d_pm_runtime_put(v3d);
6066
return ret;
6167
}
6268

drivers/gpu/drm/v3d/v3d_perfmon.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
235235
if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
236236
return;
237237

238+
if (!pm_runtime_get_if_active(v3d->drm.dev))
239+
return;
240+
238241
ncounters = perfmon->ncounters;
239242
mask = GENMASK(ncounters - 1, 0);
240243

@@ -260,6 +263,8 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
260263
V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
261264

262265
v3d->active_perfmon = perfmon;
266+
267+
v3d_pm_runtime_put(v3d);
263268
}
264269

265270
void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
@@ -271,18 +276,23 @@ void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
271276
return;
272277

273278
mutex_lock(&perfmon->lock);
274-
if (perfmon != v3d->active_perfmon) {
275-
mutex_unlock(&perfmon->lock);
276-
return;
277-
}
279+
if (perfmon != v3d->active_perfmon)
280+
goto out;
281+
282+
if (!pm_runtime_get_if_active(v3d->drm.dev))
283+
goto out_clear;
278284

279285
if (capture)
280286
for (i = 0; i < perfmon->ncounters; i++)
281287
perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i));
282288

283289
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
284290

291+
v3d_pm_runtime_put(v3d);
292+
293+
out_clear:
285294
v3d->active_perfmon = NULL;
295+
out:
286296
mutex_unlock(&perfmon->lock);
287297
}
288298

0 commit comments

Comments
 (0)