mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-16 18:26:42 +00:00
9a865e4588
The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert the sti drm drivers from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Alain Volmat <alain.volmat@foss.st.com> Signed-off-by: Douglas Anderson <dianders@chromium.org> Link: https://patchwork.freedesktop.org/patch/msgid/20230507162616.1368908-42-u.kleine-koenig@pengutronix.de
278 lines
7.3 KiB
C
278 lines
7.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) STMicroelectronics SA 2014
|
|
* Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
|
|
* Fabien Dessenne <fabien.dessenne@st.com>
|
|
* for STMicroelectronics.
|
|
*/
|
|
|
|
#include <linux/component.h>
|
|
#include <linux/io.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/reset.h>
|
|
|
|
#include <drm/drm_device.h>
|
|
#include <drm/drm_print.h>
|
|
#include <drm/drm_vblank.h>
|
|
|
|
#include "sti_compositor.h"
|
|
#include "sti_crtc.h"
|
|
#include "sti_cursor.h"
|
|
#include "sti_drv.h"
|
|
#include "sti_gdp.h"
|
|
#include "sti_plane.h"
|
|
#include "sti_vid.h"
|
|
#include "sti_vtg.h"
|
|
|
|
/*
|
|
* stiH407 compositor properties
|
|
*/
|
|
static const struct sti_compositor_data stih407_compositor_data = {
|
|
.nb_subdev = 8,
|
|
.subdev_desc = {
|
|
{STI_CURSOR_SUBDEV, (int)STI_CURSOR, 0x000},
|
|
{STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
|
|
{STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
|
|
{STI_GPD_SUBDEV, (int)STI_GDP_2, 0x300},
|
|
{STI_GPD_SUBDEV, (int)STI_GDP_3, 0x400},
|
|
{STI_VID_SUBDEV, (int)STI_HQVDP_0, 0x700},
|
|
{STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00},
|
|
{STI_MIXER_AUX_SUBDEV, STI_MIXER_AUX, 0xD00},
|
|
},
|
|
};
|
|
|
|
void sti_compositor_debugfs_init(struct sti_compositor *compo,
|
|
struct drm_minor *minor)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < STI_MAX_VID; i++)
|
|
if (compo->vid[i])
|
|
vid_debugfs_init(compo->vid[i], minor);
|
|
|
|
for (i = 0; i < STI_MAX_MIXER; i++)
|
|
if (compo->mixer[i])
|
|
sti_mixer_debugfs_init(compo->mixer[i], minor);
|
|
}
|
|
|
|
static int sti_compositor_bind(struct device *dev,
|
|
struct device *master,
|
|
void *data)
|
|
{
|
|
struct sti_compositor *compo = dev_get_drvdata(dev);
|
|
struct drm_device *drm_dev = data;
|
|
unsigned int i, mixer_id = 0, vid_id = 0, crtc_id = 0;
|
|
struct sti_private *dev_priv = drm_dev->dev_private;
|
|
struct drm_plane *cursor = NULL;
|
|
struct drm_plane *primary = NULL;
|
|
struct sti_compositor_subdev_descriptor *desc = compo->data.subdev_desc;
|
|
unsigned int array_size = compo->data.nb_subdev;
|
|
|
|
dev_priv->compo = compo;
|
|
|
|
/* Register mixer subdev and video subdev first */
|
|
for (i = 0; i < array_size; i++) {
|
|
switch (desc[i].type) {
|
|
case STI_VID_SUBDEV:
|
|
compo->vid[vid_id++] =
|
|
sti_vid_create(compo->dev, drm_dev, desc[i].id,
|
|
compo->regs + desc[i].offset);
|
|
break;
|
|
case STI_MIXER_MAIN_SUBDEV:
|
|
case STI_MIXER_AUX_SUBDEV:
|
|
compo->mixer[mixer_id++] =
|
|
sti_mixer_create(compo->dev, drm_dev, desc[i].id,
|
|
compo->regs + desc[i].offset);
|
|
break;
|
|
case STI_GPD_SUBDEV:
|
|
case STI_CURSOR_SUBDEV:
|
|
/* Nothing to do, wait for the second round */
|
|
break;
|
|
default:
|
|
DRM_ERROR("Unknown subdev component type\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Register the other subdevs, create crtc and planes */
|
|
for (i = 0; i < array_size; i++) {
|
|
enum drm_plane_type plane_type = DRM_PLANE_TYPE_OVERLAY;
|
|
|
|
if (crtc_id < mixer_id)
|
|
plane_type = DRM_PLANE_TYPE_PRIMARY;
|
|
|
|
switch (desc[i].type) {
|
|
case STI_MIXER_MAIN_SUBDEV:
|
|
case STI_MIXER_AUX_SUBDEV:
|
|
case STI_VID_SUBDEV:
|
|
/* Nothing to do, already done at the first round */
|
|
break;
|
|
case STI_CURSOR_SUBDEV:
|
|
cursor = sti_cursor_create(drm_dev, compo->dev,
|
|
desc[i].id,
|
|
compo->regs + desc[i].offset,
|
|
1);
|
|
if (!cursor) {
|
|
DRM_ERROR("Can't create CURSOR plane\n");
|
|
break;
|
|
}
|
|
break;
|
|
case STI_GPD_SUBDEV:
|
|
primary = sti_gdp_create(drm_dev, compo->dev,
|
|
desc[i].id,
|
|
compo->regs + desc[i].offset,
|
|
(1 << mixer_id) - 1,
|
|
plane_type);
|
|
if (!primary) {
|
|
DRM_ERROR("Can't create GDP plane\n");
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
DRM_ERROR("Unknown subdev component type\n");
|
|
return 1;
|
|
}
|
|
|
|
/* The first planes are reserved for primary planes*/
|
|
if (crtc_id < mixer_id && primary) {
|
|
sti_crtc_init(drm_dev, compo->mixer[crtc_id],
|
|
primary, cursor);
|
|
crtc_id++;
|
|
cursor = NULL;
|
|
primary = NULL;
|
|
}
|
|
}
|
|
|
|
drm_vblank_init(drm_dev, crtc_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sti_compositor_unbind(struct device *dev, struct device *master,
|
|
void *data)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
static const struct component_ops sti_compositor_ops = {
|
|
.bind = sti_compositor_bind,
|
|
.unbind = sti_compositor_unbind,
|
|
};
|
|
|
|
static const struct of_device_id compositor_of_match[] = {
|
|
{
|
|
.compatible = "st,stih407-compositor",
|
|
.data = &stih407_compositor_data,
|
|
}, {
|
|
/* end node */
|
|
}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, compositor_of_match);
|
|
|
|
static int sti_compositor_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct device_node *vtg_np;
|
|
struct sti_compositor *compo;
|
|
struct resource *res;
|
|
unsigned int i;
|
|
|
|
compo = devm_kzalloc(dev, sizeof(*compo), GFP_KERNEL);
|
|
if (!compo) {
|
|
DRM_ERROR("Failed to allocate compositor context\n");
|
|
return -ENOMEM;
|
|
}
|
|
compo->dev = dev;
|
|
for (i = 0; i < STI_MAX_MIXER; i++)
|
|
compo->vtg_vblank_nb[i].notifier_call = sti_crtc_vblank_cb;
|
|
|
|
/* populate data structure depending on compatibility */
|
|
BUG_ON(!of_match_node(compositor_of_match, np)->data);
|
|
|
|
memcpy(&compo->data, of_match_node(compositor_of_match, np)->data,
|
|
sizeof(struct sti_compositor_data));
|
|
|
|
/* Get Memory ressources */
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (res == NULL) {
|
|
DRM_ERROR("Get memory resource failed\n");
|
|
return -ENXIO;
|
|
}
|
|
compo->regs = devm_ioremap(dev, res->start, resource_size(res));
|
|
if (compo->regs == NULL) {
|
|
DRM_ERROR("Register mapping failed\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* Get clock resources */
|
|
compo->clk_compo_main = devm_clk_get(dev, "compo_main");
|
|
if (IS_ERR(compo->clk_compo_main)) {
|
|
DRM_ERROR("Cannot get compo_main clock\n");
|
|
return PTR_ERR(compo->clk_compo_main);
|
|
}
|
|
|
|
compo->clk_compo_aux = devm_clk_get(dev, "compo_aux");
|
|
if (IS_ERR(compo->clk_compo_aux)) {
|
|
DRM_ERROR("Cannot get compo_aux clock\n");
|
|
return PTR_ERR(compo->clk_compo_aux);
|
|
}
|
|
|
|
compo->clk_pix_main = devm_clk_get(dev, "pix_main");
|
|
if (IS_ERR(compo->clk_pix_main)) {
|
|
DRM_ERROR("Cannot get pix_main clock\n");
|
|
return PTR_ERR(compo->clk_pix_main);
|
|
}
|
|
|
|
compo->clk_pix_aux = devm_clk_get(dev, "pix_aux");
|
|
if (IS_ERR(compo->clk_pix_aux)) {
|
|
DRM_ERROR("Cannot get pix_aux clock\n");
|
|
return PTR_ERR(compo->clk_pix_aux);
|
|
}
|
|
|
|
/* Get reset resources */
|
|
compo->rst_main = devm_reset_control_get_shared(dev, "compo-main");
|
|
/* Take compo main out of reset */
|
|
if (!IS_ERR(compo->rst_main))
|
|
reset_control_deassert(compo->rst_main);
|
|
|
|
compo->rst_aux = devm_reset_control_get_shared(dev, "compo-aux");
|
|
/* Take compo aux out of reset */
|
|
if (!IS_ERR(compo->rst_aux))
|
|
reset_control_deassert(compo->rst_aux);
|
|
|
|
vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0);
|
|
if (vtg_np)
|
|
compo->vtg[STI_MIXER_MAIN] = of_vtg_find(vtg_np);
|
|
of_node_put(vtg_np);
|
|
|
|
vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 1);
|
|
if (vtg_np)
|
|
compo->vtg[STI_MIXER_AUX] = of_vtg_find(vtg_np);
|
|
of_node_put(vtg_np);
|
|
|
|
platform_set_drvdata(pdev, compo);
|
|
|
|
return component_add(&pdev->dev, &sti_compositor_ops);
|
|
}
|
|
|
|
static void sti_compositor_remove(struct platform_device *pdev)
|
|
{
|
|
component_del(&pdev->dev, &sti_compositor_ops);
|
|
}
|
|
|
|
struct platform_driver sti_compositor_driver = {
|
|
.driver = {
|
|
.name = "sti-compositor",
|
|
.of_match_table = compositor_of_match,
|
|
},
|
|
.probe = sti_compositor_probe,
|
|
.remove_new = sti_compositor_remove,
|
|
};
|
|
|
|
MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
|
|
MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
|
|
MODULE_LICENSE("GPL");
|