Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df5225bc authored by Inki Dae's avatar Inki Dae
Browse files

drm/exynos: consider deferred probe case



This patch makes sure that exynos drm framework handles deferred
probe case correctly.

Sub drivers could be probed before resources, clock, regulator,
phy or panel, are ready for them so we should make sure that exynos
drm core waits until all resources are ready and sub drivers are
probed correctly.

Chagelog v2:
- Make sure that exynos drm core tries to bind sub drivers only in case that
  they have a pair: crtc and encoder/connector components should be a pair.
- Remove unnecessary patch:
  drm/exynos: mipi-dsi: consider panel driver-deferred probe
- Return error type correctly.

Signed-off-by: default avatarInki Dae <inki.dae@samsung.com>
Acked-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
parent 2e1ce1a1
Loading
Loading
Loading
Loading
+16 −2
Original line number Original line Diff line number Diff line
@@ -1325,12 +1325,26 @@ static const struct component_ops exynos_dp_ops = {


static int exynos_dp_probe(struct platform_device *pdev)
static int exynos_dp_probe(struct platform_device *pdev)
{
{
	return exynos_drm_component_add(&pdev->dev, &exynos_dp_ops);
	int ret;

	ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
					exynos_dp_display.type);
	if (ret)
		return ret;

	ret = component_add(&pdev->dev, &exynos_dp_ops);
	if (ret)
		exynos_drm_component_del(&pdev->dev,
						EXYNOS_DEVICE_TYPE_CONNECTOR);

	return ret;
}
}


static int exynos_dp_remove(struct platform_device *pdev)
static int exynos_dp_remove(struct platform_device *pdev)
{
{
	exynos_drm_component_del(&pdev->dev, &exynos_dp_ops);
	component_del(&pdev->dev, &exynos_dp_ops);
	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);

	return 0;
	return 0;
}
}


+19 −3
Original line number Original line Diff line number Diff line
@@ -295,9 +295,15 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
	struct exynos_dpi *ctx;
	struct exynos_dpi *ctx;
	int ret;
	int ret;


	ret = exynos_drm_component_add(dev,
					EXYNOS_DEVICE_TYPE_CONNECTOR,
					exynos_dpi_display.type);
	if (ret)
		return ERR_PTR(ret);

	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
	if (!ctx)
		return NULL;
		goto err_del_component;


	ctx->dev = dev;
	ctx->dev = dev;
	exynos_dpi_display.ctx = ctx;
	exynos_dpi_display.ctx = ctx;
@@ -306,16 +312,24 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
	ret = exynos_dpi_parse_dt(ctx);
	ret = exynos_dpi_parse_dt(ctx);
	if (ret < 0) {
	if (ret < 0) {
		devm_kfree(dev, ctx);
		devm_kfree(dev, ctx);
		return NULL;
		goto err_del_component;
	}
	}


	if (ctx->panel_node) {
	if (ctx->panel_node) {
		ctx->panel = of_drm_find_panel(ctx->panel_node);
		ctx->panel = of_drm_find_panel(ctx->panel_node);
		if (!ctx->panel)
		if (!ctx->panel) {
			exynos_drm_component_del(dev,
						EXYNOS_DEVICE_TYPE_CONNECTOR);
			return ERR_PTR(-EPROBE_DEFER);
			return ERR_PTR(-EPROBE_DEFER);
		}
		}
	}


	return &exynos_dpi_display;
	return &exynos_dpi_display;

err_del_component:
	exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);

	return NULL;
}
}


int exynos_dpi_remove(struct device *dev)
int exynos_dpi_remove(struct device *dev)
@@ -327,5 +341,7 @@ int exynos_dpi_remove(struct device *dev)
	encoder->funcs->destroy(encoder);
	encoder->funcs->destroy(encoder);
	drm_connector_cleanup(&ctx->connector);
	drm_connector_cleanup(&ctx->connector);


	exynos_drm_component_del(dev, EXYNOS_DEVICE_TYPE_CONNECTOR);

	return 0;
	return 0;
}
}
+116 −22
Original line number Original line Diff line number Diff line
@@ -48,7 +48,10 @@ static LIST_HEAD(drm_component_list);


