diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c | 10 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/lontium-lt8912b.c | 84 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/nwl-dsi.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/sec-dsim.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/ti-sn65dsi83.c | 67 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/dw_hdmi-imx.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx8mp-ldb.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/imx8qm-ldb.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif-mux-display.c | 37 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/lcdif-crtc.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdifv3/lcdifv3-crtc.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/mhdp/cdns-mhdp-imx8qm.c | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 16 | ||||
-rw-r--r-- | drivers/gpu/imx/lcdifv3/lcdifv3-common.c | 13 |
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 = <8912_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; } |