build/patch/kernel/archive/sunxi-5.10/megous/drm-sun4i-Support-taking-over-display-pipeline-state.patch

566 lines
18 KiB
Diff

From 7e737d031df08562043c0ea349dcd9c12b0e3f0e Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megous@megous.com>
Date: Sun, 5 Jul 2020 16:02:01 +0200
Subject: [PATCH 205/323] drm: sun4i: Support taking over display pipeline
state from p-boot
For perfect, flickerless and fast boot.
Signed-off-by: Ondrej Jirman <megous@megous.com>
---
drivers/clk/sunxi-ng/ccu-sun50i-a64.c | 4 +-
drivers/gpu/drm/drm_fb_helper.c | 16 ++++++
drivers/gpu/drm/panel/panel-sitronix-st7703.c | 17 ++++++
drivers/gpu/drm/sun4i/sun4i_tcon.c | 23 ++++++++
drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 +
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 13 +++++
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 2 +
drivers/gpu/drm/sun4i/sun8i_mixer.c | 55 +++++++++++++++++++
drivers/gpu/drm/sun4i/sun8i_mixer.h | 3 +
drivers/of/base.c | 1 +
drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 15 +++++
drivers/video/backlight/pwm_bl.c | 23 +++++++-
12 files changed, 172 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
index 3e76c751e..03e8051e3 100644
--- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
+++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.c
@@ -967,7 +967,9 @@ static int sun50i_a64_ccu_probe(struct platform_device *pdev)
val &= ~GENMASK(19, 16);
writel(val | (0 << 16), reg + SUN50I_A64_PLL_AUDIO_REG);
- writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
+ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &val);
+ if (ret)
+ writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
/* Force the parent of TCON0 to PLL-MIPI */
val = readl(reg + SUN50I_A64_TCON0_REG);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 8033467db..47a86dfd9 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -2082,6 +2082,8 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
struct fb_info *fbi;
u32 format;
void *vaddr;
+ u32 fb_start;
+ int ret;
drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
sizes->surface_width, sizes->surface_height,
@@ -2128,6 +2130,20 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
fbi->fix.smem_start =
page_to_phys(virt_to_page(fbi->screen_buffer));
#endif
+
+ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start);
+ if (ret == 0) {
+ // copy framebuffer contents from p-boot if reasonable
+ if (fbi->screen_size != 720 * 1440 * 4) {
+ pr_err("surface width(%d), height(%d) and bpp(%d) does not match p-boot requirements\n",
+ sizes->surface_width, sizes->surface_height,
+ sizes->surface_bpp);
+ return 0;
+ }
+
+ pr_err("passing framebuffer from p-boot to fbcon\n");
+ memcpy(vaddr, __va(fb_start), fbi->screen_size);
+ }
}
return 0;
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c
index 1fe0e7299..948790f1e 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c
@@ -58,6 +58,7 @@ struct st7703 {
struct dentry *debugfs;
const struct st7703_panel_desc *desc;
+ bool hw_preenabled;
};
struct st7703_panel_desc {
@@ -360,6 +361,11 @@ static int st7703_enable(struct drm_panel *panel)
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
int ret;
+ if (ctx->hw_preenabled) {
+ ctx->hw_preenabled = false;
+ return 0;
+ }
+
ret = ctx->desc->init_sequence(ctx);
if (ret < 0) {
dev_err(ctx->dev, "Panel init sequence failed: %d\n", ret);
@@ -424,8 +430,10 @@ static int st7703_prepare(struct drm_panel *panel)
if (ctx->prepared)
return 0;
+ if (!ctx->hw_preenabled) {
dev_dbg(ctx->dev, "Resetting the panel\n");
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ }
ret = regulator_enable(ctx->iovcc);
if (ret < 0) {
@@ -441,10 +449,12 @@ static int st7703_prepare(struct drm_panel *panel)
}
/* Give power supplies time to stabilize before deasserting reset. */
+ if (!ctx->hw_preenabled) {
usleep_range(10000, 20000);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
usleep_range(15000, 20000);
+ }
ctx->prepared = true;
@@ -521,12 +531,19 @@ static int st7703_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
struct st7703 *ctx;
+ u32 fb_start;
int ret;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
+ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start);
+ if (ret == 0) {
+ /* the display pipeline is already initialized by p-boot */
+ ctx->hw_preenabled = true;
+ }
+
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio)) {
dev_err(dev, "cannot get reset gpio\n");
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 0fc63ee45..0af3f5060 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -39,6 +39,8 @@
#include "sun8i_tcon_top.h"
#include "sunxi_engine.h"
+static bool hw_preconfigured;
+
static struct drm_connector *sun4i_tcon_get_connector(const struct drm_encoder *encoder)
{
struct drm_connector *connector;
@@ -738,6 +740,13 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon,
const struct drm_encoder *encoder,
const struct drm_display_mode *mode)
{
+ if (tcon->hw_preconfigured) {
+ // avoid the first modeset
+ tcon->hw_preconfigured = false;
+ hw_preconfigured = false;
+ return;
+ }
+
switch (encoder->encoder_type) {
case DRM_MODE_ENCODER_DSI:
/* DSI is tied to special case of CPU interface */
@@ -888,6 +897,7 @@ static int sun4i_tcon_init_regmap(struct device *dev,
return PTR_ERR(tcon->regs);
}
+ if (!tcon->hw_preconfigured) {
/* Make sure the TCON is disabled and all IRQs are off */
regmap_write(tcon->regs, SUN4I_TCON_GCTL_REG, 0);
regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0);
@@ -896,6 +906,7 @@ static int sun4i_tcon_init_regmap(struct device *dev,
/* Disable IO lines and set them to tristate */
regmap_write(tcon->regs, SUN4I_TCON0_IO_TRI_REG, ~0);
regmap_write(tcon->regs, SUN4I_TCON1_IO_TRI_REG, ~0);
+ }
return 0;
}
@@ -1167,6 +1178,9 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
tcon->dev = dev;
tcon->id = engine->id;
tcon->quirks = of_device_get_match_data(dev);
+
+ if (tcon->id == 0)
+ tcon->hw_preconfigured = hw_preconfigured;
tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
if (IS_ERR(tcon->lcd_rst)) {
@@ -1188,12 +1202,14 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
}
}
+ if (!tcon->hw_preconfigured) {
/* Make sure our TCON is reset */
ret = reset_control_reset(tcon->lcd_rst);
if (ret) {
dev_err(dev, "Couldn't deassert our reset line\n");
return ret;
}
+ }
if (tcon->quirks->supports_lvds) {
/*
@@ -1360,8 +1376,15 @@ static int sun4i_tcon_probe(struct platform_device *pdev)
const struct sun4i_tcon_quirks *quirks;
struct drm_bridge *bridge;
struct drm_panel *panel;
+ u32 fb_start;
int ret;
+ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start);
+ if (ret == 0) {
+ /* the display pipeline is already initialized by p-boot */
+ hw_preconfigured = true;
+ }
+
quirks = of_device_get_match_data(&pdev->dev);
/* panels and bridges are present only on TCONs with channel 0 */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index ea0e6b023..f532d87ea 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -293,6 +293,8 @@ struct sun4i_tcon {
/* TCON list management */
struct list_head list;
+
+ bool hw_preconfigured;
};
struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index d7fff121e..572d637dc 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -732,6 +732,7 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
reset_control_deassert(dsi->reset);
clk_prepare_enable(dsi->mod_clk);
+ if (!dsi->hw_preconfigured) {
/*
* Enable the DSI block.
*/
@@ -758,6 +759,7 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
sun6i_dsi_setup_inst_loop(dsi, mode);
sun6i_dsi_setup_format(dsi, mode);
sun6i_dsi_setup_timings(dsi, mode);
+ }
phy_init(dsi->dphy);
@@ -787,11 +789,15 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
if (dsi->panel)
drm_panel_enable(dsi->panel);
+ if (!dsi->hw_preconfigured) {
sun6i_dsi_start(dsi, DSI_START_HSC);
udelay(1000);
sun6i_dsi_start(dsi, DSI_START_HSD);
+ }
+
+ dsi->hw_preconfigured = false;
}
static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
@@ -1107,6 +1113,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
struct sun6i_dsi *dsi;
struct resource *res;
void __iomem *base;
+ u32 fb_start;
int ret;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
@@ -1117,6 +1124,12 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
dsi->host.ops = &sun6i_dsi_host_ops;
dsi->host.dev = dev;
+ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start);
+ if (ret == 0) {
+ /* the display pipeline is already initialized by p-boot */
+ dsi->hw_preconfigured = true;
+ }
+
if (of_device_is_compatible(dev->of_node,
"allwinner,sun6i-a31-mipi-dsi"))
bus_clk_name = "bus";
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
index c863900ae..7f80ff130 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
@@ -31,6 +31,8 @@ struct sun6i_dsi {
struct mipi_dsi_device *device;
struct drm_device *drm;
struct drm_panel *panel;
+
+ bool hw_preconfigured;
};
static inline struct sun6i_dsi *host_to_sun6i_dsi(struct mipi_dsi_host *host)
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 5b42cf25c..65b24dce7 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -22,6 +22,7 @@
#include <drm/drm_probe_helper.h>
#include "sun4i_drv.h"
+#include "sun4i_tcon.h"
#include "sun8i_mixer.h"
#include "sun8i_ui_layer.h"
#include "sun8i_vi_layer.h"
@@ -32,6 +33,8 @@ struct de2_fmt_info {
u32 de2_fmt;
};
+static bool hw_preconfigured;
+
static const struct de2_fmt_info de2_formats[] = {
{
.drm_fmt = DRM_FORMAT_ARGB8888,
@@ -250,8 +253,38 @@ int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format)
static void sun8i_mixer_commit(struct sunxi_engine *engine)
{
+ struct sun8i_mixer* mixer = engine_to_sun8i_mixer(engine);
+ struct sun4i_tcon* tcon;
+ u32 val, saved, ret;
+
DRM_DEBUG_DRIVER("Committing changes\n");
+ if (mixer->hw_preconfigured && engine->id == 0) {
+ /*
+ * This is the first commit, wait for vblank on tcon0 before continuing.
+ */
+ list_for_each_entry(tcon, &mixer->drv->tcon_list, list) {
+ if (tcon->id == 0) {
+ regmap_read(tcon->regs, SUN4I_TCON_GINT0_REG, &saved);
+ saved &= 0xffff0000;
+
+ regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, 0);
+
+ pr_err("polling for the first vblank\n");
+ ret = regmap_read_poll_timeout(tcon->regs, SUN4I_TCON_GINT0_REG, val,
+ val & (SUN4I_TCON_GINT0_VBLANK_INT(0) |
+ SUN4I_TCON_GINT0_VBLANK_INT(1) |
+ SUN4I_TCON_GINT0_TCON0_TRI_FINISH_INT),
+ 100, 40000);
+ pr_err("polling for the first vblank done %d\n", ret);
+
+ regmap_write(tcon->regs, SUN4I_TCON_GINT0_REG, saved);
+ }
+ }
+
+ mixer->hw_preconfigured = false;
+ }
+
regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF,
SUN8I_MIXER_GLOBAL_DBUFF_ENABLE);
}
@@ -362,6 +395,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
dev_set_drvdata(dev, mixer);
mixer->engine.ops = &sun8i_engine_ops;
mixer->engine.node = dev->of_node;
+ mixer->drv = drv;
if (of_find_property(dev->of_node, "iommus", NULL)) {
/*
@@ -386,6 +420,11 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
*/
mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node);
+ if (mixer->engine.id == 0) {
+ mixer->hw_preconfigured = hw_preconfigured;
+ hw_preconfigured = false;
+ }
+
mixer->cfg = of_device_get_match_data(dev);
if (!mixer->cfg)
return -EINVAL;
@@ -434,8 +473,11 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
* reason for the mixer to be functional. Make sure it's the
* case.
*/
+
+ if (!mixer->hw_preconfigured) {
if (mixer->cfg->mod_rate)
clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate);
+ }
clk_prepare_enable(mixer->mod_clk);
@@ -443,6 +485,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
base = sun8i_blender_base(mixer);
+ if (!mixer->hw_preconfigured) {
/* Reset registers and disable unused sub-engines */
if (mixer->cfg->is_de3) {
for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4)
@@ -474,6 +517,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
/* Enable the mixer */
regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL,
SUN8I_MIXER_GLOBAL_CTL_RT_EN);
+ } /* hw_preconfigured */
/* Set background color to black */
regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base),
@@ -494,8 +538,10 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master,
SUN8I_MIXER_BLEND_MODE(base, i),
SUN8I_MIXER_BLEND_MODE_DEF);
+ if (!mixer->hw_preconfigured) {
regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base),
SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0);
+ }
return 0;
@@ -525,6 +571,15 @@ static const struct component_ops sun8i_mixer_ops = {
static int sun8i_mixer_probe(struct platform_device *pdev)
{
+ int ret;
+ u32 fb_start;
+
+ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start);
+ if (ret == 0) {
+ /* the display pipeline is already initialized by p-boot */
+ hw_preconfigured = true;
+ }
+
return component_add(&pdev->dev, &sun8i_mixer_ops);
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index 7576b523f..1d31fdc71 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -179,6 +179,9 @@ struct sun8i_mixer {
struct clk *bus_clk;
struct clk *mod_clk;
+
+ struct sun4i_drv *drv;
+ bool hw_preconfigured;
};
static inline struct sun8i_mixer *
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 161a23631..bb539f7fe 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -36,6 +36,7 @@ LIST_HEAD(aliases_lookup);
struct device_node *of_root;
EXPORT_SYMBOL(of_root);
struct device_node *of_chosen;
+EXPORT_SYMBOL(of_chosen);
struct device_node *of_aliases;
struct device_node *of_stdout;
static const char *of_stdout_options;
diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
index 1fa761ba6..9863c6a2f 100644
--- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
+++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
@@ -92,6 +92,8 @@ struct sun6i_dphy {
struct phy *phy;
struct phy_configure_opts_mipi_dphy config;
+
+ bool hw_preconfigured;
};
static int sun6i_dphy_init(struct phy *phy)
@@ -124,6 +126,11 @@ static int sun6i_dphy_power_on(struct phy *phy)
struct sun6i_dphy *dphy = phy_get_drvdata(phy);
u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
+ if (dphy->hw_preconfigured) {
+ dphy->hw_preconfigured = false;
+ return 0;
+ }
+
regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
@@ -255,11 +262,19 @@ static int sun6i_dphy_probe(struct platform_device *pdev)
struct sun6i_dphy *dphy;
struct resource *res;
void __iomem *regs;
+ u32 fb_start;
+ int ret;
dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
return -ENOMEM;
+ ret = of_property_read_u32_index(of_chosen, "p-boot,framebuffer-start", 0, &fb_start);
+ if (ret == 0) {
+ /* the display pipeline is already initialized by p-boot */
+ dphy->hw_preconfigured = true;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(regs)) {
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 1cf924f3a..b0dfa5441 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -466,7 +466,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
struct backlight_device *bl;
struct device_node *node = pdev->dev.of_node;
struct pwm_bl_data *pb;
- struct pwm_state state;
+ struct pwm_state state, state_real;
unsigned int i;
int ret;
@@ -533,6 +533,11 @@ static int pwm_backlight_probe(struct platform_device *pdev)
/* Sync up PWM state. */
pwm_init_state(pb->pwm, &state);
+ /* Read real state, but only if the PWM is enabled. */
+ pwm_get_state(pb->pwm, &state_real);
+ if (state_real.enabled)
+ state = state_real;
+
/*
* The DT case will set the pwm_period_ns field to 0 and store the
* period, parsed from the DT, in the PWM device. For the non-DT case,
@@ -627,6 +632,22 @@ static int pwm_backlight_probe(struct platform_device *pdev)
bl->props.brightness = data->dft_brightness;
bl->props.power = pwm_backlight_initial_power_state(pb);
+ if (bl->props.power == FB_BLANK_UNBLANK && pb->levels) {
+ /* If the backlight is already on, determine the default
+ * brightness from PWM duty cycle instead of forcing
+ * the brightness determined by the driver
+ */
+ pwm_get_state(pb->pwm, &state);
+ u64 level = (u64)state.duty_cycle * pb->scale;
+ do_div(level, (u64)state.period);
+
+ for (i = 0; i <= data->max_brightness; i++) {
+ if (data->levels[i] > level) {
+ bl->props.brightness = i;
+ break;
+ }
+ }
+ }
backlight_update_status(bl);
platform_set_drvdata(pdev, bl);
--
2.34.0