struct component_dev {
struct component_dev {
	struct list_head list;
	struct list_head list;
	struct device *dev;
	struct device *crtc_dev;
	struct device *conn_dev;
	enum exynos_drm_output_type out_type;
	unsigned int dev_type_flag;
};
};


static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
@@ -382,22 +385,65 @@ static const struct dev_pm_ops exynos_drm_pm_ops = {
};
};


int exynos_drm_component_add(struct device *dev,
int exynos_drm_component_add(struct device *dev,
				const struct component_ops *ops)
				enum exynos_drm_device_type dev_type,
				enum exynos_drm_output_type out_type)
{
{
	struct component_dev *cdev;
	struct component_dev *cdev;
	int ret;

	if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
			dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
		DRM_ERROR("invalid device type.\n");
		return -EINVAL;
	}

	mutex_lock(&drm_component_lock);

	/*
	 * Make sure to check if there is a component which has two device
	 * objects, for connector and for encoder/connector.
	 * It should make sure that crtc and encoder/connector drivers are
	 * ready before exynos drm core binds them.
	 */
	list_for_each_entry(cdev, &drm_component_list, list) {
		if (cdev->out_type == out_type) {
			/*
			 * If crtc and encoder/connector device objects are
			 * added already just return.
			 */
			if (cdev->dev_type_flag == (EXYNOS_DEVICE_TYPE_CRTC |
						EXYNOS_DEVICE_TYPE_CONNECTOR)) {
				mutex_unlock(&drm_component_lock);
				return 0;
			}

			if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
				cdev->crtc_dev = dev;
				cdev->dev_type_flag |= dev_type;
			}

			if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
				cdev->conn_dev = dev;
				cdev->dev_type_flag |= dev_type;
			}

			mutex_unlock(&drm_component_lock);
			return 0;
		}
	}

	mutex_unlock(&drm_component_lock);


	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
	if (!cdev)
	if (!cdev)
		return -ENOMEM;
		return -ENOMEM;


	ret = component_add(dev, ops);
	if (dev_type == EXYNOS_DEVICE_TYPE_CRTC)
	if (ret) {
		cdev->crtc_dev = dev;
		kfree(cdev);
	if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR)
		return ret;
		cdev->conn_dev = dev;
	}


	cdev->dev = dev;
	cdev->out_type = out_type;
	cdev->dev_type_flag = dev_type;


	mutex_lock(&drm_component_lock);
	mutex_lock(&drm_component_lock);
	list_add_tail(&cdev->list, &drm_component_list);
	list_add_tail(&cdev->list, &drm_component_list);
@@ -407,23 +453,40 @@ int exynos_drm_component_add(struct device *dev,
}
}


void exynos_drm_component_del(struct device *dev,
void exynos_drm_component_del(struct device *dev,
				const struct component_ops *ops)
				enum exynos_drm_device_type dev_type)
{
{
	struct component_dev *cdev, *next;
	struct component_dev *cdev, *next;


	mutex_lock(&drm_component_lock);
	mutex_lock(&drm_component_lock);


	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
	list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
		if (dev == cdev->dev) {
		if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
			if (cdev->crtc_dev == dev) {
				cdev->crtc_dev = NULL;
				cdev->dev_type_flag &= ~dev_type;
			}
		}

		if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
			if (cdev->conn_dev == dev) {
				cdev->conn_dev = NULL;
				cdev->dev_type_flag &= ~dev_type;
			}
		}

		/*
		 * Release cdev object only in case that both of crtc and
		 * encoder/connector device objects are NULL.
		 */
		if (!cdev->crtc_dev && !cdev->conn_dev) {
			list_del(&cdev->list);
			list_del(&cdev->list);
			kfree(cdev);
			kfree(cdev);
			break;
		}
		}

		break;
	}
	}


	mutex_unlock(&drm_component_lock);
	mutex_unlock(&drm_component_lock);

	component_del(dev, ops);
}
}


static int compare_of(struct device *dev, void *data)
static int compare_of(struct device *dev, void *data)
@@ -433,29 +496,60 @@ static int compare_of(struct device *dev, void *data)


