diff options
Diffstat (limited to 'drivers/gpu/drm/mediatek/mtk_disp_merge.c')
-rw-r--r-- | drivers/gpu/drm/mediatek/mtk_disp_merge.c | 99 |
1 files changed, 79 insertions, 20 deletions
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_merge.c b/drivers/gpu/drm/mediatek/mtk_disp_merge.c index 22f768d923d5..b174dda091d3 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_merge.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_merge.c @@ -10,7 +10,7 @@ #include <linux/reset.h> #include <linux/soc/mediatek/mtk-cmdq.h> -#include "mtk_drm_ddp_comp.h" +#include "mtk_ddp_comp.h" #include "mtk_drm_drv.h" #include "mtk_disp_drv.h" @@ -222,6 +222,71 @@ void mtk_merge_clk_disable(struct device *dev) clk_disable_unprepare(priv->clk); } +enum drm_mode_status mtk_merge_mode_valid(struct device *dev, + const struct drm_display_mode *mode) +{ + struct mtk_disp_merge *priv = dev_get_drvdata(dev); + unsigned long rate; + + rate = clk_get_rate(priv->clk); + + /* Convert to KHz and round the number */ + rate = (rate + 500) / 1000; + + if (rate && mode->clock > rate) { + dev_dbg(dev, "invalid clock: %d (>%lu)\n", mode->clock, rate); + return MODE_CLOCK_HIGH; + } + + /* + * Measure the bandwidth requirement of hardware prefetch (per frame) + * + * let N = prefetch buffer size in lines + * (ex. N=3, then prefetch buffer size = 3 lines) + * + * prefetch size = htotal * N (pixels) + * time per line = 1 / fps / vtotal (seconds) + * duration = vbp * time per line + * = vbp / fps / vtotal + * + * data rate = prefetch size / duration + * = htotal * N / (vbp / fps / vtotal) + * = htotal * vtotal * fps * N / vbp + * = clk * N / vbp (pixels per second) + * + * Say 4K60 (CEA-861) is the maximum mode supported by the SoC + * data rate = 594000K * N / 72 = 8250 (standard) + * (remove K * N due to the same unit) + * + * For 2560x1440@144 (clk=583600K, vbp=17): + * data rate = 583600 / 17 ~= 34329 > 8250 (NG) + * + * For 2560x1440@120 (clk=497760K, vbp=77): + * data rate = 497760 / 77 ~= 6464 < 8250 (OK) + * + * A non-standard 4K60 timing (clk=521280K, vbp=54) + * data rate = 521280 / 54 ~= 9653 > 8250 (NG) + * + * Bandwidth requirement of hardware prefetch increases significantly + * when the VBP decreases (more than 4x in this example). + * + * The proposed formula is only one way to estimate whether our SoC + * supports the mode setting. The basic idea behind it is just to check + * if the data rate requirement is too high (directly proportional to + * pixel clock, inversely proportional to vbp). Please adjust the + * function if it doesn't fit your situation in the future. + */ + rate = mode->clock / (mode->vtotal - mode->vsync_end); + + if (rate > 8250) { + dev_dbg(dev, "invalid rate: %lu (>8250): " DRM_MODE_FMT "\n", + rate, DRM_MODE_ARG(mode)); + return MODE_BAD; + } + + return MODE_OK; +} + static int mtk_disp_merge_bind(struct device *dev, struct device *master, void *data) { @@ -241,7 +306,6 @@ static const struct component_ops mtk_disp_merge_component_ops = { static int mtk_disp_merge_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct mtk_disp_merge *priv; int ret; @@ -249,24 +313,20 @@ static int mtk_disp_merge_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->regs)) { - dev_err(dev, "failed to ioremap merge\n"); - return PTR_ERR(priv->regs); - } + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) + return dev_err_probe(dev, PTR_ERR(priv->regs), + "failed to ioremap merge\n"); priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) { - dev_err(dev, "failed to get merge clk\n"); - return PTR_ERR(priv->clk); - } + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "failed to get merge clk\n"); priv->async_clk = devm_clk_get_optional(dev, "merge_async"); - if (IS_ERR(priv->async_clk)) { - dev_err(dev, "failed to get merge async clock\n"); - return PTR_ERR(priv->async_clk); - } + if (IS_ERR(priv->async_clk)) + return dev_err_probe(dev, PTR_ERR(priv->async_clk), + "failed to get merge async clock\n"); if (priv->async_clk) { priv->reset_ctl = devm_reset_control_get_optional_exclusive(dev, NULL); @@ -289,9 +349,9 @@ static int mtk_disp_merge_probe(struct platform_device *pdev) ret = component_add(dev, &mtk_disp_merge_component_ops); if (ret != 0) - dev_err(dev, "Failed to add component: %d\n", ret); + return dev_err_probe(dev, ret, "Failed to add component\n"); - return ret; + return 0; } static void mtk_disp_merge_remove(struct platform_device *pdev) @@ -308,10 +368,9 @@ MODULE_DEVICE_TABLE(of, mtk_disp_merge_driver_dt_match); struct platform_driver mtk_disp_merge_driver = { .probe = mtk_disp_merge_probe, - .remove_new = mtk_disp_merge_remove, + .remove = mtk_disp_merge_remove, .driver = { .name = "mediatek-disp-merge", - .owner = THIS_MODULE, .of_match_table = mtk_disp_merge_driver_dt_match, }, }; |