Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit 8afe816

Browse files
AngeloGioacchino Del RegnoJassiBrar
authored andcommitted
mailbox: mtk-cmdq-mailbox: Implement Runtime PM with autosuspend
MediaTek found an issue with display HW registers configuration, and located the reason in the CMDQ Mailbox driver; reporting the original comment with the analysis of this problem by Jason-JH Lin: GCE should config HW in every vblanking duration. The stream done event is the start signal of vblanking. If stream done event is sent between GCE clk_disable and clk_enable. After GCE clk_enable the stream done event may not appear immediately and have about 3us delay. Normal case: clk_disable -> get EventA -> clk_enable -> clear EventA -> wait EventB -> get EventB -> config HW Abnormal case: clk_disable -> get EventA -> clk_enable -> EventA delay appear -> clear EventA fail -> wait EventB but get EventA -> config HW This abnormal case may configure display HW in the vactive or non-vblanking duration. From his analysis we get that the GCE may finish its event processing after some amount of time (and not immediately after sending commands to it); since the GCE is used for more than just display, and it gets used frequently, solve this issue by implementing Runtime PM handlers with autosuspend: this allows us to overcome to the remote processor delay issues and reduce the clock enable()/disable() calls, while also still managing to save some power, which is something that we wouldn't be able to do if we just enable the GCE clocks at probe. Speaking of which: if Runtime PM is not available there will obviously be no way to get this power saving action so, in this case, the clocks will be enabled at probe() time, kept enabled for the entire driver's life and disabled at remove(). Reported-by: Jason-JH.Lin <[email protected]> Signed-off-by: AngeloGioacchino Del Regno <[email protected]> Tested-by: Jason-JH.Lin <[email protected]> Signed-off-by: Jassi Brar <[email protected]>
1 parent 5cb5d0c commit 8afe816

File tree

1 file changed

+68
-12
lines changed

1 file changed

+68
-12
lines changed

drivers/mailbox/mtk-cmdq-mailbox.c

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
#include <linux/kernel.h>
1414
#include <linux/module.h>
1515
#include <linux/platform_device.h>
16+
#include <linux/pm_runtime.h>
1617
#include <linux/mailbox_controller.h>
1718
#include <linux/mailbox/mtk-cmdq-mailbox.h>
1819
#include <linux/of.h>
1920

21+
#define CMDQ_MBOX_AUTOSUSPEND_DELAY_MS 100
22+
2023
#define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
2124
#define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE)
2225
#define CMDQ_GCE_NUM_MAX (2)
@@ -283,10 +286,8 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq,
283286
break;
284287
}
285288

286-
if (list_empty(&thread->task_busy_list)) {
289+
if (list_empty(&thread->task_busy_list))
287290
cmdq_thread_disable(cmdq, thread);
288-
clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);
289-
}
290291
}
291292

292293
static irqreturn_t cmdq_irq_handler(int irq, void *dev)
@@ -307,9 +308,26 @@ static irqreturn_t cmdq_irq_handler(int irq, void *dev)
307308
spin_unlock_irqrestore(&thread->chan->lock, flags);
308309
}
309310

311+
pm_runtime_mark_last_busy(cmdq->mbox.dev);
312+
310313
return IRQ_HANDLED;
311314
}
312315

316+
static int cmdq_runtime_resume(struct device *dev)
317+
{
318+
struct cmdq *cmdq = dev_get_drvdata(dev);
319+
320+
return clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks);
321+
}
322+
323+
static int cmdq_runtime_suspend(struct device *dev)
324+
{
325+
struct cmdq *cmdq = dev_get_drvdata(dev);
326+
327+
clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);
328+
return 0;
329+
}
330+
313331
static int cmdq_suspend(struct device *dev)
314332
{
315333
struct cmdq *cmdq = dev_get_drvdata(dev);
@@ -333,16 +351,14 @@ static int cmdq_suspend(struct device *dev)
333351
if (cmdq->pdata->sw_ddr_en)
334352
cmdq_sw_ddr_enable(cmdq, false);
335353

336-
clk_bulk_unprepare(cmdq->pdata->gce_num, cmdq->clocks);
337-
338-
return 0;
354+
return pm_runtime_force_suspend(dev);
339355
}
340356

341357
static int cmdq_resume(struct device *dev)
342358
{
343359
struct cmdq *cmdq = dev_get_drvdata(dev);
344360

345-
WARN_ON(clk_bulk_prepare(cmdq->pdata->gce_num, cmdq->clocks));
361+
WARN_ON(pm_runtime_force_resume(dev));
346362
cmdq->suspended = false;
347363

348364
if (cmdq->pdata->sw_ddr_en)
@@ -358,6 +374,9 @@ static int cmdq_remove(struct platform_device *pdev)
358374
if (cmdq->pdata->sw_ddr_en)
359375
cmdq_sw_ddr_enable(cmdq, false);
360376

377+
if (!IS_ENABLED(CONFIG_PM))
378+
cmdq_runtime_suspend(&pdev->dev);
379+
361380
clk_bulk_unprepare(cmdq->pdata->gce_num, cmdq->clocks);
362381
return 0;
363382
}
@@ -369,13 +388,20 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
369388
struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);
370389
struct cmdq_task *task;
371390
unsigned long curr_pa, end_pa;
391+
int ret;
372392

