Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+98 -12
+98 -12
drivers/gpu/host1x/mipi.c
··· 118 118 119 119 struct tegra_mipi { 120 120 const struct tegra_mipi_soc *soc; 121 + struct device *dev; 121 122 void __iomem *regs; 122 123 struct mutex lock; 123 124 struct clk *clk; 125 + 126 + unsigned long usage_count; 124 127 }; 125 128 126 129 struct tegra_mipi_device { ··· 143 140 unsigned long offset) 144 141 { 145 142 writel(value, mipi->regs + (offset << 2)); 143 + } 144 + 145 + static int tegra_mipi_power_up(struct tegra_mipi *mipi) 146 + { 147 + u32 value; 148 + int err; 149 + 150 + err = clk_enable(mipi->clk); 151 + if (err < 0) 152 + return err; 153 + 154 + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0); 155 + value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; 156 + 157 + if (mipi->soc->needs_vclamp_ref) 158 + value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; 159 + 160 + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0); 161 + 162 + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2); 163 + value &= ~MIPI_CAL_BIAS_PAD_PDVREG; 164 + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2); 165 + 166 + clk_disable(mipi->clk); 167 + 168 + return 0; 169 + } 170 + 171 + static int tegra_mipi_power_down(struct tegra_mipi *mipi) 172 + { 173 + u32 value; 174 + int err; 175 + 176 + err = clk_enable(mipi->clk); 177 + if (err < 0) 178 + return err; 179 + 180 + /* 181 + * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that 182 + * supplies the DSI pads. This must be kept enabled until none of the 183 + * DSI lanes are used anymore. 184 + */ 185 + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2); 186 + value |= MIPI_CAL_BIAS_PAD_PDVREG; 187 + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2); 188 + 189 + /* 190 + * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF 191 + * control a regulator that supplies current to the pre-driver logic. 192 + * Powering down this regulator causes DSI to fail, so it must remain 193 + * powered on until none of the DSI lanes are used anymore. 194 + */ 195 + value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0); 196 + 197 + if (mipi->soc->needs_vclamp_ref) 198 + value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; 199 + 200 + value |= MIPI_CAL_BIAS_PAD_PDVCLAMP; 201 + tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0); 202 + 203 + return 0; 146 204 } 147 205 148 206 struct tegra_mipi_device *tegra_mipi_request(struct device *device) ··· 242 178 dev->pads = args.args[0]; 243 179 dev->device = device; 244 180 181 + mutex_lock(&dev->mipi->lock); 182 + 183 + if (dev->mipi->usage_count++ == 0) { 184 + err = tegra_mipi_power_up(dev->mipi); 185 + if (err < 0) { 186 + dev_err(dev->mipi->dev, 187 + "failed to power up MIPI bricks: %d\n", 188 + err); 189 + return ERR_PTR(err); 190 + } 191 + } 192 + 193 + mutex_unlock(&dev->mipi->lock); 194 + 245 195 return dev; 246 196 247 197 put: ··· 270 192 271 193 void tegra_mipi_free(struct tegra_mipi_device *device) 272 194 { 195 + int err; 196 + 197 + mutex_lock(&device->mipi->lock); 198 + 199 + if (--device->mipi->usage_count == 0) { 200 + err = tegra_mipi_power_down(device->mipi); 201 + if (err < 0) { 202 + /* 203 + * Not much that can be done here, so an error message 204 + * will have to do. 205 + */ 206 + dev_err(device->mipi->dev, 207 + "failed to power down MIPI bricks: %d\n", 208 + err); 209 + } 210 + } 211 + 212 + mutex_unlock(&device->mipi->lock); 213 + 273 214 platform_device_put(device->pdev); 274 215 kfree(device); 275 216 } ··· 324 227 325 228 mutex_lock(&device->mipi->lock); 326 229 327 - value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0); 328 - value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP; 329 - 330 - if (soc->needs_vclamp_ref) 331 - value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF; 332 - 333 - tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0); 334 - 335 230 value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) | 336 231 MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref); 337 232 tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG1); 338 - 339 - value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2); 340 - value &= ~MIPI_CAL_BIAS_PAD_PDVREG; 341 - tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2); 342 233 343 234 value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2); 344 235 value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); ··· 511 426 return -ENOMEM; 512 427 513 428 mipi->soc = match->data; 429 + mipi->dev = &pdev->dev; 514 430 515 431 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 516 432 mipi->regs = devm_ioremap_resource(&pdev->dev, res);