|
@@ -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;
|
|
|
|
|
@@ -866,7 +866,8 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
|
|
|
adjusted_mode->clock = pixel_clock_hz / 1000 + 1;
|
|
|
|
|
|
/* 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;
|
|
|
|
|
@@ -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;
|
|
|
}
|
|
|
|