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

drm/tegra: Allocate resources at probe time

Since the .init() and .exit() functions are executed whenever the DRM
driver is loaded or unloaded, care must be taken not to use them for
resource allocation. Otherwise deferred probing cannot be used, since
the .init() and .exit() are not run at probe time. Similarly the code
that frees resources must be run at .remove() time. If it is run from
the .exit() function, it can release resources multiple times.

To handle this more consistently, rename the tegra_output_parse_dt()
function to tegra_output_probe() and introduce tegra_output_remove()
which can be used to free output-related resources.

Signed-off-by: Thierry Reding <treding@nvidia.com>

+56 -27
+6
drivers/gpu/drm/tegra/dc.c
··· 1191 1191 return err; 1192 1192 } 1193 1193 1194 + err = tegra_dc_rgb_remove(dc); 1195 + if (err < 0) { 1196 + dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err); 1197 + return err; 1198 + } 1199 + 1194 1200 clk_disable_unprepare(dc->clk); 1195 1201 1196 1202 return 0;
+3 -1
drivers/gpu/drm/tegra/drm.h
··· 236 236 237 237 /* from rgb.c */ 238 238 extern int tegra_dc_rgb_probe(struct tegra_dc *dc); 239 + extern int tegra_dc_rgb_remove(struct tegra_dc *dc); 239 240 extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc); 240 241 extern int tegra_dc_rgb_exit(struct tegra_dc *dc); 241 242 242 243 /* from output.c */ 243 - extern int tegra_output_parse_dt(struct tegra_output *output); 244 + extern int tegra_output_probe(struct tegra_output *output); 245 + extern int tegra_output_remove(struct tegra_output *output); 244 246 extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output); 245 247 extern int tegra_output_exit(struct tegra_output *output); 246 248
+7 -1
drivers/gpu/drm/tegra/hdmi.c
··· 1226 1226 1227 1227 hdmi->output.dev = &pdev->dev; 1228 1228 1229 - err = tegra_output_parse_dt(&hdmi->output); 1229 + err = tegra_output_probe(&hdmi->output); 1230 1230 if (err < 0) 1231 1231 return err; 1232 1232 ··· 1269 1269 if (err < 0) { 1270 1270 dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 1271 1271 err); 1272 + return err; 1273 + } 1274 + 1275 + err = tegra_output_remove(&hdmi->output); 1276 + if (err < 0) { 1277 + dev_err(&pdev->dev, "failed to remove output: %d\n", err); 1272 1278 return err; 1273 1279 } 1274 1280
+25 -24
drivers/gpu/drm/tegra/output.c
··· 161 161 return IRQ_HANDLED; 162 162 } 163 163 164 - int tegra_output_parse_dt(struct tegra_output *output) 164 + int tegra_output_probe(struct tegra_output *output) 165 165 { 166 166 enum of_gpio_flags flags; 167 167 struct device_node *ddc; ··· 191 191 output->hpd_gpio = of_get_named_gpio_flags(output->of_node, 192 192 "nvidia,hpd-gpio", 0, 193 193 &flags); 194 - 195 - return 0; 196 - } 197 - 198 - int tegra_output_init(struct drm_device *drm, struct tegra_output *output) 199 - { 200 - int connector, encoder, err; 201 - 202 194 if (gpio_is_valid(output->hpd_gpio)) { 203 195 unsigned long flags; 204 196 ··· 204 212 err = gpio_to_irq(output->hpd_gpio); 205 213 if (err < 0) { 206 214 dev_err(output->dev, "gpio_to_irq(): %d\n", err); 207 - goto free_hpd; 215 + gpio_free(output->hpd_gpio); 216 + return err; 208 217 } 209 218 210 219 output->hpd_irq = err; ··· 218 225 if (err < 0) { 219 226 dev_err(output->dev, "failed to request IRQ#%u: %d\n", 220 227 output->hpd_irq, err); 221 - goto free_hpd; 228 + gpio_free(output->hpd_gpio); 229 + return err; 222 230 } 223 231 224 232 output->connector.polled = DRM_CONNECTOR_POLL_HPD; 225 233 } 234 + 235 + return 0; 236 + } 237 + 238 + int tegra_output_remove(struct tegra_output *output) 239 + { 240 + if (gpio_is_valid(output->hpd_gpio)) { 241 + free_irq(output->hpd_irq, output); 242 + gpio_free(output->hpd_gpio); 243 + } 244 + 245 + if (output->ddc) 246 + put_device(&output->ddc->dev); 247 + 248 + return 0; 249 + } 250 + 251 + int tegra_output_init(struct drm_device *drm, struct tegra_output *output) 252 + { 253 + int connector, encoder; 226 254 227 255 switch (output->type) { 228 256 case TEGRA_OUTPUT_RGB: ··· 275 261 output->encoder.possible_crtcs = 0x3; 276 262 277 263 return 0; 278 - 279 - free_hpd: 280 - gpio_free(output->hpd_gpio); 281 - 282 - return err; 283 264 } 284 265 285 266 int tegra_output_exit(struct tegra_output *output) 286 267 { 287 - if (gpio_is_valid(output->hpd_gpio)) { 288 - free_irq(output->hpd_irq, output); 289 - gpio_free(output->hpd_gpio); 290 - } 291 - 292 - if (output->ddc) 293 - put_device(&output->ddc->dev); 294 - 295 268 return 0; 296 269 }
+15 -1
drivers/gpu/drm/tegra/rgb.c
··· 147 147 rgb->output.dev = dc->dev; 148 148 rgb->output.of_node = np; 149 149 150 - err = tegra_output_parse_dt(&rgb->output); 150 + err = tegra_output_probe(&rgb->output); 151 151 if (err < 0) 152 152 return err; 153 153 ··· 170 170 } 171 171 172 172 dc->rgb = &rgb->output; 173 + 174 + return 0; 175 + } 176 + 177 + int tegra_dc_rgb_remove(struct tegra_dc *dc) 178 + { 179 + int err; 180 + 181 + if (!dc->rgb) 182 + return 0; 183 + 184 + err = tegra_output_remove(dc->rgb); 185 + if (err < 0) 186 + return err; 173 187 174 188 return 0; 175 189 }