diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c')
-rw-r--r-- | drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 129 |
1 files changed, 110 insertions, 19 deletions
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index b2efecf7d160..824fb3c65742 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -12,8 +12,10 @@ #include <linux/component.h> #include <linux/debugfs.h> #include <linux/iopoll.h> +#include <linux/math64.h> +#include <linux/media-bus-format.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/reset.h> @@ -22,6 +24,7 @@ #include <drm/bridge/dw_mipi_dsi.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> +#include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> @@ -265,6 +268,7 @@ struct dw_mipi_dsi { struct dw_mipi_dsi *master; /* dual-dsi master ptr */ struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */ + struct drm_display_mode mode; const struct dw_mipi_dsi_plat_data *plat_data; }; @@ -332,6 +336,7 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, if (IS_ERR(bridge)) return PTR_ERR(bridge); + bridge->pre_enable_prev_first = true; dsi->panel_bridge = bridge; drm_bridge_add(&dsi->bridge); @@ -536,6 +541,59 @@ static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = { .transfer = dw_mipi_dsi_host_transfer, }; +static u32 * +dw_mipi_dsi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data; + u32 *input_fmts; + + if (pdata->get_input_bus_fmts) + return pdata->get_input_bus_fmts(pdata->priv_data, + bridge, bridge_state, + crtc_state, conn_state, + output_fmt, num_input_fmts); + + /* Fall back to MEDIA_BUS_FMT_FIXED as the only input format. */ + input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) + return NULL; + input_fmts[0] = MEDIA_BUS_FMT_FIXED; + *num_input_fmts = 1; + + return input_fmts; +} + +static int dw_mipi_dsi_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data; + bool ret; + + bridge_state->input_bus_cfg.flags = + DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE; + + if (pdata->mode_fixup) { + ret = pdata->mode_fixup(pdata->priv_data, &crtc_state->mode, + &crtc_state->adjusted_mode); + if (!ret) { + DRM_DEBUG_DRIVER("failed to fixup mode " DRM_MODE_FMT "\n", + DRM_MODE_ARG(&crtc_state->mode)); + return -EINVAL; + } + } + + return 0; +} + static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi) { u32 val; @@ -628,7 +686,7 @@ static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi) * timeout clock division should be computed with the * high speed transmission counter timeout and byte lane... */ - dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(10) | + dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVISION(0) | TX_ESC_CLK_DIVISION(esc_clk_division)); } @@ -691,7 +749,7 @@ static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi) * compute high speed transmission counter timeout according * to the timeout clock division (TO_CLK_DIVISION) and byte lane... */ - dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000)); + dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(0) | LPRX_TO_CNT(0)); /* * TODO dw drv improvements * the Bus-Turn-Around Timeout Counter should be computed @@ -701,20 +759,45 @@ static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi) dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE); } +static const u32 minimum_lbccs[] = {10, 5, 4, 3}; + +static inline u32 dw_mipi_dsi_get_minimum_lbcc(struct dw_mipi_dsi *dsi) +{ + return minimum_lbccs[dsi->lanes - 1]; +} + /* Get lane byte clock cycles. */ static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi, const struct drm_display_mode *mode, u32 hcomponent) { - u32 frac, lbcc; + u32 frac, lbcc, minimum_lbcc; + int bpp; - lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8; + if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { + /* lbcc based on lane_mbps */ + lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8; + } else { + /* lbcc based on pixel clock rate */ + bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + if (bpp < 0) { + dev_err(dsi->dev, "failed to get bpp\n"); + return 0; + } + + lbcc = div_u64((u64)hcomponent * mode->clock * bpp, dsi->lanes * 8); + } frac = lbcc % mode->clock; lbcc = lbcc / mode->clock; if (frac) lbcc++; + minimum_lbcc = dw_mipi_dsi_get_minimum_lbcc(dsi); + + if (lbcc < minimum_lbcc) + lbcc = minimum_lbcc; + return lbcc; } @@ -859,15 +942,6 @@ static void dw_mipi_dsi_bridge_post_atomic_disable(struct drm_bridge *bridge, */ dw_mipi_dsi_set_mode(dsi, 0); - /* - * TODO Only way found to call panel-bridge post_disable & - * panel unprepare before the dsi "final" disable... - * This needs to be fixed in the drm_bridge framework and the API - * needs to be updated to manage our own call chains... - */ - if (dsi->panel_bridge->funcs->post_disable) - dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge); - if (phy_ops->power_off) phy_ops->power_off(dsi->plat_data->priv_data); @@ -942,15 +1016,25 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi, phy_ops->power_on(dsi->plat_data->priv_data); } +static void dw_mipi_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + + /* Power up the dsi ctl into a command mode */ + dw_mipi_dsi_mode_set(dsi, &dsi->mode); + if (dsi->slave) + dw_mipi_dsi_mode_set(dsi->slave, &dsi->mode); +} + static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); - dw_mipi_dsi_mode_set(dsi, adjusted_mode); - if (dsi->slave) - dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode); + /* Store the display mode for later use in pre_enable callback */ + drm_mode_copy(&dsi->mode, adjusted_mode); } static void dw_mipi_dsi_bridge_atomic_enable(struct drm_bridge *bridge, @@ -1003,7 +1087,10 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge, static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = dw_mipi_dsi_bridge_atomic_get_input_bus_fmts, + .atomic_check = dw_mipi_dsi_bridge_atomic_check, .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_pre_enable = dw_mipi_dsi_bridge_atomic_pre_enable, .atomic_enable = dw_mipi_dsi_bridge_atomic_enable, .atomic_post_disable = dw_mipi_dsi_bridge_post_atomic_disable, .mode_set = dw_mipi_dsi_bridge_mode_set, @@ -1178,9 +1265,7 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, dsi->bridge.driver_private = dsi; dsi->bridge.funcs = &dw_mipi_dsi_bridge_funcs; -#ifdef CONFIG_OF dsi->bridge.of_node = pdev->dev.of_node; -#endif return dsi; } @@ -1207,6 +1292,12 @@ void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave) } EXPORT_SYMBOL_GPL(dw_mipi_dsi_set_slave); +struct drm_bridge *dw_mipi_dsi_get_bridge(struct dw_mipi_dsi *dsi) +{ + return &dsi->bridge; +} +EXPORT_SYMBOL_GPL(dw_mipi_dsi_get_bridge); + /* * Probe/remove API, used from platforms based on the DRM bridge API. */ |