diff options
Diffstat (limited to 'drivers/gpu/drm/vc4/vc4_dsi.c')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_dsi.c | 112 |
1 files changed, 65 insertions, 47 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index d1e0dc908048..554605af344e 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -33,6 +33,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <linux/clk.h> #include <linux/clk-provider.h> @@ -504,7 +505,6 @@ struct vc4_dsi { struct mipi_dsi_host dsi_host; struct drm_encoder *encoder; struct drm_bridge *bridge; - bool is_panel_bridge; void __iomem *regs; @@ -859,14 +859,11 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, pll_clock = parent_rate / divider; pixel_clock_hz = pll_clock / dsi->divider; - /* Round up the clk_set_rate() request slightly, since - * PLLD_DSI1 is an integer divider and its rate selection will - * never round up. - */ - adjusted_mode->clock = pixel_clock_hz / 1000 + 1; + adjusted_mode->clock = pixel_clock_hz / 1000; /* Given the new pixel clock, adjust HFP to keep vrefresh the same. */ - adjusted_mode->htotal = pixel_clock_hz / (mode->vrefresh * mode->vtotal); + adjusted_mode->htotal = adjusted_mode->clock * mode->htotal / + mode->clock; adjusted_mode->hsync_end += adjusted_mode->htotal - mode->htotal; adjusted_mode->hsync_start += adjusted_mode->htotal - mode->htotal; @@ -900,7 +897,11 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) vc4_dsi_dump_regs(dsi); } - phy_clock = pixel_clock_hz * dsi->divider; + /* Round up the clk_set_rate() request slightly, since + * PLLD_DSI1 is an integer divider and its rate selection will + * never round up. + */ + phy_clock = (pixel_clock_hz + 1000) * dsi->divider; ret = clk_set_rate(dsi->pll_phy_clock, phy_clock); if (ret) { dev_err(&dsi->pdev->dev, @@ -1288,7 +1289,6 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { struct vc4_dsi *dsi = host_to_dsi(host); - int ret = 0; dsi->lanes = device->lanes; dsi->channel = device->channel; @@ -1323,34 +1323,12 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host, return 0; } - dsi->bridge = of_drm_find_bridge(device->dev.of_node); - if (!dsi->bridge) { - struct drm_panel *panel = - of_drm_find_panel(device->dev.of_node); - - dsi->bridge = drm_panel_bridge_add(panel, - DRM_MODE_CONNECTOR_DSI); - if (IS_ERR(dsi->bridge)) { - ret = PTR_ERR(dsi->bridge); - dsi->bridge = NULL; - return ret; - } - dsi->is_panel_bridge = true; - } - - return drm_bridge_attach(dsi->encoder, dsi->bridge, NULL); + return 0; } static int vc4_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *device) { - struct vc4_dsi *dsi = host_to_dsi(host); - - if (dsi->is_panel_bridge) { - drm_panel_bridge_remove(dsi->bridge); - dsi->bridge = NULL; - } - return 0; } @@ -1492,16 +1470,13 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); - struct vc4_dsi *dsi; + struct vc4_dsi *dsi = dev_get_drvdata(dev); struct vc4_dsi_encoder *vc4_dsi_encoder; + struct drm_panel *panel; const struct of_device_id *match; dma_cap_mask_t dma_mask; int ret; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); - if (!dsi) - return -ENOMEM; - match = of_match_device(vc4_dsi_dt_match, dev); if (!match) return -ENODEV; @@ -1516,7 +1491,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) vc4_dsi_encoder->dsi = dsi; dsi->encoder = &vc4_dsi_encoder->base.base; - dsi->pdev = pdev; dsi->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(dsi->regs)) return PTR_ERR(dsi->regs); @@ -1597,6 +1571,18 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) return ret; } + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, + &panel, &dsi->bridge); + if (ret) + return ret; + + if (panel) { + dsi->bridge = devm_drm_panel_bridge_add(dev, panel, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(dsi->bridge)) + return PTR_ERR(dsi->bridge); + } + /* The esc clock rate is supposed to always be 100Mhz. */ ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); if (ret) { @@ -1615,12 +1601,11 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) DRM_MODE_ENCODER_DSI, NULL); drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); - dsi->dsi_host.ops = &vc4_dsi_host_ops; - dsi->dsi_host.dev = dev; - - mipi_dsi_host_register(&dsi->dsi_host); - - dev_set_drvdata(dev, dsi); + ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL); + if (ret) { + dev_err(dev, "bridge attach failed: %d\n", ret); + return ret; + } pm_runtime_enable(dev); @@ -1638,8 +1623,6 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, vc4_dsi_encoder_destroy(dsi->encoder); - mipi_dsi_host_unregister(&dsi->dsi_host); - if (dsi->port == 1) vc4->dsi1 = NULL; } @@ -1651,12 +1634,47 @@ static const struct component_ops vc4_dsi_ops = { static int vc4_dsi_dev_probe(struct platform_device *pdev) { - return component_add(&pdev->dev, &vc4_dsi_ops); + struct device *dev = &pdev->dev; + struct vc4_dsi *dsi; + int ret; + + dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + dev_set_drvdata(dev, dsi); + + dsi->pdev = pdev; + + /* Note, the initialization sequence for DSI and panels is + * tricky. The component bind above won't get past its + * -EPROBE_DEFER until the panel/bridge probes. The + * panel/bridge will return -EPROBE_DEFER until it has a + * mipi_dsi_host to register its device to. So, we register + * the host during pdev probe time, so vc4 as a whole can then + * -EPROBE_DEFER its component bind process until the panel + * successfully attaches. + */ + dsi->dsi_host.ops = &vc4_dsi_host_ops; + dsi->dsi_host.dev = dev; + mipi_dsi_host_register(&dsi->dsi_host); + + ret = component_add(&pdev->dev, &vc4_dsi_ops); + if (ret) { + mipi_dsi_host_unregister(&dsi->dsi_host); + return ret; + } + + return 0; } static int vc4_dsi_dev_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct vc4_dsi *dsi = dev_get_drvdata(dev); + component_del(&pdev->dev, &vc4_dsi_ops); + mipi_dsi_host_unregister(&dsi->dsi_host); + return 0; } |