373393
/* Client should not flush new tasks if suspended. */
374394
WARN_ON(cmdq->suspended);
375395

396+
ret = pm_runtime_get_sync(cmdq->mbox.dev);
397+
if (ret < 0)
398+
return ret;
399+
376400
task = kzalloc(sizeof(*task), GFP_ATOMIC);
377-
if (!task)
401+
if (!task) {
402+
pm_runtime_put_autosuspend(cmdq->mbox.dev);
378403
return -ENOMEM;
404+
}
379405

380406
task->cmdq = cmdq;
381407
INIT_LIST_HEAD(&task->list_entry);
@@ -384,8 +410,6 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
384410
task->pkt = pkt;
385411

386412
if (list_empty(&thread->task_busy_list)) {
387-
WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));
388-
389413
/*
390414
* The thread reset will clear thread related register to 0,
391415
* including pc, end, priority, irq, suspend and enable. Thus
@@ -424,6 +448,9 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
424448
}
425449
list_move_tail(&task->list_entry, &thread->task_busy_list);
426450

451+
pm_runtime_mark_last_busy(cmdq->mbox.dev);
452+
pm_runtime_put_autosuspend(cmdq->mbox.dev);
453+
427454
return 0;
428455
}
429456

@@ -439,6 +466,8 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan)
439466
struct cmdq_task *task, *tmp;
440467
unsigned long flags;
441468

469+
WARN_ON(pm_runtime_get_sync(cmdq->mbox.dev));
470+
442471
spin_lock_irqsave(&thread->chan->lock, flags);
443472
if (list_empty(&thread->task_busy_list))
444473
goto done;
@@ -457,7 +486,6 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan)
457486
}
458487

459488
cmdq_thread_disable(cmdq, thread);
460-
clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);
461489

462490
done:
463491
/*
@@ -467,6 +495,9 @@ static void cmdq_mbox_shutdown(struct mbox_chan *chan)
467495
* to do any operation here, only unlock and leave.
468496
*/
469497
spin_unlock_irqrestore(&thread->chan->lock, flags);
498+
499+
pm_runtime_mark_last_busy(cmdq->mbox.dev);
500+
pm_runtime_put_autosuspend(cmdq->mbox.dev);
470501
}
471502

472503
static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
@@ -477,6 +508,11 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
477508
struct cmdq_task *task, *tmp;
478509
unsigned long flags;
479510
u32 enable;
511+
int ret;
512+
513+
ret = pm_runtime_get_sync(cmdq->mbox.dev);
514+
if (ret < 0)
515+
return ret;
480516

481517
spin_lock_irqsave(&thread->chan->lock, flags);
482518
if (list_empty(&thread->task_busy_list))
@@ -497,10 +533,12 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
497533

498534
cmdq_thread_resume(thread);
499535
cmdq_thread_disable(cmdq, thread);
500-
clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);
501536

502537
out:
503538
spin_unlock_irqrestore(&thread->chan->lock, flags);
539+
pm_runtime_mark_last_busy(cmdq->mbox.dev);
540+
pm_runtime_put_autosuspend(cmdq->mbox.dev);
541+
504542
return 0;
505543

506544
wait:
@@ -513,6 +551,8 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
513551

514552
return -EFAULT;
515553
}
554+
pm_runtime_mark_last_busy(cmdq->mbox.dev);
555+
pm_runtime_put_autosuspend(cmdq->mbox.dev);
516556
return 0;
517557
}
518558

@@ -642,12 +682,28 @@ static int cmdq_probe(struct platform_device *pdev)
642682
return err;
643683
}
644684

685+
/* If Runtime PM is not available enable the clocks now. */
686+
if (!IS_ENABLED(CONFIG_PM)) {
687+
err = cmdq_runtime_resume(dev);
688+
if (err)
689+
return err;
690+
}
691+
692+
err = devm_pm_runtime_enable(dev);
693+
if (err)
694+
return err;
695+
696+
pm_runtime_set_autosuspend_delay(dev, CMDQ_MBOX_AUTOSUSPEND_DELAY_MS);
697+
pm_runtime_use_autosuspend(dev);
698+
645699
return 0;
646700
}
647701

648702
static const struct dev_pm_ops cmdq_pm_ops = {
649703
.suspend = cmdq_suspend,
650704
.resume = cmdq_resume,
705+
SET_RUNTIME_PM_OPS(cmdq_runtime_suspend,
706+
cmdq_runtime_resume, NULL)
651707
};
652708

653709
static const struct gce_plat gce_plat_v2 = {

0 commit comments

Comments
 (0)