static int exynos_drm_add_components(struct device *dev, struct master *m)
static int exynos_drm_add_components(struct device *dev, struct master *m)
{
{
	unsigned int attached_cnt = 0;
	struct component_dev *cdev;
	struct component_dev *cdev;
	unsigned int attach_cnt = 0;


	mutex_lock(&drm_component_lock);
	mutex_lock(&drm_component_lock);


	list_for_each_entry(cdev, &drm_component_list, list) {
	list_for_each_entry(cdev, &drm_component_list, list) {
		int ret;
		int ret;


		/*
		 * Add components to master only in case that crtc and
		 * encoder/connector device objects exist.
		 */
		if (!cdev->crtc_dev || !cdev->conn_dev)
			continue;

		attach_cnt++;

		mutex_unlock(&drm_component_lock);
		mutex_unlock(&drm_component_lock);


		ret = component_master_add_child(m, compare_of, cdev->dev);
		/*
		if (!ret)
		 * fimd and dpi modules have same device object so add
			attached_cnt++;
		 * only crtc device object in this case.
		 *
		 * TODO. if dpi module follows driver-model driver then
		 * below codes can be removed.
		 */
		if (cdev->crtc_dev == cdev->conn_dev) {
			ret = component_master_add_child(m, compare_of,
					cdev->crtc_dev);
			if (ret < 0)
				return ret;

			goto out_lock;
		}


		/*
		 * Do not chage below call order.
		 * crtc device first should be added to master because
		 * connector/encoder need pipe number of crtc when they
		 * are created.
		 */
		ret = component_master_add_child(m, compare_of, cdev->crtc_dev);
		ret |= component_master_add_child(m, compare_of,
							cdev->conn_dev);
		if (ret < 0)
			return ret;

out_lock:
		mutex_lock(&drm_component_lock);
		mutex_lock(&drm_component_lock);
	}
	}


	mutex_unlock(&drm_component_lock);
	mutex_unlock(&drm_component_lock);


	if (!attached_cnt)
	return attach_cnt ? 0 : -ENODEV;
		return -ENXIO;

	return 0;
}
}


static int exynos_drm_bind(struct device *dev)
static int exynos_drm_bind(struct device *dev)
+10 −3
Original line number Original line Diff line number Diff line
@@ -42,6 +42,13 @@ struct drm_connector;


extern unsigned int drm_vblank_offdelay;
extern unsigned int drm_vblank_offdelay;


/* This enumerates device type. */
enum exynos_drm_device_type {
	EXYNOS_DEVICE_TYPE_NONE,
	EXYNOS_DEVICE_TYPE_CRTC,
	EXYNOS_DEVICE_TYPE_CONNECTOR,
};

/* this enumerates display type. */
/* this enumerates display type. */
enum exynos_drm_output_type {
enum exynos_drm_output_type {
	EXYNOS_DISPLAY_TYPE_NONE,
	EXYNOS_DISPLAY_TYPE_NONE,
@@ -354,12 +361,12 @@ void exynos_drm_remove_vidi(void);
int exynos_drm_create_enc_conn(struct drm_device *dev,
int exynos_drm_create_enc_conn(struct drm_device *dev,
				struct exynos_drm_display *display);
				struct exynos_drm_display *display);


struct component_ops;
int exynos_drm_component_add(struct device *dev,
int exynos_drm_component_add(struct device *dev,
				const struct component_ops *ops);
				enum exynos_drm_device_type dev_type,
				enum exynos_drm_output_type out_type);


void exynos_drm_component_del(struct device *dev,
void exynos_drm_component_del(struct device *dev,
				const struct component_ops *ops);
				enum exynos_drm_device_type dev_type);


extern struct platform_driver fimd_driver;
extern struct platform_driver fimd_driver;
extern struct platform_driver dp_driver;
extern struct platform_driver dp_driver;
+31 −10
Original line number Original line Diff line number Diff line
@@ -1423,10 +1423,16 @@ static int exynos_dsi_probe(struct platform_device *pdev)
	struct exynos_dsi *dsi;
	struct exynos_dsi *dsi;
	int ret;
	int ret;


	ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
					exynos_dsi_display.type);
	if (ret)
		return ret;

	dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
	dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
	if (!dsi) {
	if (!dsi) {
		dev_err(&pdev->dev, "failed to allocate dsi object.\n");
		dev_err(&pdev->dev, "failed to allocate dsi object.\n");
		return -ENOMEM;
		ret = -ENOMEM;
		goto err_del_component;
	}
	}


