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