gpu: host1x: mipi: Power down regulators when unused

Keep track of the number of users of DSI and CSI pads and power down the
regulators that supply the bricks when all users are gone.

Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
Thierry Reding 2015-04-10 11:29:41 +02:00
parent 5e7752436e
commit 15372d4be7
1 changed files with 98 additions and 12 deletions

View File

@ -118,9 +118,12 @@ struct tegra_mipi_soc {
struct tegra_mipi { struct tegra_mipi {
const struct tegra_mipi_soc *soc; const struct tegra_mipi_soc *soc;
struct device *dev;
void __iomem *regs; void __iomem *regs;
struct mutex lock; struct mutex lock;
struct clk *clk; struct clk *clk;
unsigned long usage_count;
}; };
struct tegra_mipi_device { struct tegra_mipi_device {
@ -142,6 +145,67 @@ static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
writel(value, mipi->regs + (offset << 2)); writel(value, mipi->regs + (offset << 2));
} }
static int tegra_mipi_power_up(struct tegra_mipi *mipi)
{
u32 value;
int err;
err = clk_enable(mipi->clk);
if (err < 0)
return err;
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
if (mipi->soc->needs_vclamp_ref)
value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
clk_disable(mipi->clk);
return 0;
}
static int tegra_mipi_power_down(struct tegra_mipi *mipi)
{
u32 value;
int err;
err = clk_enable(mipi->clk);
if (err < 0)
return err;
/*
* The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
* supplies the DSI pads. This must be kept enabled until none of the
* DSI lanes are used anymore.
*/
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
value |= MIPI_CAL_BIAS_PAD_PDVREG;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
/*
* MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
* control a regulator that supplies current to the pre-driver logic.
* Powering down this regulator causes DSI to fail, so it must remain
* powered on until none of the DSI lanes are used anymore.
*/
value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
if (mipi->soc->needs_vclamp_ref)
value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
return 0;
}
struct tegra_mipi_device *tegra_mipi_request(struct device *device) struct tegra_mipi_device *tegra_mipi_request(struct device *device)
{ {
struct device_node *np = device->of_node; struct device_node *np = device->of_node;
@ -178,6 +242,20 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device)
dev->pads = args.args[0]; dev->pads = args.args[0];
dev->device = device; dev->device = device;
mutex_lock(&dev->mipi->lock);
if (dev->mipi->usage_count++ == 0) {
err = tegra_mipi_power_up(dev->mipi);
if (err < 0) {
dev_err(dev->mipi->dev,
"failed to power up MIPI bricks: %d\n",
err);
return ERR_PTR(err);
}
}
mutex_unlock(&dev->mipi->lock);
return dev; return dev;
put: put:
@ -192,6 +270,25 @@ EXPORT_SYMBOL(tegra_mipi_request);
void tegra_mipi_free(struct tegra_mipi_device *device) void tegra_mipi_free(struct tegra_mipi_device *device)
{ {
int err;
mutex_lock(&device->mipi->lock);
if (--device->mipi->usage_count == 0) {
err = tegra_mipi_power_down(device->mipi);
if (err < 0) {
/*
* Not much that can be done here, so an error message
* will have to do.
*/
dev_err(device->mipi->dev,
"failed to power down MIPI bricks: %d\n",
err);
}
}
mutex_unlock(&device->mipi->lock);
platform_device_put(device->pdev); platform_device_put(device->pdev);
kfree(device); kfree(device);
} }
@ -227,22 +324,10 @@ int tegra_mipi_calibrate(struct tegra_mipi_device *device)
mutex_lock(&device->mipi->lock); mutex_lock(&device->mipi->lock);
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
if (soc->needs_vclamp_ref)
value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) | value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref); MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1); tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2); value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
@ -426,6 +511,7 @@ static int tegra_mipi_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
mipi->soc = match->data; mipi->soc = match->data;
mipi->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mipi->regs = devm_ioremap_resource(&pdev->dev, res); mipi->regs = devm_ioremap_resource(&pdev->dev, res);