drm/tegra: Changes for v5.6-rc1
This contains a small set of mostly fixes and some minor improvements. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAl4ZGs0THHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zoZUkEACo+fhKHTmkCz4W+4m90aEFL/VUi7Bn lFmssk2E71WvxUSWfLYawuhi/bgtMLvPmTi1KTCpvdvr+ladF1f4/Vd6vuhMu7Ec t2ZePYmRlpX+3PHW+V2fIRyBC9/6wSx2UEAbcMOijIPHlWDNdrxo26w0+af0dBJ3 b8oIv5q5jX0GzZDOCOsBngUwnVzCYthVz7SA+UXOwBvNPO61kjx8IA/IfBHAQ+HF ZVpn//C7g2/wHq125kdrFUwAWQPvFMHOs1JPfXS9248kobkIabSp0XxLaj403cFU WWa/no5cPkG+WRwaX5egvE2/8P3mDzu3ev9gkgpdDJxnn1YL656mPWO7jNqW4IJd JD8kTk7ODh7KQvY3xdHD3MrHpHWHtHdOiFZtOygiW84W2yK5/g/LRd/oph/xP1RE /h8eiRP/mK7VkVRmgbiZTIEQVp10RKH56XtVd1Cf3eFNkig1Q8MLarwy2rhClyJO 7cJZKoOqRfx0aMYipxx5+YWDXrCVwtNrQKTroJ27ycre3ykv+NAoeHeei9ie81Rg XZeCF3OuvVNXoUPQRS3XYkH70CTbdDTpkwymctcfwWOerhn65QAHvCSNR91ezmNq YwC7CkS6ymeG9UhSoCvNoLa1adfeztRCwg8RkKdHgHFtHNNQuyRlp4xNyrJNgoCO YX5dApw++iTOwQ== =OBEv -----END PGP SIGNATURE----- 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: Dave 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
This commit is contained in:
commit
fd7226fbb2
|
@ -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 @@ put:
|
|||
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 @@ put:
|
|||
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);
|
||||
}
|
||||
|
||||
for (i = 0; i < hub->soc->num_wgrps; i++) {
|
||||
struct tegra_windowgroup *wgrp = &hub->wgrps[i];
|
||||
|
||||
mutex_destroy(&wgrp->lock);
|
||||
}
|
||||
|
||||
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->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);
|
||||
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,76 +3991,29 @@ static int tegra_sor_remove(struct platform_device *pdev)
|
|||
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);
|
||||
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 (err < 0) {
|
||||
dev_err(dev, "failed to deassert reset: %d\n", err);
|
||||
reset_control_release(sor->rst);
|
||||
clk_disable_unprepare(sor->clk);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_sor_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_sor *sor = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
if (sor->hdmi_supply) {
|
||||
err = regulator_disable(sor->hdmi_supply);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
tegra_output_resume(&sor->output);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_sor_resume(struct device *dev)
|
||||
static int __maybe_unused tegra_sor_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_sor *sor = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
@ -3993,12 +4024,20 @@ static int tegra_sor_resume(struct device *dev)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = tegra_output_resume(&sor->output);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to resume output: %d\n", err);
|
||||
|
||||
if (sor->hdmi_supply)
|
||||
regulator_disable(sor->hdmi_supply);
|
||||
|
||||
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 @@ detach:
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue