Commit c1d4b38c authored by Laurent Pinchart's avatar Laurent Pinchart

drm: rcar-du: Perform initialization/cleanup at probe/remove time

The drm driver .load() operation is prone to race conditions as it
initializes the driver after registering the device nodes. Its usage is
deprecated, inline it in the probe function and call drm_dev_alloc() and
drm_dev_register() explicitly.

For consistency inline the .unload() handler in the remove function as
well.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 4c440050
...@@ -144,82 +144,6 @@ MODULE_DEVICE_TABLE(of, rcar_du_of_table); ...@@ -144,82 +144,6 @@ MODULE_DEVICE_TABLE(of, rcar_du_of_table);
* DRM operations * DRM operations
*/ */
static int rcar_du_unload(struct drm_device *dev)
{
struct rcar_du_device *rcdu = dev->dev_private;
if (rcdu->fbdev)
drm_fbdev_cma_fini(rcdu->fbdev);
drm_kms_helper_poll_fini(dev);
drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev);
dev->irq_enabled = 0;
dev->dev_private = NULL;
return 0;
}
static int rcar_du_load(struct drm_device *dev, unsigned long flags)
{
struct platform_device *pdev = dev->platformdev;
struct device_node *np = pdev->dev.of_node;
struct rcar_du_device *rcdu;
struct resource *mem;
int ret;
if (np == NULL) {
dev_err(dev->dev, "no platform data\n");
return -ENODEV;
}
rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
if (rcdu == NULL) {
dev_err(dev->dev, "failed to allocate private data\n");
return -ENOMEM;
}
init_waitqueue_head(&rcdu->commit.wait);
rcdu->dev = &pdev->dev;
rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
rcdu->ddev = dev;
dev->dev_private = rcdu;
/* I/O resources */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(rcdu->mmio))
return PTR_ERR(rcdu->mmio);
/* Initialize vertical blanking interrupts handling. Start with vblank
* disabled for all CRTCs.
*/
ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize vblank\n");
goto done;
}
/* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu);
if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret);
goto done;
}
dev->irq_enabled = 1;
platform_set_drvdata(pdev, rcdu);
done:
if (ret)
rcar_du_unload(dev);
return ret;
}
static void rcar_du_lastclose(struct drm_device *dev) static void rcar_du_lastclose(struct drm_device *dev)
{ {
struct rcar_du_device *rcdu = dev->dev_private; struct rcar_du_device *rcdu = dev->dev_private;
...@@ -260,10 +184,7 @@ static const struct file_operations rcar_du_fops = { ...@@ -260,10 +184,7 @@ static const struct file_operations rcar_du_fops = {
static struct drm_driver rcar_du_driver = { static struct drm_driver rcar_du_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME
| DRIVER_ATOMIC, | DRIVER_ATOMIC,
.load = rcar_du_load,
.unload = rcar_du_unload,
.lastclose = rcar_du_lastclose, .lastclose = rcar_du_lastclose,
.set_busid = drm_platform_set_busid,
.get_vblank_counter = drm_vblank_no_hw_counter, .get_vblank_counter = drm_vblank_no_hw_counter,
.enable_vblank = rcar_du_enable_vblank, .enable_vblank = rcar_du_enable_vblank,
.disable_vblank = rcar_du_disable_vblank, .disable_vblank = rcar_du_disable_vblank,
...@@ -323,18 +244,116 @@ static const struct dev_pm_ops rcar_du_pm_ops = { ...@@ -323,18 +244,116 @@ static const struct dev_pm_ops rcar_du_pm_ops = {
* Platform driver * Platform driver
*/ */
static int rcar_du_probe(struct platform_device *pdev) static int rcar_du_remove(struct platform_device *pdev)
{ {
return drm_platform_init(&rcar_du_driver, pdev); struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
struct drm_device *ddev = rcdu->ddev;
mutex_lock(&ddev->mode_config.mutex);
drm_connector_unplug_all(ddev);
mutex_unlock(&ddev->mode_config.mutex);
drm_dev_unregister(ddev);
if (rcdu->fbdev)
drm_fbdev_cma_fini(rcdu->fbdev);
drm_kms_helper_poll_fini(ddev);
drm_mode_config_cleanup(ddev);
drm_vblank_cleanup(ddev);
drm_dev_unref(ddev);
return 0;
} }
static int rcar_du_remove(struct platform_device *pdev) static int rcar_du_probe(struct platform_device *pdev)
{ {
struct rcar_du_device *rcdu = platform_get_drvdata(pdev); struct device_node *np = pdev->dev.of_node;
struct rcar_du_device *rcdu;
struct drm_connector *connector;
struct drm_device *ddev;
struct resource *mem;
int ret;
if (np == NULL) {
dev_err(&pdev->dev, "no device tree node\n");
return -ENODEV;
}
/* Allocate and initialize the DRM and R-Car device structures. */
rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
if (rcdu == NULL)
return -ENOMEM;
init_waitqueue_head(&rcdu->commit.wait);
rcdu->dev = &pdev->dev;
rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
if (!ddev)
return -ENOMEM;
drm_dev_set_unique(ddev, dev_name(&pdev->dev));
rcdu->ddev = ddev;
ddev->dev_private = rcdu;
drm_put_dev(rcdu->ddev); platform_set_drvdata(pdev, rcdu);
/* I/O resources */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(rcdu->mmio)) {
ret = PTR_ERR(rcdu->mmio);
goto error;
}
/* Initialize vertical blanking interrupts handling. Start with vblank
* disabled for all CRTCs.
*/
ret = drm_vblank_init(ddev, (1 << rcdu->info->num_crtcs) - 1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize vblank\n");
goto error;
}
/* DRM/KMS objects */
ret = rcar_du_modeset_init(rcdu);
if (ret < 0) {
dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret);
goto error;
}
ddev->irq_enabled = 1;
/* Register the DRM device with the core and the connectors with
* sysfs.
*/
ret = drm_dev_register(ddev, 0);
if (ret)
goto error;
mutex_lock(&ddev->mode_config.mutex);
drm_for_each_connector(connector, ddev) {
ret = drm_connector_register(connector);
if (ret < 0)
break;
}
mutex_unlock(&ddev->mode_config.mutex);
if (ret < 0)
goto error;
DRM_INFO("Device %s probed\n", dev_name(&pdev->dev));
return 0; return 0;
error:
rcar_du_remove(pdev);
return ret;
} }
static struct platform_driver rcar_du_platform_driver = { static struct platform_driver rcar_du_platform_driver = {
......
...@@ -55,12 +55,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { ...@@ -55,12 +55,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
.best_encoder = rcar_du_connector_best_encoder, .best_encoder = rcar_du_connector_best_encoder,
}; };
static void rcar_du_hdmi_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static enum drm_connector_status static enum drm_connector_status
rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force)
{ {
...@@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = { ...@@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = {
.reset = drm_atomic_helper_connector_reset, .reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_hdmi_connector_detect, .detect = rcar_du_hdmi_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rcar_du_hdmi_connector_destroy, .destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
...@@ -108,9 +102,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, ...@@ -108,9 +102,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
return ret; return ret;
drm_connector_helper_add(connector, &connector_helper_funcs); drm_connector_helper_add(connector, &connector_helper_funcs);
ret = drm_connector_register(connector);
if (ret < 0)
return ret;
connector->dpms = DRM_MODE_DPMS_OFF; connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base, drm_object_property_set_value(&connector->base,
......
...@@ -62,12 +62,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { ...@@ -62,12 +62,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
.best_encoder = rcar_du_connector_best_encoder, .best_encoder = rcar_du_connector_best_encoder,
}; };
static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static enum drm_connector_status static enum drm_connector_status
rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
{ {
...@@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = { ...@@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = {
.reset = drm_atomic_helper_connector_reset, .reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_lvds_connector_detect, .detect = rcar_du_lvds_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rcar_du_lvds_connector_destroy, .destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
...@@ -117,9 +111,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, ...@@ -117,9 +111,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
return ret; return ret;
drm_connector_helper_add(connector, &connector_helper_funcs); drm_connector_helper_add(connector, &connector_helper_funcs);
ret = drm_connector_register(connector);
if (ret < 0)
return ret;
connector->dpms = DRM_MODE_DPMS_OFF; connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base, drm_object_property_set_value(&connector->base,
......
...@@ -31,12 +31,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { ...@@ -31,12 +31,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = {
.best_encoder = rcar_du_connector_best_encoder, .best_encoder = rcar_du_connector_best_encoder,
}; };
static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static enum drm_connector_status static enum drm_connector_status
rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
{ {
...@@ -48,7 +42,7 @@ static const struct drm_connector_funcs connector_funcs = { ...@@ -48,7 +42,7 @@ static const struct drm_connector_funcs connector_funcs = {
.reset = drm_atomic_helper_connector_reset, .reset = drm_atomic_helper_connector_reset,
.detect = rcar_du_vga_connector_detect, .detect = rcar_du_vga_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rcar_du_vga_connector_destroy, .destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
...@@ -76,9 +70,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, ...@@ -76,9 +70,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
return ret; return ret;
drm_connector_helper_add(connector, &connector_helper_funcs); drm_connector_helper_add(connector, &connector_helper_funcs);
ret = drm_connector_register(connector);
if (ret < 0)
return ret;
connector->dpms = DRM_MODE_DPMS_OFF; connector->dpms = DRM_MODE_DPMS_OFF;
drm_object_property_set_value(&connector->base, drm_object_property_set_value(&connector->base,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment