Commit fd7226fb authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/tegra/for-5.6-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/tegra: Changes for v5.6-rc1

This contains a small set of mostly fixes and some minor improvements.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200111004835.2412858-1-thierry.reding@gmail.com
parents 688486a4 033ccdb7
......@@ -1727,6 +1727,7 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
{
struct tegra_dc *dc = to_tegra_dc(crtc);
u32 value;
int err;
if (!tegra_dc_idle(dc)) {
tegra_dc_stop(dc);
......@@ -1773,7 +1774,9 @@ static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
spin_unlock_irq(&crtc->dev->event_lock);
pm_runtime_put_sync(dc->dev);
err = host1x_client_suspend(&dc->client);
if (err < 0)
dev_err(dc->dev, "failed to suspend: %d\n", err);
}
static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
......@@ -1783,8 +1786,13 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
struct tegra_dc_state *state = to_dc_state(crtc->state);
struct tegra_dc *dc = to_tegra_dc(crtc);
u32 value;
int err;
pm_runtime_get_sync(dc->dev);
err = host1x_client_resume(&dc->client);
if (err < 0) {
dev_err(dc->dev, "failed to resume: %d\n", err);
return;
}
/* initialize display controller */
if (dc->syncpt) {
......@@ -1996,7 +2004,7 @@ static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
static int tegra_dc_init(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->parent);
struct drm_device *drm = dev_get_drvdata(client->host);
unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
struct tegra_dc *dc = host1x_client_to_dc(client);
struct tegra_drm *tegra = drm->dev_private;
......@@ -2012,6 +2020,15 @@ static int tegra_dc_init(struct host1x_client *client)
if (!tegra_dc_has_window_groups(dc))
return 0;
/*
* Set the display hub as the host1x client parent for the display
* controller. This is needed for the runtime reference counting that
* ensures the display hub is always powered when any of the display
* controllers are.
*/
if (dc->soc->has_nvdisplay)
client->parent = &tegra->hub->client;
dc->syncpt = host1x_syncpt_request(client, flags);
if (!dc->syncpt)
dev_warn(dc->dev, "failed to allocate syncpoint\n");
......@@ -2077,9 +2094,9 @@ static int tegra_dc_init(struct host1x_client *client)
/*
* Inherit the DMA parameters (such as maximum segment size) from the
* parent device.
* parent host1x device.
*/
client->dev->dma_parms = client->parent->dma_parms;
client->dev->dma_parms = client->host->dma_parms;
return 0;
......@@ -2121,9 +2138,74 @@ static int tegra_dc_exit(struct host1x_client *client)
return 0;
}
static int tegra_dc_runtime_suspend(struct host1x_client *client)
{
struct tegra_dc *dc = host1x_client_to_dc(client);
struct device *dev = client->dev;
int err;
err = reset_control_assert(dc->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
if (dc->soc->has_powergate)
tegra_powergate_power_off(dc->powergate);
clk_disable_unprepare(dc->clk);
pm_runtime_put_sync(dev);
return 0;
}
static int tegra_dc_runtime_resume(struct host1x_client *client)
{
struct tegra_dc *dc = host1x_client_to_dc(client);
struct device *dev = client->dev;
int err;
err = pm_runtime_get_sync(dev);
if (err < 0) {
dev_err(dev, "failed to get runtime PM: %d\n", err);
return err;
}
if (dc->soc->has_powergate) {
err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
dc->rst);
if (err < 0) {
dev_err(dev, "failed to power partition: %d\n", err);
goto put_rpm;
}
} else {
err = clk_prepare_enable(dc->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
goto put_rpm;
}
err = reset_control_deassert(dc->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
goto disable_clk;
}
}
return 0;
disable_clk:
clk_disable_unprepare(dc->clk);
put_rpm:
pm_runtime_put_sync(dev);
return err;
}
static const struct host1x_client_ops dc_client_ops = {
.init = tegra_dc_init,
.exit = tegra_dc_exit,
.suspend = tegra_dc_runtime_suspend,
.resume = tegra_dc_runtime_resume,
};
static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
......@@ -2535,65 +2617,10 @@ static int tegra_dc_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int tegra_dc_suspend(struct device *dev)
{
struct tegra_dc *dc = dev_get_drvdata(dev);
int err;
err = reset_control_assert(dc->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
if (dc->soc->has_powergate)
tegra_powergate_power_off(dc->powergate);
clk_disable_unprepare(dc->clk);
return 0;
}
static int tegra_dc_resume(struct device *dev)
{
struct tegra_dc *dc = dev_get_drvdata(dev);
int err;
if (dc->soc->has_powergate) {
err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
dc->rst);
if (err < 0) {
dev_err(dev, "failed to power partition: %d\n", err);
return err;
}
} else {
err = clk_prepare_enable(dc->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
return err;
}
err = reset_control_deassert(dc->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
return err;
}
}
return 0;
}
#endif
static const struct dev_pm_ops tegra_dc_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL)
};
struct platform_driver tegra_dc_driver = {
.driver = {
.name = "tegra-dc",
.of_match_table = tegra_dc_of_match,
.pm = &tegra_dc_pm_ops,
},
.probe = tegra_dc_probe,
.remove = tegra_dc_remove,
......
......@@ -588,7 +588,7 @@ static int tegra_dpaux_remove(struct platform_device *pdev)
/* make sure pads are powered down when not in use */
tegra_dpaux_pad_power_down(dpaux);
pm_runtime_put(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
drm_dp_aux_unregister(&dpaux->aux);
......
......@@ -905,7 +905,7 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra,
int host1x_client_iommu_attach(struct host1x_client *client)
{
struct iommu_domain *domain = iommu_get_domain_for_dev(client->dev);
struct drm_device *drm = dev_get_drvdata(client->parent);
struct drm_device *drm = dev_get_drvdata(client->host);
struct tegra_drm *tegra = drm->dev_private;
struct iommu_group *group = NULL;
int err;
......@@ -941,7 +941,7 @@ int host1x_client_iommu_attach(struct host1x_client *client)
void host1x_client_iommu_detach(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->parent);
struct drm_device *drm = dev_get_drvdata(client->host);
struct tegra_drm *tegra = drm->dev_private;
struct iommu_domain *domain;
......
......@@ -144,6 +144,8 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
void tegra_output_exit(struct tegra_output *output);
void tegra_output_find_possible_crtcs(struct tegra_output *output,
struct drm_device *drm);
int tegra_output_suspend(struct tegra_output *output);
int tegra_output_resume(struct tegra_output *output);
int tegra_output_connector_get_modes(struct drm_connector *connector);
enum drm_connector_status
......
......@@ -840,7 +840,9 @@ static void tegra_dsi_unprepare(struct tegra_dsi *dsi)
dev_err(dsi->dev, "failed to disable MIPI calibration: %d\n",
err);
pm_runtime_put(dsi->dev);
err = host1x_client_suspend(&dsi->client);
if (err < 0)
dev_err(dsi->dev, "failed to suspend: %d\n", err);
}
static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
......@@ -882,11 +884,15 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
tegra_dsi_unprepare(dsi);
}
static void tegra_dsi_prepare(struct tegra_dsi *dsi)
static int tegra_dsi_prepare(struct tegra_dsi *dsi)
{
int err;
pm_runtime_get_sync(dsi->dev);
err = host1x_client_resume(&dsi->client);
if (err < 0) {
dev_err(dsi->dev, "failed to resume: %d\n", err);
return err;
}
err = tegra_mipi_enable(dsi->mipi);
if (err < 0)
......@@ -899,6 +905,8 @@ static void tegra_dsi_prepare(struct tegra_dsi *dsi)
if (dsi->slave)
tegra_dsi_prepare(dsi->slave);
return 0;
}
static void tegra_dsi_encoder_enable(struct drm_encoder *encoder)
......@@ -909,8 +917,13 @@ static void tegra_dsi_encoder_enable(struct drm_encoder *encoder)
struct tegra_dsi *dsi = to_dsi(output);
struct tegra_dsi_state *state;
u32 value;
int err;
tegra_dsi_prepare(dsi);
err = tegra_dsi_prepare(dsi);
if (err < 0) {
dev_err(dsi->dev, "failed to prepare: %d\n", err);
return;
}
state = tegra_dsi_get_state(dsi);
......@@ -1030,7 +1043,7 @@ static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
static int tegra_dsi_init(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->parent);
struct drm_device *drm = dev_get_drvdata(client->host);
struct tegra_dsi *dsi = host1x_client_to_dsi(client);
int err;
......@@ -1075,9 +1088,89 @@ static int tegra_dsi_exit(struct host1x_client *client)
return 0;
}
static int tegra_dsi_runtime_suspend(struct host1x_client *client)
{
struct tegra_dsi *dsi = host1x_client_to_dsi(client);
struct device *dev = client->dev;
int err;
if (dsi->rst) {
err = reset_control_assert(dsi->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
}
usleep_range(1000, 2000);
clk_disable_unprepare(dsi->clk_lp);
clk_disable_unprepare(dsi->clk);
regulator_disable(dsi->vdd);
pm_runtime_put_sync(dev);
return 0;
}
static int tegra_dsi_runtime_resume(struct host1x_client *client)
{
struct tegra_dsi *dsi = host1x_client_to_dsi(client);
struct device *dev = client->dev;
int err;
err = pm_runtime_get_sync(dev);
if (err < 0) {
dev_err(dev, "failed to get runtime PM: %d\n", err);
return err;
}
err = regulator_enable(dsi->vdd);
if (err < 0) {
dev_err(dev, "failed to enable VDD supply: %d\n", err);
goto put_rpm;
}
err = clk_prepare_enable(dsi->clk);
if (err < 0) {
dev_err(dev, "cannot enable DSI clock: %d\n", err);
goto disable_vdd;
}
err = clk_prepare_enable(dsi->clk_lp);
if (err < 0) {
dev_err(dev, "cannot enable low-power clock: %d\n", err);
goto disable_clk;
}
usleep_range(1000, 2000);
if (dsi->rst) {
err = reset_control_deassert(dsi->rst);
if (err < 0) {
dev_err(dev, "cannot assert reset: %d\n", err);
goto disable_clk_lp;
}
}
return 0;
disable_clk_lp:
clk_disable_unprepare(dsi->clk_lp);
disable_clk:
clk_disable_unprepare(dsi->clk);
disable_vdd:
regulator_disable(dsi->vdd);
put_rpm:
pm_runtime_put_sync(dev);
return err;
}
static const struct host1x_client_ops dsi_client_ops = {
.init = tegra_dsi_init,
.exit = tegra_dsi_exit,
.suspend = tegra_dsi_runtime_suspend,
.resume = tegra_dsi_runtime_resume,
};
static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
......@@ -1596,79 +1689,6 @@ static int tegra_dsi_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int tegra_dsi_suspend(struct device *dev)
{
struct tegra_dsi *dsi = dev_get_drvdata(dev);
int err;
if (dsi->rst) {
err = reset_control_assert(dsi->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
}
usleep_range(1000, 2000);
clk_disable_unprepare(dsi->clk_lp);
clk_disable_unprepare(dsi->clk);
regulator_disable(dsi->vdd);
return 0;
}
static int tegra_dsi_resume(struct device *dev)
{
struct tegra_dsi *dsi = dev_get_drvdata(dev);
int err;
err = regulator_enable(dsi->vdd);
if (err < 0) {
dev_err(dsi->dev, "failed to enable VDD supply: %d\n", err);
return err;
}
err = clk_prepare_enable(dsi->clk);
if (err < 0) {
dev_err(dev, "cannot enable DSI clock: %d\n", err);
goto disable_vdd;
}
err = clk_prepare_enable(dsi->clk_lp);
if (err < 0) {
dev_err(dev, "cannot enable low-power clock: %d\n", err);
goto disable_clk;
}
usleep_range(1000, 2000);
if (dsi->rst) {
err = reset_control_deassert(dsi->rst);
if (err < 0) {
dev_err(dev, "cannot assert reset: %d\n", err);
goto disable_clk_lp;
}
}
return 0;
disable_clk_lp:
clk_disable_unprepare(dsi->clk_lp);
disable_clk:
clk_disable_unprepare(dsi->clk);
disable_vdd:
regulator_disable(dsi->vdd);
return err;
}
#endif
static const struct dev_pm_ops tegra_dsi_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_dsi_suspend, tegra_dsi_resume, NULL)
};
static const struct of_device_id tegra_dsi_of_match[] = {
{ .compatible = "nvidia,tegra210-dsi", },
{ .compatible = "nvidia,tegra132-dsi", },
......@@ -1682,7 +1702,6 @@ struct platform_driver tegra_dsi_driver = {
.driver = {
.name = "tegra-dsi",
.of_match_table = tegra_dsi_of_match,
.pm = &tegra_dsi_pm_ops,
},
.probe = tegra_dsi_probe,
.remove = tegra_dsi_remove,
......
......@@ -34,7 +34,7 @@ static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
static int gr2d_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct drm_device *dev = dev_get_drvdata(client->host);
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
struct gr2d *gr2d = to_gr2d(drm);
int err;
......@@ -76,7 +76,7 @@ static int gr2d_init(struct host1x_client *client)
static int gr2d_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct drm_device *dev = dev_get_drvdata(client->host);
struct tegra_drm *tegra = dev->dev_private;
struct gr2d *gr2d = to_gr2d(drm);
int err;
......
......@@ -43,7 +43,7 @@ static inline struct gr3d *to_gr3d(struct tegra_drm_client *client)
static int gr3d_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct drm_device *dev = dev_get_drvdata(client->host);
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
struct gr3d *gr3d = to_gr3d(drm);
int err;
......@@ -85,7 +85,7 @@ static int gr3d_init(struct host1x_client *client)
static int gr3d_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct drm_device *dev = dev_get_drvdata(client->host);
struct gr3d *gr3d = to_gr3d(drm);
int err;
......
......@@ -1146,6 +1146,7 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
struct tegra_hdmi *hdmi = to_hdmi(output);
u32 value;
int err;
/*
* The following accesses registers of the display controller, so make
......@@ -1171,7 +1172,9 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_ENABLE);
tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_MASK);
pm_runtime_put(hdmi->dev);
err = host1x_client_suspend(&hdmi->client);
if (err < 0)
dev_err(hdmi->dev, "failed to suspend: %d\n", err);
}
static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
......@@ -1186,7 +1189,11 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
u32 value;
int err;
pm_runtime_get_sync(hdmi->dev);
err = host1x_client_resume(&hdmi->client);
if (err < 0) {
dev_err(hdmi->dev, "failed to resume: %d\n", err);
return;
}
/*
* Enable and unmask the HDA codec SCRATCH0 register interrupt. This
......@@ -1424,8 +1431,8 @@ static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
static int tegra_hdmi_init(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->parent);
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
struct drm_device *drm = dev_get_drvdata(client->host);
int err;
hdmi->output.dev = client->dev;
......@@ -1490,9 +1497,66 @@ static int tegra_hdmi_exit(struct host1x_client *client)
return 0;
}
static int tegra_hdmi_runtime_suspend(struct host1x_client *client)
{
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
struct device *dev = client->dev;
int err;
err = reset_control_assert(hdmi->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
usleep_range(1000, 2000);
clk_disable_unprepare(hdmi->clk);
pm_runtime_put_sync(dev);
return 0;
}
static int tegra_hdmi_runtime_resume(struct host1x_client *client)
{
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
struct device *dev = client->dev;
int err;
err = pm_runtime_get_sync(dev);
if (err < 0) {
dev_err(dev, "failed to get runtime PM: %d\n", err);
return err;
}
err = clk_prepare_enable(hdmi->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
goto put_rpm;
}
usleep_range(1000, 2000);
err = reset_control_deassert(hdmi->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
goto disable_clk;
}
return 0;
disable_clk:
clk_disable_unprepare(hdmi->clk);
put_rpm:
pm_runtime_put_sync(dev);
return err;
}
static const struct host1x_client_ops hdmi_client_ops = {
.init = tegra_hdmi_init,
.exit = tegra_hdmi_exit,
.suspend = tegra_hdmi_runtime_suspend,
.resume = tegra_hdmi_runtime_resume,
};
static const struct tegra_hdmi_config tegra20_hdmi_config = {
......@@ -1700,58 +1764,10 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int tegra_hdmi_suspend(struct device *dev)
{
struct tegra_hdmi *hdmi = dev_get_drvdata(dev);
int err;
err = reset_control_assert(hdmi->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
usleep_range(1000, 2000);
clk_disable_unprepare(hdmi->clk);
return 0;
}
static int tegra_hdmi_resume(struct device *dev)
{
struct tegra_hdmi *hdmi = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(hdmi->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
return err;
}
usleep_range(1000, 2000);
err = reset_control_deassert(hdmi->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
clk_disable_unprepare(hdmi->clk);
return err;
}
return 0;
}
#endif
static const struct dev_pm_ops tegra_hdmi_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_hdmi_suspend, tegra_hdmi_resume, NULL)
};
struct platform_driver tegra_hdmi_driver = {
.driver = {
.name = "tegra-hdmi",
.of_match_table = tegra_hdmi_of_match,
.pm = &tegra_hdmi_pm_ops,
},
.probe = tegra_hdmi_probe,
.remove = tegra_hdmi_remove,
......
......@@ -95,17 +95,25 @@ static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
{
int err = 0;
mutex_lock(&wgrp->lock);
if (wgrp->usecount == 0) {
pm_runtime_get_sync(wgrp->parent);
err = host1x_client_resume(wgrp->parent);
if (err < 0) {
dev_err(wgrp->parent->dev, "failed to resume: %d\n", err);
goto unlock;
}
reset_control_deassert(wgrp->rst);
}
wgrp->usecount++;
mutex_unlock(&wgrp->lock);
return 0;
unlock:
mutex_unlock(&wgrp->lock);
return err;
}
static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
......@@ -121,7 +129,7 @@ static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
wgrp->index);
}
pm_runtime_put(wgrp->parent);
host1x_client_suspend(wgrp->parent);
}
wgrp->usecount--;
......@@ -379,6 +387,7 @@ static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
struct tegra_plane *p = to_tegra_plane(plane);
struct tegra_dc *dc;
u32 value;
int err;
/* rien ne va plus */
if (!old_state || !old_state->crtc)
......@@ -386,6 +395,12 @@ static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
dc = to_tegra_dc(old_state->crtc);
err = host1x_client_resume(&dc->client);
if (err < 0) {
dev_err(dc->dev, "failed to resume: %d\n", err);
return;
}
/*
* XXX Legacy helpers seem to sometimes call ->atomic_disable() even
* on planes that are already disabled. Make sure we fallback to the
......@@ -394,15 +409,13 @@ static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
if (WARN_ON(p->dc == NULL))
p->dc = dc;
pm_runtime_get_sync(dc->dev);
value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
value &= ~WIN_ENABLE;
tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
tegra_dc_remove_shared_plane(dc, p);
pm_runtime_put(dc->dev);
host1x_client_suspend(&dc->client);
}
static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
......@@ -415,6 +428,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
struct tegra_plane *p = to_tegra_plane(plane);
dma_addr_t base;
u32 value;
int err;
/* rien ne va plus */
if (!plane->state->crtc || !plane->state->fb)
......@@ -425,7 +439,11 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
return;
}
pm_runtime_get_sync(dc->dev);
err = host1x_client_resume(&dc->client);
if (err < 0) {
dev_err(dc->dev, "failed to resume: %d\n", err);
return;
}
tegra_dc_assign_shared_plane(dc, p);
......@@ -515,7 +533,7 @@ static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
value &= ~CONTROL_CSC_ENABLE;
tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
pm_runtime_put(dc->dev);
host1x_client_suspend(&dc->client);
}
static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
......@@ -551,7 +569,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
plane->base.index = index;
plane->wgrp = &hub->wgrps[wgrp];
plane->wgrp->parent = dc->dev;
plane->wgrp->parent = &dc->client;
p = &plane->base.base;
......@@ -656,8 +674,13 @@ int tegra_display_hub_atomic_check(struct drm_device *drm,
static void tegra_display_hub_update(struct tegra_dc *dc)
{
u32 value;
int err;
pm_runtime_get_sync(dc->dev);
err = host1x_client_resume(&dc->client);
if (err < 0) {
dev_err(dc->dev, "failed to resume: %d\n", err);
return;
}
value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
value &= ~LATENCY_EVENT;
......@@ -672,7 +695,7 @@ static void tegra_display_hub_update(struct tegra_dc *dc)
tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
pm_runtime_put(dc->dev);
host1x_client_suspend(&dc->client);
}
void tegra_display_hub_atomic_commit(struct drm_device *drm,
......@@ -705,7 +728,7 @@ void tegra_display_hub_atomic_commit(struct drm_device *drm,
static int tegra_display_hub_init(struct host1x_client *client)
{
struct tegra_display_hub *hub = to_tegra_display_hub(client);
struct drm_device *drm = dev_get_drvdata(client->parent);
struct drm_device *drm = dev_get_drvdata(client->host);
struct tegra_drm *tegra = drm->dev_private;
struct tegra_display_hub_state *state;
......@@ -723,7 +746,7 @@ static int tegra_display_hub_init(struct host1x_client *client)
static int tegra_display_hub_exit(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->parent);
struct drm_device *drm = dev_get_drvdata(client->host);
struct tegra_drm *tegra = drm->dev_private;
drm_atomic_private_obj_fini(&tegra->hub->base);
......@@ -732,9 +755,85 @@ static int tegra_display_hub_exit(struct host1x_client *client)
return 0;
}
static int tegra_display_hub_runtime_suspend(struct host1x_client *client)
{
struct tegra_display_hub *hub = to_tegra_display_hub(client);
struct device *dev = client->dev;
unsigned int i = hub->num_heads;
int err;
err = reset_control_assert(hub->rst);
if (err < 0)
return err;
while (i--)
clk_disable_unprepare(hub->clk_heads[i]);
clk_disable_unprepare(hub->clk_hub);
clk_disable_unprepare(hub->clk_dsc);
clk_disable_unprepare(hub->clk_disp);
pm_runtime_put_sync(dev);
return 0;
}
static int tegra_display_hub_runtime_resume(struct host1x_client *client)
{
struct tegra_display_hub *hub = to_tegra_display_hub(client);
struct device *dev = client->dev;
unsigned int i;
int err;
err = pm_runtime_get_sync(dev);
if (err < 0) {
dev_err(dev, "failed to get runtime PM: %d\n", err);
return err;
}
err = clk_prepare_enable(hub->clk_disp);
if (err < 0)
goto put_rpm;
err = clk_prepare_enable(hub->clk_dsc);
if (err < 0)
goto disable_disp;
err = clk_prepare_enable(hub->clk_hub);
if (err < 0)
goto disable_dsc;
for (i = 0; i < hub->num_heads; i++) {
err = clk_prepare_enable(hub->clk_heads[i]);
if (err < 0)
goto disable_heads;
}
err = reset_control_deassert(hub->rst);
if (err < 0)
goto disable_heads;
return 0;
disable_heads:
while (i--)
clk_disable_unprepare(hub->clk_heads[i]);
clk_disable_unprepare(hub->clk_hub);
disable_dsc:
clk_disable_unprepare(hub->clk_dsc);
disable_disp:
clk_disable_unprepare(hub->clk_disp);
put_rpm:
pm_runtime_put_sync(dev);
return err;
}
static const struct host1x_client_ops tegra_display_hub_ops = {
.init = tegra_display_hub_init,
.exit = tegra_display_hub_exit,
.suspend = tegra_display_hub_runtime_suspend,
.resume = tegra_display_hub_runtime_resume,
};
static int tegra_display_hub_probe(struct platform_device *pdev)
......@@ -851,6 +950,7 @@ static int tegra_display_hub_probe(struct platform_device *pdev)
static int tegra_display_hub_remove(struct platform_device *pdev)
{
struct tegra_display_hub *hub = platform_get_drvdata(pdev);
unsigned int i;
int err;
err = host1x_client_unregister(&hub->client);
......@@ -859,78 +959,17 @@ static int tegra_display_hub_remove(struct platform_device *pdev)
err);
}
pm_runtime_disable(&pdev->dev);
return err;
}
static int __maybe_unused tegra_display_hub_suspend(struct device *dev)
{
struct tegra_display_hub *hub = dev_get_drvdata(dev);
unsigned int i = hub->num_heads;
int err;
err = reset_control_assert(hub->rst);
if (err < 0)
return err;
while (i--)
clk_disable_unprepare(hub->clk_heads[i]);
clk_disable_unprepare(hub->clk_hub);
clk_disable_unprepare(hub->clk_dsc);
clk_disable_unprepare(hub->clk_disp);
return 0;
}
static int __maybe_unused tegra_display_hub_resume(struct device *dev)
{
struct tegra_display_hub *hub = dev_get_drvdata(dev);
unsigned int i;
int err;
err = clk_prepare_enable(hub->clk_disp);
if (err < 0)
return err;
err = clk_prepare_enable(hub->clk_dsc);
if (err < 0)
goto disable_disp;
err = clk_prepare_enable(hub->clk_hub);
if (err < 0)
goto disable_dsc;
for (i = 0; i < hub->soc->num_wgrps; i++) {
struct tegra_windowgroup *wgrp = &hub->wgrps[i];
for (i = 0; i < hub->num_heads; i++) {
err = clk_prepare_enable(hub->clk_heads[i]);
if (err < 0)
goto disable_heads;
mutex_destroy(&wgrp->lock);
}
err = reset_control_deassert(hub->rst);
if (err < 0)
goto disable_heads;
return 0;
disable_heads:
while (i--)
clk_disable_unprepare(hub->clk_heads[i]);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(hub->clk_hub);
disable_dsc:
clk_disable_unprepare(hub->clk_dsc);
disable_disp:
clk_disable_unprepare(hub->clk_disp);
return err;
}
static const struct dev_pm_ops tegra_display_hub_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_display_hub_suspend,
tegra_display_hub_resume, NULL)
};
static const struct tegra_display_hub_soc tegra186_display_hub = {
.num_wgrps = 6,
.supports_dsc = true,
......@@ -958,7 +997,6 @@ struct platform_driver tegra_display_hub_driver = {
.driver = {
.name = "tegra-display-hub",
.of_match_table = tegra_display_hub_of_match,
.pm = &tegra_display_hub_pm_ops,
},
.probe = tegra_display_hub_probe,
.remove = tegra_display_hub_remove,
......
......@@ -17,7 +17,7 @@ struct tegra_windowgroup {
struct mutex lock;
unsigned int index;
struct device *parent;
struct host1x_client *parent;
struct reset_control *rst;
};
......
......@@ -250,3 +250,19 @@ void tegra_output_find_possible_crtcs(struct tegra_output *output,
output->encoder.possible_crtcs = mask;
}
int tegra_output_suspend(struct tegra_output *output)
{
if (output->hpd_irq)
disable_irq(output->hpd_irq);
return 0;
}
int tegra_output_resume(struct tegra_output *output)
{
if (output->hpd_irq)
enable_irq(output->hpd_irq);
return 0;
}
......@@ -2255,7 +2255,7 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
if (err < 0)
dev_err(sor->dev, "failed to power off I/O pad: %d\n", err);
pm_runtime_put(sor->dev);
host1x_client_suspend(&sor->client);
}
static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
......@@ -2276,7 +2276,11 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
mode = &encoder->crtc->state->adjusted_mode;
pclk = mode->clock * 1000;
pm_runtime_get_sync(sor->dev);
err = host1x_client_resume(&sor->client);
if (err < 0) {
dev_err(sor->dev, "failed to resume: %d\n", err);
return;
}
/* switch to safe parent clock */
err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
......@@ -2722,7 +2726,7 @@ static void tegra_sor_dp_disable(struct drm_encoder *encoder)
if (output->panel)
drm_panel_unprepare(output->panel);
pm_runtime_put(sor->dev);
host1x_client_suspend(&sor->client);
}
static void tegra_sor_dp_enable(struct drm_encoder *encoder)
......@@ -2742,7 +2746,11 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)
mode = &encoder->crtc->state->adjusted_mode;
info = &output->connector.display_info;
pm_runtime_get_sync(sor->dev);
err = host1x_client_resume(&sor->client);
if (err < 0) {
dev_err(sor->dev, "failed to resume: %d\n", err);
return;
}
/* switch to safe parent clock */
err = tegra_sor_set_parent_clock(sor, sor->clk_safe);
......@@ -3053,7 +3061,7 @@ static const struct tegra_sor_ops tegra_sor_dp_ops = {
static int tegra_sor_init(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->parent);
struct drm_device *drm = dev_get_drvdata(client->host);
const struct drm_encoder_helper_funcs *helpers = NULL;
struct tegra_sor *sor = host1x_client_to_sor(client);
int connector = DRM_MODE_CONNECTOR_Unknown;
......@@ -3190,9 +3198,80 @@ static int tegra_sor_exit(struct host1x_client *client)
return 0;
}
static int tegra_sor_runtime_suspend(struct host1x_client *client)
{
struct tegra_sor *sor = host1x_client_to_sor(client);
struct device *dev = client->dev;
int err;
if (sor->rst) {
err = reset_control_assert(sor->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
reset_control_release(sor->rst);
}
usleep_range(1000, 2000);
clk_disable_unprepare(sor->clk);
pm_runtime_put_sync(dev);
return 0;
}
static int tegra_sor_runtime_resume(struct host1x_client *client)
{
struct tegra_sor *sor = host1x_client_to_sor(client);
struct device *dev = client->dev;
int err;
err = pm_runtime_get_sync(dev);
if (err < 0) {
dev_err(dev, "failed to get runtime PM: %d\n", err);
return err;
}
err = clk_prepare_enable(sor->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
goto put_rpm;
}
usleep_range(1000, 2000);
if (sor->rst) {
err = reset_control_acquire(sor->rst);
if (err < 0) {
dev_err(dev, "failed to acquire reset: %d\n", err);
goto disable_clk;
}
err = reset_control_deassert(sor->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
goto release_reset;
}
}
return 0;
release_reset:
reset_control_release(sor->rst);
disable_clk:
clk_disable_unprepare(sor->clk);
put_rpm:
pm_runtime_put_sync(dev);
return err;
}
static const struct host1x_client_ops sor_client_ops = {
.init = tegra_sor_init,
.exit = tegra_sor_exit,
.suspend = tegra_sor_runtime_suspend,
.resume = tegra_sor_runtime_resume,
};
static const u8 tegra124_sor_xbar_cfg[5] = {
......@@ -3843,10 +3922,9 @@ static int tegra_sor_probe(struct platform_device *pdev)
if (!sor->clk_pad) {
char *name;
err = pm_runtime_get_sync(&pdev->dev);
err = host1x_client_resume(&sor->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to get runtime PM: %d\n",
err);
dev_err(sor->dev, "failed to resume: %d\n", err);
goto remove;
}
......@@ -3857,7 +3935,7 @@ static int tegra_sor_probe(struct platform_device *pdev)
}
sor->clk_pad = tegra_clk_sor_pad_register(sor, name);
pm_runtime_put(&pdev->dev);
host1x_client_suspend(&sor->client);
}
if (IS_ERR(sor->clk_pad)) {
......@@ -3913,54 +3991,21 @@ static int tegra_sor_remove(struct platform_device *pdev)
return 0;
}
static int tegra_sor_runtime_suspend(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
int err;
if (sor->rst) {
err = reset_control_assert(sor->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
reset_control_release(sor->rst);
}
usleep_range(1000, 2000);
clk_disable_unprepare(sor->clk);
return 0;
}
static int tegra_sor_runtime_resume(struct device *dev)
static int __maybe_unused tegra_sor_suspend(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(sor->clk);
err = tegra_output_suspend(&sor->output);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
dev_err(dev, "failed to suspend output: %d\n", err);
return err;
}
usleep_range(1000, 2000);
if (sor->rst) {
err = reset_control_acquire(sor->rst);
if (err < 0) {
dev_err(dev, "failed to acquire reset: %d\n", err);
clk_disable_unprepare(sor->clk);
return err;
}
err = reset_control_deassert(sor->rst);
if (sor->hdmi_supply) {
err = regulator_disable(sor->hdmi_supply);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
reset_control_release(sor->rst);
clk_disable_unprepare(sor->clk);
tegra_output_resume(&sor->output);
return err;
}
}
......@@ -3968,37 +4013,31 @@ static int tegra_sor_runtime_resume(struct device *dev)
return 0;
}
static int tegra_sor_suspend(struct device *dev)
static int __maybe_unused tegra_sor_resume(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
int err;
if (sor->hdmi_supply) {
err = regulator_disable(sor->hdmi_supply);
err = regulator_enable(sor->hdmi_supply);
if (err < 0)
return err;
}
return 0;
}
err = tegra_output_resume(&sor->output);
if (err < 0) {
dev_err(dev, "failed to resume output: %d\n", err);
static int tegra_sor_resume(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
int err;
if (sor->hdmi_supply)
regulator_disable(sor->hdmi_supply);
if (sor->hdmi_supply) {
err = regulator_enable(sor->hdmi_supply);
if (err < 0)
return err;
return err;
}
return 0;
}
static const struct dev_pm_ops tegra_sor_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_sor_runtime_suspend, tegra_sor_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(tegra_sor_suspend, tegra_sor_resume)
};
......
......@@ -161,7 +161,7 @@ static int vic_boot(struct vic *vic)
static int vic_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct drm_device *dev = dev_get_drvdata(client->host);
struct tegra_drm *tegra = dev->dev_private;
struct vic *vic = to_vic(drm);
int err;
......@@ -190,9 +190,9 @@ static int vic_init(struct host1x_client *client)
/*
* Inherit the DMA parameters (such as maximum segment size) from the
* parent device.
* parent host1x device.
*/
client->dev->dma_parms = client->parent->dma_parms;
client->dev->dma_parms = client->host->dma_parms;
return 0;
......@@ -209,7 +209,7 @@ static int vic_init(struct host1x_client *client)
static int vic_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct drm_device *dev = dev_get_drvdata(client->parent);
struct drm_device *dev = dev_get_drvdata(client->host);
struct tegra_drm *tegra = dev->dev_private;
struct vic *vic = to_vic(drm);
int err;
......
......@@ -120,7 +120,7 @@ static void host1x_subdev_register(struct host1x_device *device,
mutex_lock(&device->clients_lock);
list_move_tail(&client->list, &device->clients);
list_move_tail(&subdev->list, &device->active);
client->parent = &device->dev;
client->host = &device->dev;
subdev->client = client;
mutex_unlock(&device->clients_lock);
mutex_unlock(&device->subdevs_lock);
......@@ -156,7 +156,7 @@ static void __host1x_subdev_unregister(struct host1x_device *device,
*/
mutex_lock(&device->clients_lock);
subdev->client = NULL;
client->parent = NULL;
client->host = NULL;
list_move_tail(&subdev->list, &device->subdevs);
/*
* XXX: Perhaps don't do this here, but rather explicitly remove it
......@@ -710,6 +710,10 @@ int host1x_client_register(struct host1x_client *client)
struct host1x *host1x;
int err;
INIT_LIST_HEAD(&client->list);
mutex_init(&client->lock);
client->usecount = 0;
mutex_lock(&devices_lock);
list_for_each_entry(host1x, &devices, list) {
......@@ -768,3 +772,74 @@ int host1x_client_unregister(struct host1x_client *client)
return 0;
}
EXPORT_SYMBOL(host1x_client_unregister);
int host1x_client_suspend(struct host1x_client *client)
{
int err = 0;
mutex_lock(&client->lock);
if (client->usecount == 1) {
if (client->ops && client->ops->suspend) {
err = client->ops->suspend(client);
if (err < 0)
goto unlock;
}
}
client->usecount--;
dev_dbg(client->dev, "use count: %u\n", client->usecount);
if (client->parent) {
err = host1x_client_suspend(client->parent);
if (err < 0)
goto resume;
}
goto unlock;
resume:
if (client->usecount == 0)
if (client->ops && client->ops->resume)
client->ops->resume(client);
client->usecount++;
unlock:
mutex_unlock(&client->lock);
return err;
}
EXPORT_SYMBOL(host1x_client_suspend);
int host1x_client_resume(struct host1x_client *client)
{
int err = 0;
mutex_lock(&client->lock);
if (client->parent) {
err = host1x_client_resume(client->parent);
if (err < 0)
goto unlock;
}
if (client->usecount == 0) {
if (client->ops && client->ops->resume) {
err = client->ops->resume(client);
if (err < 0)
goto suspend;
}
}
client->usecount++;
dev_dbg(client->dev, "use count: %u\n", client->usecount);
goto unlock;
suspend:
if (client->parent)
host1x_client_suspend(client->parent);
unlock:
mutex_unlock(&client->lock);
return err;
}
EXPORT_SYMBOL(host1x_client_resume);
......@@ -339,10 +339,8 @@ static int host1x_probe(struct platform_device *pdev)
}
syncpt_irq = platform_get_irq(pdev, 0);
if (syncpt_irq < 0) {
dev_err(&pdev->dev, "failed to get IRQ: %d\n", syncpt_irq);
if (syncpt_irq < 0)
return syncpt_irq;
}
mutex_init(&host->devices_lock);
INIT_LIST_HEAD(&host->devices);
......
......@@ -421,7 +421,7 @@ int host1x_syncpt_init(struct host1x *host)
struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client,
unsigned long flags)
{
struct host1x *host = dev_get_drvdata(client->parent->parent);
struct host1x *host = dev_get_drvdata(client->host->parent);
return host1x_syncpt_alloc(host, client, flags);
}
......
......@@ -24,16 +24,20 @@ struct iommu_group;
* struct host1x_client_ops - host1x client operations
* @init: host1x client initialization code
* @exit: host1x client tear down code
* @suspend: host1x client suspend code
* @resume: host1x client resume code
*/
struct host1x_client_ops {
int (*init)(struct host1x_client *client);
int (*exit)(struct host1x_client *client);
int (*suspend)(struct host1x_client *client);
int (*resume)(struct host1x_client *client);
};
/**
* struct host1x_client - host1x client structure
* @list: list node for the host1x client
* @parent: pointer to struct device representing the host1x controller
* @host: pointer to struct device representing the host1x controller
* @dev: pointer to struct device backing this host1x client
* @group: IOMMU group that this client is a member of
* @ops: host1x client operations
......@@ -44,7 +48,7 @@ struct host1x_client_ops {
*/
struct host1x_client {
struct list_head list;
struct device *parent;
struct device *host;
struct device *dev;
struct iommu_group *group;
......@@ -55,6 +59,10 @@ struct host1x_client {
struct host1x_syncpt **syncpts;
unsigned int num_syncpts;
struct host1x_client *parent;
unsigned int usecount;
struct mutex lock;
};
/*
......@@ -309,6 +317,9 @@ int host1x_device_exit(struct host1x_device *device);
int host1x_client_register(struct host1x_client *client);
int host1x_client_unregister(struct host1x_client *client);
int host1x_client_suspend(struct host1x_client *client);
int host1x_client_resume(struct host1x_client *client);
struct tegra_mipi_device;
struct tegra_mipi_device *tegra_mipi_request(struct device *device);
......
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