	init_completion(&dsi->completed);
	init_completion(&dsi->completed);
@@ -1440,7 +1446,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)


	ret = exynos_dsi_parse_dt(dsi);
	ret = exynos_dsi_parse_dt(dsi);
	if (ret)
	if (ret)
		return ret;
		goto err_del_component;


	dsi->supplies[0].supply = "vddcore";
	dsi->supplies[0].supply = "vddcore";
	dsi->supplies[1].supply = "vddio";
	dsi->supplies[1].supply = "vddio";
@@ -1454,32 +1460,37 @@ static int exynos_dsi_probe(struct platform_device *pdev)
	dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
	dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
	if (IS_ERR(dsi->pll_clk)) {
	if (IS_ERR(dsi->pll_clk)) {
		dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
		dev_info(&pdev->dev, "failed to get dsi pll input clock\n");
		return -EPROBE_DEFER;
		ret = PTR_ERR(dsi->pll_clk);
		goto err_del_component;
	}
	}


	dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
	dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
	if (IS_ERR(dsi->bus_clk)) {
	if (IS_ERR(dsi->bus_clk)) {
		dev_info(&pdev->dev, "failed to get dsi bus clock\n");
		dev_info(&pdev->dev, "failed to get dsi bus clock\n");
		return -EPROBE_DEFER;
		ret = PTR_ERR(dsi->bus_clk);
		goto err_del_component;
	}
	}


	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
	dsi->reg_base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(dsi->reg_base)) {
	if (IS_ERR(dsi->reg_base)) {
		dev_err(&pdev->dev, "failed to remap io region\n");
		dev_err(&pdev->dev, "failed to remap io region\n");
		return PTR_ERR(dsi->reg_base);
		ret = PTR_ERR(dsi->reg_base);
		goto err_del_component;
	}
	}


	dsi->phy = devm_phy_get(&pdev->dev, "dsim");
	dsi->phy = devm_phy_get(&pdev->dev, "dsim");
	if (IS_ERR(dsi->phy)) {
	if (IS_ERR(dsi->phy)) {
		dev_info(&pdev->dev, "failed to get dsim phy\n");
		dev_info(&pdev->dev, "failed to get dsim phy\n");
		return -EPROBE_DEFER;
		ret = PTR_ERR(dsi->phy);
		goto err_del_component;
	}
	}


	dsi->irq = platform_get_irq(pdev, 0);
	dsi->irq = platform_get_irq(pdev, 0);
	if (dsi->irq < 0) {
	if (dsi->irq < 0) {
		dev_err(&pdev->dev, "failed to request dsi irq resource\n");
		dev_err(&pdev->dev, "failed to request dsi irq resource\n");
		return dsi->irq;
		ret = dsi->irq;
		goto err_del_component;
	}
	}


	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
@@ -1488,19 +1499,29 @@ static int exynos_dsi_probe(struct platform_device *pdev)
					dev_name(&pdev->dev), dsi);
					dev_name(&pdev->dev), dsi);
	if (ret) {
	if (ret) {
		dev_err(&pdev->dev, "failed to request dsi irq\n");
		dev_err(&pdev->dev, "failed to request dsi irq\n");
		return ret;
		goto err_del_component;
	}
	}


	exynos_dsi_display.ctx = dsi;
	exynos_dsi_display.ctx = dsi;


	platform_set_drvdata(pdev, &exynos_dsi_display);
	platform_set_drvdata(pdev, &exynos_dsi_display);


	return exynos_drm_component_add(&pdev->dev, &exynos_dsi_component_ops);
	ret = component_add(&pdev->dev, &exynos_dsi_component_ops);
	if (ret)
		goto err_del_component;

	return ret;

err_del_component:
	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
	return ret;
}
}


static int exynos_dsi_remove(struct platform_device *pdev)
static int exynos_dsi_remove(struct platform_device *pdev)
{
{
	exynos_drm_component_del(&pdev->dev, &exynos_dsi_component_ops);
	component_del(&pdev->dev, &exynos_dsi_component_ops);
	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);

	return 0;
	return 0;
}
}


Loading