summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c1
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c10
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt8912b.c84
-rw-r--r--drivers/gpu/drm/bridge/nwl-dsi.c13
-rw-r--r--drivers/gpu/drm/bridge/sec-dsim.c6
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi83.c67
-rw-r--r--drivers/gpu/drm/imx/dw_hdmi-imx.c20
-rw-r--r--drivers/gpu/drm/imx/imx8mp-ldb.c23
-rw-r--r--drivers/gpu/drm/imx/imx8qm-ldb.c23
-rw-r--r--drivers/gpu/drm/imx/lcdif-mux-display.c37
-rw-r--r--drivers/gpu/drm/imx/lcdif/lcdif-crtc.c4
-rw-r--r--drivers/gpu/drm/imx/lcdifv3/lcdifv3-crtc.c6
-rw-r--r--drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c32
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c16
-rw-r--r--drivers/gpu/imx/lcdifv3/lcdifv3-common.c13
15 files changed, 275 insertions, 80 deletions
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
index 35a37f6b6d6f..9edf6772d160 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c
@@ -309,7 +309,6 @@ err_reg_read:
mutex_unlock(&mhdp->api_lock);
DRM_DEV_ERROR(mhdp->dev, "Failed to read register.\n");
-mutex_unlock(&mhdp->api_lock);
return ret;
}
EXPORT_SYMBOL(cdns_mhdp_reg_read);
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
index 47c853781d84..e64a36bd58b4 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
@@ -54,6 +54,8 @@ int cdns_hdmi_get_edid_block(void *data, u8 *edid,
u8 msg[2], reg[5], i;
int ret;
+ mutex_lock(&mhdp->api_lock);
+
for (i = 0; i < 4; i++) {
msg[0] = block / 2;
msg[1] = block % 2;
@@ -80,6 +82,8 @@ int cdns_hdmi_get_edid_block(void *data, u8 *edid,
break;
}
+ mutex_unlock(&mhdp->api_lock);
+
if (ret)
DRM_ERROR("get block[%d] edid failed: %d\n", block, ret);
return ret;
@@ -90,6 +94,8 @@ int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data)
u8 msg[4], reg[6];
int ret;
+ mutex_lock(&mhdp->api_lock);
+
msg[0] = 0x54;
msg[1] = addr;
msg[2] = 0;
@@ -111,6 +117,7 @@ int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data)
*data = reg[5];
err_scdc_read:
+ mutex_unlock(&mhdp->api_lock);
if (ret)
DRM_ERROR("scdc read failed: %d\n", ret);
return ret;
@@ -121,6 +128,8 @@ int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value)
u8 msg[5], reg[5];
int ret;
+ mutex_lock(&mhdp->api_lock);
+
msg[0] = 0x54;
msg[1] = addr;
msg[2] = 0;
@@ -144,6 +153,7 @@ int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value)
ret = -EINVAL;
err_scdc_write:
+ mutex_unlock(&mhdp->api_lock);
if (ret)
DRM_ERROR("scdc write failed: %d\n", ret);
return ret;
diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c
index 9dd52282a055..ce7aa1981b96 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8912b.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c
@@ -41,6 +41,8 @@ struct lt8912 {
struct videomode mode;
+ struct regulator_bulk_data supplies[7];
+
u8 data_lanes;
bool is_power_on;
};
@@ -162,24 +164,32 @@ static int lt8912_write_rxlogicres_config(struct lt8912 *lt)
return ret;
};
+/* enable LVDS output with some hardcoded configuration, not required for the HDMI output */
static int lt8912_write_lvds_config(struct lt8912 *lt)
{
const struct reg_sequence seq[] = {
+ // lvds power up
{0x44, 0x30},
{0x51, 0x05},
- {0x50, 0x24},
- {0x51, 0x2d},
- {0x52, 0x04},
- {0x69, 0x0e},
+
+ // core pll bypass
+ {0x50, 0x24}, // cp=50uA
+ {0x51, 0x2d}, // Pix_clk as reference, second order passive LPF PLL
+ {0x52, 0x04}, // loopdiv=0, use second-order PLL
+ {0x69, 0x0e}, // CP_PRESET_DIV_RATIO
{0x69, 0x8e},
{0x6a, 0x00},
- {0x6c, 0xb8},
+ {0x6c, 0xb8}, // RGD_CP_SOFT_K_EN,RGD_CP_SOFT_K[13:8]
{0x6b, 0x51},
- {0x04, 0xfb},
+
+ {0x04, 0xfb}, // core pll reset
{0x04, 0xff},
- {0x7f, 0x00},
- {0xa8, 0x13},
- {0x02, 0xf7},
+
+ // scaler bypass
+ {0x7f, 0x00}, // disable scaler
+ {0xa8, 0x13}, // 0x13: JEIDA, 0x33: VESA
+
+ {0x02, 0xf7}, // lvds pll reset
{0x02, 0xff},
{0x03, 0xcf},
{0x03, 0xff},
@@ -247,6 +257,12 @@ static int lt8912_free_i2c(struct lt8912 *lt)
static int lt8912_hard_power_on(struct lt8912 *lt)
{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(lt->supplies), lt->supplies);
+ if (ret)
+ return ret;
+
gpiod_set_value_cansleep(lt->gp_reset, 0);
msleep(20);
@@ -257,6 +273,9 @@ static void lt8912_hard_power_off(struct lt8912 *lt)
{
gpiod_set_value_cansleep(lt->gp_reset, 1);
msleep(20);
+
+ regulator_bulk_disable(ARRAY_SIZE(lt->supplies), lt->supplies);
+
lt->is_power_on = false;
}
@@ -627,6 +646,48 @@ static const struct drm_bridge_funcs lt8912_bridge_funcs = {
.get_edid = lt8912_bridge_get_edid,
};
+static int __maybe_unused lt8912_bridge_resume(struct device *dev)
+{
+ struct lt8912 *lt = dev_get_drvdata(dev);
+ int ret;
+
+ ret = lt8912_hard_power_on(lt);
+ if (ret)
+ return ret;
+
+ ret = lt8912_soft_power_on(lt);
+ if (ret)
+ return ret;
+
+ return lt8912_video_on(lt);
+}
+
+static int __maybe_unused lt8912_bridge_suspend(struct device *dev)
+{
+ struct lt8912 *lt = dev_get_drvdata(dev);
+
+ lt8912_hard_power_off(lt);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(lt8912_bridge_pm_ops, lt8912_bridge_suspend, lt8912_bridge_resume);
+
+static int lt8912_get_regulators(struct lt8912 *lt)
+{
+ unsigned int i;
+ const char * const supply_names[] = {
+ "vdd", "vccmipirx", "vccsysclk", "vcclvdstx",
+ "vcchdmitx", "vcclvdspll", "vcchdmipll"
+ };
+
+ for (i = 0; i < ARRAY_SIZE(lt->supplies); i++)
+ lt->supplies[i].supply = supply_names[i];
+
+ return devm_regulator_bulk_get(lt->dev, ARRAY_SIZE(lt->supplies),
+ lt->supplies);
+}
+
static int lt8912_parse_dt(struct lt8912 *lt)
{
struct gpio_desc *gp_reset;
@@ -683,6 +744,10 @@ static int lt8912_parse_dt(struct lt8912 *lt)
goto err_free_host_node;
}
+ ret = lt8912_get_regulators(lt);
+ if (ret)
+ goto err_free_host_node;
+
of_node_put(port_node);
return 0;
@@ -763,6 +828,7 @@ static struct i2c_driver lt8912_i2c_driver = {
.driver = {
.name = "lt8912",
.of_match_table = lt8912_dt_match,
+ .pm = &lt8912_bridge_pm_ops,
},
.probe = lt8912_probe,
.remove = lt8912_remove,
diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index b19cf038025d..193e9e140d3e 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -434,6 +434,19 @@ static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
hbp = bytes * hback_porch - 10;
hsa = bytes * hsync_len - 10;
hsa = roundup(hsa, 2);
+ } else if (of_device_is_compatible(dsi->panel_bridge->of_node,
+ "lontium,lt8912b")) {
+ int bytes = mipi_dsi_pixel_format_to_bpp(dsi->format) >> 3;
+
+ /*
+ * Most likely we have the same issue with the LT8912B as
+ * described above. However, magic timings are a bit different
+ * compared to the displays.
+ */
+ hfp = hfront_porch;
+ hbp = bytes * hback_porch;
+ hsa = bytes * hsync_len;
+ hsa = roundup(hsa, 2);
} else {
hfp = hfront_porch;
hbp = hback_porch;
diff --git a/drivers/gpu/drm/bridge/sec-dsim.c b/drivers/gpu/drm/bridge/sec-dsim.c
index 1eb34e755509..dd4c0b0c2078 100644
--- a/drivers/gpu/drm/bridge/sec-dsim.c
+++ b/drivers/gpu/drm/bridge/sec-dsim.c
@@ -452,9 +452,9 @@ static int sec_mipi_dsim_host_attach(struct mipi_dsi_host *host,
if (dsim->channel)
return -EINVAL;
- if ((dsi->mode_flags & MIPI_DSI_MODE_VIDEO) &&
- !((dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) ||
- (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) {
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO &&
+ dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST &&
+ dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
dev_err(dev, "unsupported dsi mode\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index f96c0a89854b..14e7ed36ba98 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -33,6 +33,7 @@
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
@@ -143,6 +144,7 @@ struct sn65dsi83 {
struct mipi_dsi_device *dsi;
struct drm_bridge *panel_bridge;
struct gpio_desc *enable_gpio;
+ struct regulator *vcc;
int dsi_lanes;
bool lvds_dual_link;
bool lvds_dual_link_even_odd_swap;
@@ -290,20 +292,16 @@ err_dsi_attach:
return ret;
}
-static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
- struct drm_bridge_state *old_bridge_state)
+static void sn65dsi83_detach(struct drm_bridge *bridge)
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
- /*
- * Reset the chip, pull EN line low for t_reset=10ms,
- * then high for t_en=1ms.
- */
- regcache_mark_dirty(ctx->regmap);
- gpiod_set_value(ctx->enable_gpio, 0);
- usleep_range(10000, 11000);
- gpiod_set_value(ctx->enable_gpio, 1);
- usleep_range(1000, 1100);
+ if (!ctx->dsi)
+ return;
+
+ mipi_dsi_detach(ctx->dsi);
+ mipi_dsi_device_unregister(ctx->dsi);
+ ctx->dsi = NULL;
}
static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx,
@@ -383,7 +381,15 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
u16 val;
int ret;
- usleep_range(10000, 11000);
+ ret = regulator_enable(ctx->vcc);
+ if (ret) {
+ dev_err(ctx->dev, "Failed to enable vcc: %d\n", ret);
+ return;
+ }
+
+ /* Deassert reset */
+ gpiod_set_value(ctx->enable_gpio, 1);
+ usleep_range(1000, 1100);
/* Get the LVDS format from the bridge state. */
bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
@@ -515,6 +521,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
dev_err(ctx->dev, "failed to lock PLL, ret=%i\n", ret);
/* On failure, disable PLL again and exit. */
regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00);
+ regulator_disable(ctx->vcc);
return;
}
@@ -530,19 +537,17 @@ static void sn65dsi83_atomic_disable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
+ int ret;
- /* Clear reset, disable PLL */
- regmap_write(ctx->regmap, REG_RC_RESET, 0x00);
- regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00);
-}
+ /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
+ gpiod_set_value(ctx->enable_gpio, 0);
+ usleep_range(10000, 11000);
-static void sn65dsi83_atomic_post_disable(struct drm_bridge *bridge,
- struct drm_bridge_state *old_bridge_state)
-{
- struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
+ ret = regulator_disable(ctx->vcc);
+ if (ret)
+ dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret);
- /* Put the chip in reset, pull EN line low. */
- gpiod_set_value(ctx->enable_gpio, 0);
+ regcache_mark_dirty(ctx->regmap);
}
static enum drm_mode_status
@@ -587,10 +592,9 @@ sn65dsi83_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs sn65dsi83_funcs = {
.attach = sn65dsi83_attach,
- .atomic_pre_enable = sn65dsi83_atomic_pre_enable,
+ .detach = sn65dsi83_detach,
.atomic_enable = sn65dsi83_atomic_enable,
.atomic_disable = sn65dsi83_atomic_disable,
- .atomic_post_disable = sn65dsi83_atomic_post_disable,
.mode_valid = sn65dsi83_mode_valid,
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
@@ -657,6 +661,11 @@ static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
ctx->panel_bridge = panel_bridge;
+ ctx->vcc = devm_regulator_get(dev, "vcc");
+ if (IS_ERR(ctx->vcc))
+ return dev_err_probe(dev, PTR_ERR(ctx->vcc),
+ "Failed to get supply 'vcc'\n");
+
return 0;
err_put_node:
@@ -685,10 +694,14 @@ static int sn65dsi83_probe(struct i2c_client *client,
model = id->driver_data;
}
- ctx->enable_gpio = devm_gpiod_get(ctx->dev, "enable", GPIOD_OUT_LOW);
+ /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
+ ctx->enable_gpio = devm_gpiod_get_optional(ctx->dev, "enable",
+ GPIOD_OUT_LOW);
if (IS_ERR(ctx->enable_gpio))
return PTR_ERR(ctx->enable_gpio);
+ usleep_range(10000, 11000);
+
ret = sn65dsi83_parse_dt(ctx, model);
if (ret)
return ret;
@@ -717,8 +730,8 @@ static int sn65dsi83_remove(struct i2c_client *client)
{
struct sn65dsi83 *ctx = i2c_get_clientdata(client);
- mipi_dsi_detach(ctx->dsi);
- mipi_dsi_device_unregister(ctx->dsi);
+ sn65dsi83_detach(&ctx->bridge);
+ sn65dsi83_atomic_disable(&ctx->bridge, NULL);
drm_bridge_remove(&ctx->bridge);
of_node_put(ctx->host_node);
diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index b08eae5278d0..283a9675901f 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c
@@ -193,11 +193,16 @@ imx6dl_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
static bool imx8mp_hdmi_check_clk_rate(int rate_khz)
{
- int rate = rate_khz * 1000;
+ int rate;
/* Check hdmi phy pixel clock support rate */
- if (rate != clk_round_rate(imx8mp_clocks[0].clk, rate))
+ rate = clk_round_rate(imx8mp_clocks[0].clk, rate_khz * 1000);
+ /* Drop mode if pixelclk generated is more than 10% off */
+ if ((rate < rate_khz * 900) || (rate > rate_khz * 1100)) {
+ pr_info("%s: mode with pixelclk %i kHz dropped\n",
+ __func__, rate_khz);
return false;
+ }
return true;
}
@@ -280,7 +285,6 @@ static int imx8mp_hdmi_phy_init(struct dw_hdmi *dw_hdmi, void *data,
static void imx8mp_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data)
{
struct imx_hdmi *hdmi = (struct imx_hdmi *)data;
- int val;
dev_dbg(hdmi->dev, "%s\n", __func__);
if (!hdmi->phy)
@@ -289,16 +293,6 @@ static void imx8mp_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data)
/* disable PVI */
imx8mp_hdmi_pvi_disable();
imx8mp_hdmi_pavi_powerdown();
-
- /* TODO */
- regmap_read(hdmi->regmap, 0x200, &val);
- /* Disable CEC */
- val &= ~0x2;
- /* Power down HDMI PHY
- * TODO move PHY power off to hdmi phy driver
- * val |= 0x8;
- * regmap_write(hdmi->regmap, 0x200, val);
- */
}
static int imx8mp_hdmimix_setup(struct imx_hdmi *hdmi)
diff --git a/drivers/gpu/drm/imx/imx8mp-ldb.c b/drivers/gpu/drm/imx/imx8mp-ldb.c
index be969f453587..195146f82839 100644
--- a/drivers/gpu/drm/imx/imx8mp-ldb.c
+++ b/drivers/gpu/drm/imx/imx8mp-ldb.c
@@ -13,6 +13,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_of.h>
#include "imx-drm.h"
@@ -398,6 +399,28 @@ static int imx8mp_ldb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct imx8mp_ldb *imx8mp_ldb;
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int ret;
+
+ /*
+ * Make sure we can defer already in the probe function else we will
+ * have an issue in the bind function. It is expected that in the bind
+ * function the driver is functional.
+ */
+ for_each_child_of_node(np, child) {
+ struct drm_panel *panel;
+ struct drm_bridge *next_bridge;
+
+ ret = drm_of_find_panel_or_bridge(child, 1, 0,
+ &panel, &next_bridge);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ /*
+ * We can continue even if there is an error, this is most
+ * likely because this driver has not loaded yet
+ */
+ }
imx8mp_ldb = devm_kzalloc(dev, sizeof(*imx8mp_ldb), GFP_KERNEL);
if (!imx8mp_ldb)
diff --git a/drivers/gpu/drm/imx/imx8qm-ldb.c b/drivers/gpu/drm/imx/imx8qm-ldb.c
index 383c80014fdf..05751505f3de 100644
--- a/drivers/gpu/drm/imx/imx8qm-ldb.c
+++ b/drivers/gpu/drm/imx/imx8qm-ldb.c
@@ -14,6 +14,7 @@
#include <drm/bridge/fsl_imx_ldb.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -503,6 +504,28 @@ static int imx8qm_ldb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct imx8qm_ldb *imx8qm_ldb;
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int ret;
+
+ /*
+ * Make sure we can defer already in the probe function else we will
+ * have an issue in the bind function. It is expected that in the bind
+ * function the driver is functional.
+ */
+ for_each_child_of_node(np, child) {
+ struct drm_panel *panel;
+ struct drm_bridge *next_bridge;
+
+ ret = drm_of_find_panel_or_bridge(child, 1, 0,
+ &panel, &next_bridge);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ /*
+ * We can continue even if there is an error, this is most
+ * likely because this driver has not loaded yet
+ */
+ }
imx8qm_ldb = devm_kzalloc(dev, sizeof(*imx8qm_ldb), GFP_KERNEL);
if (!imx8qm_ldb)
diff --git a/drivers/gpu/drm/imx/lcdif-mux-display.c b/drivers/gpu/drm/imx/lcdif-mux-display.c
index 7e218269407d..c0a9866a8a66 100644
--- a/drivers/gpu/drm/imx/lcdif-mux-display.c
+++ b/drivers/gpu/drm/imx/lcdif-mux-display.c
@@ -49,20 +49,6 @@ static inline struct imx_lcdif_mux_display *enc_to_lmuxd(struct drm_encoder *e)
return container_of(e, struct imx_lcdif_mux_display, encoder);
}
-static void imx_lmuxd_encoder_enable(struct drm_encoder *encoder)
-{
- struct imx_lcdif_mux_display *lmuxd = enc_to_lmuxd(encoder);
-
- clk_prepare_enable(lmuxd->clk_pixel);
-}
-
-static void imx_lmuxd_encoder_disable(struct drm_encoder *encoder)
-{
- struct imx_lcdif_mux_display *lmuxd = enc_to_lmuxd(encoder);
-
- clk_disable_unprepare(lmuxd->clk_pixel);
-}
-
static void
imx_lmuxd_encoder_atomic_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
@@ -117,8 +103,6 @@ imx_lmuxd_encoder_atomic_check(struct drm_encoder *encoder,
}
static const struct drm_encoder_helper_funcs imx_lmuxd_encoder_helper_funcs = {
- .enable = imx_lmuxd_encoder_enable,
- .disable = imx_lmuxd_encoder_disable,
.atomic_mode_set = imx_lmuxd_encoder_atomic_mode_set,
.atomic_check = imx_lmuxd_encoder_atomic_check,
};
@@ -195,14 +179,33 @@ static int imx_lmuxd_bind(struct device *dev, struct device *master, void *data)
}
}
+ /**
+ * We need to make sure the clock is enabled the whole time we use this
+ * driver. Else it might happen that something in the display pipeline
+ * runs out of sync and we see some ghosting effects. It is not clear
+ * what exactly triggers this issue and could maybe also be solved in
+ * the driver that causes the issue by adding a dependency to the pixel
+ * clock.
+ */
+ ret = clk_prepare_enable(lmuxd->clk_pixel);
+ if (ret)
+ return ret;
+
lmuxd->dev = dev;
- return imx_lmuxd_register(drm, lmuxd);
+ ret = imx_lmuxd_register(drm, lmuxd);
+ if (ret)
+ clk_disable_unprepare(lmuxd->clk_pixel);
+
+ return ret;
}
static void imx_lmuxd_unbind(struct device *dev, struct device *master,
void *data)
{
+ struct imx_lcdif_mux_display *lmuxd = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(lmuxd->clk_pixel);
}
static const struct component_ops imx_lmuxd_ops = {
diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c b/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c
index 195555343158..ee53652f68ef 100644
--- a/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c
+++ b/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c
@@ -255,7 +255,9 @@ static enum drm_mode_status lcdif_crtc_mode_valid(struct drm_crtc *crtc,
check_pix_clk:
rate = lcdif_pix_clk_round_rate(lcdif, mode->clock * 1000);
- if (rate <= 0 || rate != mode->clock * 1000)
+ /* allow +/-10% pixel clock rate shift */
+ if (rate < mode->clock * 900 ||
+ rate > mode->clock * 1100)
return MODE_BAD;
return MODE_OK;
diff --git a/drivers/gpu/drm/imx/lcdifv3/lcdifv3-crtc.c b/drivers/gpu/drm/imx/lcdifv3/lcdifv3-crtc.c
index 5a7133743850..e594556ffce8 100644
--- a/drivers/gpu/drm/imx/lcdifv3/lcdifv3-crtc.c
+++ b/drivers/gpu/drm/imx/lcdifv3/lcdifv3-crtc.c
@@ -231,9 +231,9 @@ check_pix_clk:
if (rounded_rate <= 0)
return MODE_BAD;
- /* allow +/-0.5% HDMI pixel clock rate shift */
- if (rounded_rate < pclk_rate * 995 / 1000 ||
- rounded_rate > pclk_rate * 1005 / 1000)
+ /* allow +/-10% pixel clock rate shift */
+ if (rounded_rate < pclk_rate * 900 / 1000 ||
+ rounded_rate > pclk_rate * 1100 / 1000)
return MODE_BAD;
return MODE_OK;
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c
index 4fca888c3991..9fce234951b0 100644
--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c
@@ -12,6 +12,7 @@
#include <linux/clk.h>
#include <drm/drm_vblank.h>
#include <drm/drm_print.h>
+#include <linux/delay.h>
#include "cdns-mhdp-imx.h"
@@ -554,6 +555,7 @@ static int cdns_mhdp_firmware_load(struct imx_mhdp_device *imx_mhdp)
{
const u8 *iram;
const u8 *dram;
+ int i;
u32 rate;
int ret;
@@ -568,6 +570,12 @@ static int cdns_mhdp_firmware_load(struct imx_mhdp_device *imx_mhdp)
if (!imx_mhdp->firmware_name)
goto out;
+ /* test if ucpu is already running and if so skip fw loading */
+ if (!cdns_mhdp_bus_read(&imx_mhdp->mhdp, APB_CTRL)) {
+ DRM_INFO("HDMI uCPU already running, skipping FW load.\n");
+ goto out;
+ }
+
if (!imx_mhdp->fw) {
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOUEVENT,
imx_mhdp->firmware_name,
@@ -578,14 +586,28 @@ static int cdns_mhdp_firmware_load(struct imx_mhdp_device *imx_mhdp)
DRM_ERROR("failed to load firmware\n");
return -ENOENT;
}
- } else {
- iram = imx_mhdp->fw->data + FW_IRAM_OFFSET;
- dram = iram + FW_IRAM_SIZE;
+ }
- cdns_mhdp_firmware_write_section(imx_mhdp, iram, FW_IRAM_SIZE, ADDR_IMEM);
- cdns_mhdp_firmware_write_section(imx_mhdp, dram, FW_DRAM_SIZE, ADDR_DMEM);
+ for (i = 0; i < 10; i++) {
+ if (imx_mhdp->fw)
+ break;
+ usleep_range(1000, 10000);
}
+ if (!imx_mhdp->fw) {
+ DRM_ERROR("FW loading timed out\n");
+ return -ENXIO;
+ }
+
+ /* Copy the firmware to the hdmi controller */
+ iram = imx_mhdp->fw->data + FW_IRAM_OFFSET;
+ dram = iram + FW_IRAM_SIZE;
+
+ cdns_mhdp_firmware_write_section(imx_mhdp, iram,
+ FW_IRAM_SIZE, ADDR_IMEM);
+ cdns_mhdp_firmware_write_section(imx_mhdp, dram,
+ FW_DRAM_SIZE, ADDR_DMEM);
+
out:
/* un-reset ucpu */
cdns_mhdp_bus_write(0, &imx_mhdp->mhdp, APB_CTRL);
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 3996e88d044f..08a4af19d74c 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -575,6 +575,7 @@ static int panel_dpi_probe(struct device *dev,
struct panel_desc *desc;
unsigned int bus_flags;
struct videomode vm;
+ const char *mapping = "";
int ret;
np = dev->of_node;
@@ -599,6 +600,21 @@ static int panel_dpi_probe(struct device *dev,
of_property_read_u32(np, "width-mm", &desc->size.width);
of_property_read_u32(np, "height-mm", &desc->size.height);
+ of_property_read_string(np, "data-mapping", &mapping);
+ if (!strcmp(mapping, "rgb24")) {
+ desc->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ desc->bpc = 8;
+ } else if (!strcmp(mapping, "rgb565")) {
+ desc->bus_format = MEDIA_BUS_FMT_RGB565_1X16;
+ desc->bpc = 6;
+ } else if (!strcmp(mapping, "bgr666")) {
+ desc->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
+ desc->bpc = 6;
+ } else if (!strcmp(mapping, "lvds666")) {
+ desc->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
+ desc->bpc = 6;
+ }
+
/* Extract bus_flags from display_timing */
bus_flags = 0;
vm.flags = timing->flags;
diff --git a/drivers/gpu/imx/lcdifv3/lcdifv3-common.c b/drivers/gpu/imx/lcdifv3/lcdifv3-common.c
index 8dab74c3fdad..c61ad6bbc192 100644
--- a/drivers/gpu/imx/lcdifv3/lcdifv3-common.c
+++ b/drivers/gpu/imx/lcdifv3/lcdifv3-common.c
@@ -734,7 +734,8 @@ static int imx_lcdifv3_probe(struct platform_device *pdev)
if (IS_ERR(lcdifv3->base))
return PTR_ERR(lcdifv3->base);
- if (of_device_is_compatible(np, "fsl,imx93-lcdif")) {
+ if (of_device_is_compatible(np, "fsl,imx93-lcdif") ||
+ of_device_is_compatible(np, "fsl,imx8mp-lcdif3")) {
lcdifv3->gpr = syscon_regmap_lookup_by_phandle(np, "fsl,gpr");
if (IS_ERR(lcdifv3->gpr)) {
ret = PTR_ERR(lcdifv3->gpr);
@@ -797,6 +798,7 @@ static int imx_lcdifv3_remove(struct platform_device *pdev)
static int imx_lcdifv3_runtime_suspend(struct device *dev)
{
struct lcdifv3_soc *lcdifv3 = dev_get_drvdata(dev);
+ int val;
if (atomic_inc_return(&lcdifv3->rpm_suspended) > 1)
return 0;
@@ -809,6 +811,15 @@ static int imx_lcdifv3_runtime_suspend(struct device *dev)
if (of_device_is_compatible(dev->of_node, "fsl,imx93-lcdif"))
regmap_write(lcdifv3->gpr, 0xc, 0x0);
+ if (of_device_is_compatible(dev->of_node, "fsl,imx8mp-lcdif3")) {
+ regmap_read(lcdifv3->gpr, 0x200, &val);
+ /* Disable CEC */
+ val &= ~0x2;
+ /* Power Down HDMI PHY */
+ val |= 0x8;
+ regmap_write(lcdifv3->gpr, 0x200, val);
+ }
+
return 0;
}