diff options
Diffstat (limited to 'drivers/gpu/drm/tilcdc')
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.c | 61 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_panel.c | 74 |
2 files changed, 108 insertions, 27 deletions
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 6be623b4a86f..79a34cbd29f5 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c | |||
@@ -84,6 +84,7 @@ static int modeset_init(struct drm_device *dev) | |||
84 | if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) { | 84 | if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) { |
85 | /* oh nos! */ | 85 | /* oh nos! */ |
86 | dev_err(dev->dev, "no encoders/connectors found\n"); | 86 | dev_err(dev->dev, "no encoders/connectors found\n"); |
87 | drm_mode_config_cleanup(dev); | ||
87 | return -ENXIO; | 88 | return -ENXIO; |
88 | } | 89 | } |
89 | 90 | ||
@@ -172,33 +173,37 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
172 | dev->dev_private = priv; | 173 | dev->dev_private = priv; |
173 | 174 | ||
174 | priv->wq = alloc_ordered_workqueue("tilcdc", 0); | 175 | priv->wq = alloc_ordered_workqueue("tilcdc", 0); |
176 | if (!priv->wq) { | ||
177 | ret = -ENOMEM; | ||
178 | goto fail_free_priv; | ||
179 | } | ||
175 | 180 | ||
176 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 181 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
177 | if (!res) { | 182 | if (!res) { |
178 | dev_err(dev->dev, "failed to get memory resource\n"); | 183 | dev_err(dev->dev, "failed to get memory resource\n"); |
179 | ret = -EINVAL; | 184 | ret = -EINVAL; |
180 | goto fail; | 185 | goto fail_free_wq; |
181 | } | 186 | } |
182 | 187 | ||
183 | priv->mmio = ioremap_nocache(res->start, resource_size(res)); | 188 | priv->mmio = ioremap_nocache(res->start, resource_size(res)); |
184 | if (!priv->mmio) { | 189 | if (!priv->mmio) { |
185 | dev_err(dev->dev, "failed to ioremap\n"); | 190 | dev_err(dev->dev, "failed to ioremap\n"); |
186 | ret = -ENOMEM; | 191 | ret = -ENOMEM; |
187 | goto fail; | 192 | goto fail_free_wq; |
188 | } | 193 | } |
189 | 194 | ||
190 | priv->clk = clk_get(dev->dev, "fck"); | 195 | priv->clk = clk_get(dev->dev, "fck"); |
191 | if (IS_ERR(priv->clk)) { | 196 | if (IS_ERR(priv->clk)) { |
192 | dev_err(dev->dev, "failed to get functional clock\n"); | 197 | dev_err(dev->dev, "failed to get functional clock\n"); |
193 | ret = -ENODEV; | 198 | ret = -ENODEV; |
194 | goto fail; | 199 | goto fail_iounmap; |
195 | } | 200 | } |
196 | 201 | ||
197 | priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); | 202 | priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); |
198 | if (IS_ERR(priv->clk)) { | 203 | if (IS_ERR(priv->clk)) { |
199 | dev_err(dev->dev, "failed to get display clock\n"); | 204 | dev_err(dev->dev, "failed to get display clock\n"); |
200 | ret = -ENODEV; | 205 | ret = -ENODEV; |
201 | goto fail; | 206 | goto fail_put_clk; |
202 | } | 207 | } |
203 | 208 | ||
204 | #ifdef CONFIG_CPU_FREQ | 209 | #ifdef CONFIG_CPU_FREQ |
@@ -208,7 +213,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
208 | CPUFREQ_TRANSITION_NOTIFIER); | 213 | CPUFREQ_TRANSITION_NOTIFIER); |
209 | if (ret) { | 214 | if (ret) { |
210 | dev_err(dev->dev, "failed to register cpufreq notifier\n"); | 215 | dev_err(dev->dev, "failed to register cpufreq notifier\n"); |
211 | goto fail; | 216 | goto fail_put_disp_clk; |
212 | } | 217 | } |
213 | #endif | 218 | #endif |
214 | 219 | ||
@@ -253,13 +258,13 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
253 | ret = modeset_init(dev); | 258 | ret = modeset_init(dev); |
254 | if (ret < 0) { | 259 | if (ret < 0) { |
255 | dev_err(dev->dev, "failed to initialize mode setting\n"); | 260 | dev_err(dev->dev, "failed to initialize mode setting\n"); |
256 | goto fail; | 261 | goto fail_cpufreq_unregister; |
257 | } | 262 | } |
258 | 263 | ||
259 | ret = drm_vblank_init(dev, 1); | 264 | ret = drm_vblank_init(dev, 1); |
260 | if (ret < 0) { | 265 | if (ret < 0) { |
261 | dev_err(dev->dev, "failed to initialize vblank\n"); | 266 | dev_err(dev->dev, "failed to initialize vblank\n"); |
262 | goto fail; | 267 | goto fail_mode_config_cleanup; |
263 | } | 268 | } |
264 | 269 | ||
265 | pm_runtime_get_sync(dev->dev); | 270 | pm_runtime_get_sync(dev->dev); |
@@ -267,7 +272,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
267 | pm_runtime_put_sync(dev->dev); | 272 | pm_runtime_put_sync(dev->dev); |
268 | if (ret < 0) { | 273 | if (ret < 0) { |
269 | dev_err(dev->dev, "failed to install IRQ handler\n"); | 274 | dev_err(dev->dev, "failed to install IRQ handler\n"); |
270 | goto fail; | 275 | goto fail_vblank_cleanup; |
271 | } | 276 | } |
272 | 277 | ||
273 | platform_set_drvdata(pdev, dev); | 278 | platform_set_drvdata(pdev, dev); |
@@ -283,13 +288,48 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
283 | priv->fbdev = drm_fbdev_cma_init(dev, bpp, | 288 | priv->fbdev = drm_fbdev_cma_init(dev, bpp, |
284 | dev->mode_config.num_crtc, | 289 | dev->mode_config.num_crtc, |
285 | dev->mode_config.num_connector); | 290 | dev->mode_config.num_connector); |
291 | if (IS_ERR(priv->fbdev)) { | ||
292 | ret = PTR_ERR(priv->fbdev); | ||
293 | goto fail_irq_uninstall; | ||
294 | } | ||
286 | 295 | ||
287 | drm_kms_helper_poll_init(dev); | 296 | drm_kms_helper_poll_init(dev); |
288 | 297 | ||
289 | return 0; | 298 | return 0; |
290 | 299 | ||
291 | fail: | 300 | fail_irq_uninstall: |
292 | tilcdc_unload(dev); | 301 | pm_runtime_get_sync(dev->dev); |
302 | drm_irq_uninstall(dev); | ||
303 | pm_runtime_put_sync(dev->dev); | ||
304 | |||
305 | fail_vblank_cleanup: | ||
306 | drm_vblank_cleanup(dev); | ||
307 | |||
308 | fail_mode_config_cleanup: | ||
309 | drm_mode_config_cleanup(dev); | ||
310 | |||
311 | fail_cpufreq_unregister: | ||
312 | pm_runtime_disable(dev->dev); | ||
313 | #ifdef CONFIG_CPU_FREQ | ||
314 | cpufreq_unregister_notifier(&priv->freq_transition, | ||
315 | CPUFREQ_TRANSITION_NOTIFIER); | ||
316 | fail_put_disp_clk: | ||
317 | clk_put(priv->disp_clk); | ||
318 | #endif | ||
319 | |||
320 | fail_put_clk: | ||
321 | clk_put(priv->clk); | ||
322 | |||
323 | fail_iounmap: | ||
324 | iounmap(priv->mmio); | ||
325 | |||
326 | fail_free_wq: | ||
327 | flush_workqueue(priv->wq); | ||
328 | destroy_workqueue(priv->wq); | ||
329 | |||
330 | fail_free_priv: | ||
331 | dev->dev_private = NULL; | ||
332 | kfree(priv); | ||
293 | return ret; | 333 | return ret; |
294 | } | 334 | } |
295 | 335 | ||
@@ -502,6 +542,7 @@ static struct drm_driver tilcdc_driver = { | |||
502 | .unload = tilcdc_unload, | 542 | .unload = tilcdc_unload, |
503 | .preclose = tilcdc_preclose, | 543 | .preclose = tilcdc_preclose, |
504 | .lastclose = tilcdc_lastclose, | 544 | .lastclose = tilcdc_lastclose, |
545 | .set_busid = drm_platform_set_busid, | ||
505 | .irq_handler = tilcdc_irq, | 546 | .irq_handler = tilcdc_irq, |
506 | .irq_preinstall = tilcdc_irq_preinstall, | 547 | .irq_preinstall = tilcdc_irq_preinstall, |
507 | .irq_postinstall = tilcdc_irq_postinstall, | 548 | .irq_postinstall = tilcdc_irq_postinstall, |
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 4c7aa1d8134f..7a0315855e90 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/pinctrl/pinmux.h> | 18 | #include <linux/pinctrl/pinmux.h> |
19 | #include <linux/pinctrl/consumer.h> | 19 | #include <linux/pinctrl/consumer.h> |
20 | #include <linux/backlight.h> | 20 | #include <linux/backlight.h> |
21 | #include <linux/gpio/consumer.h> | ||
21 | #include <video/display_timing.h> | 22 | #include <video/display_timing.h> |
22 | #include <video/of_display_timing.h> | 23 | #include <video/of_display_timing.h> |
23 | #include <video/videomode.h> | 24 | #include <video/videomode.h> |
@@ -29,6 +30,7 @@ struct panel_module { | |||
29 | struct tilcdc_panel_info *info; | 30 | struct tilcdc_panel_info *info; |
30 | struct display_timings *timings; | 31 | struct display_timings *timings; |
31 | struct backlight_device *backlight; | 32 | struct backlight_device *backlight; |
33 | struct gpio_desc *enable_gpio; | ||
32 | }; | 34 | }; |
33 | #define to_panel_module(x) container_of(x, struct panel_module, base) | 35 | #define to_panel_module(x) container_of(x, struct panel_module, base) |
34 | 36 | ||
@@ -55,13 +57,17 @@ static void panel_encoder_dpms(struct drm_encoder *encoder, int mode) | |||
55 | { | 57 | { |
56 | struct panel_encoder *panel_encoder = to_panel_encoder(encoder); | 58 | struct panel_encoder *panel_encoder = to_panel_encoder(encoder); |
57 | struct backlight_device *backlight = panel_encoder->mod->backlight; | 59 | struct backlight_device *backlight = panel_encoder->mod->backlight; |
60 | struct gpio_desc *gpio = panel_encoder->mod->enable_gpio; | ||
58 | 61 | ||
59 | if (!backlight) | 62 | if (backlight) { |
60 | return; | 63 | backlight->props.power = mode == DRM_MODE_DPMS_ON ? |
64 | FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | ||
65 | backlight_update_status(backlight); | ||
66 | } | ||
61 | 67 | ||
62 | backlight->props.power = mode == DRM_MODE_DPMS_ON | 68 | if (gpio) |
63 | ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | 69 | gpiod_set_value_cansleep(gpio, |
64 | backlight_update_status(backlight); | 70 | mode == DRM_MODE_DPMS_ON ? 1 : 0); |
65 | } | 71 | } |
66 | 72 | ||
67 | static bool panel_encoder_mode_fixup(struct drm_encoder *encoder, | 73 | static bool panel_encoder_mode_fixup(struct drm_encoder *encoder, |
@@ -311,6 +317,7 @@ static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np) | |||
311 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 317 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
312 | if (!info) { | 318 | if (!info) { |
313 | pr_err("%s: allocation failed\n", __func__); | 319 | pr_err("%s: allocation failed\n", __func__); |
320 | of_node_put(info_np); | ||
314 | return NULL; | 321 | return NULL; |
315 | } | 322 | } |
316 | 323 | ||
@@ -331,22 +338,21 @@ static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np) | |||
331 | if (ret) { | 338 | if (ret) { |
332 | pr_err("%s: error reading panel-info properties\n", __func__); | 339 | pr_err("%s: error reading panel-info properties\n", __func__); |
333 | kfree(info); | 340 | kfree(info); |
341 | of_node_put(info_np); | ||
334 | return NULL; | 342 | return NULL; |
335 | } | 343 | } |
344 | of_node_put(info_np); | ||
336 | 345 | ||
337 | return info; | 346 | return info; |
338 | } | 347 | } |
339 | 348 | ||
340 | static struct of_device_id panel_of_match[]; | ||
341 | |||
342 | static int panel_probe(struct platform_device *pdev) | 349 | static int panel_probe(struct platform_device *pdev) |
343 | { | 350 | { |
344 | struct device_node *node = pdev->dev.of_node; | 351 | struct device_node *bl_node, *node = pdev->dev.of_node; |
345 | struct panel_module *panel_mod; | 352 | struct panel_module *panel_mod; |
346 | struct tilcdc_module *mod; | 353 | struct tilcdc_module *mod; |
347 | struct pinctrl *pinctrl; | 354 | struct pinctrl *pinctrl; |
348 | int ret = -EINVAL; | 355 | int ret; |
349 | |||
350 | 356 | ||
351 | /* bail out early if no DT data: */ | 357 | /* bail out early if no DT data: */ |
352 | if (!node) { | 358 | if (!node) { |
@@ -354,10 +360,40 @@ static int panel_probe(struct platform_device *pdev) | |||
354 | return -ENXIO; | 360 | return -ENXIO; |
355 | } | 361 | } |
356 | 362 | ||
357 | panel_mod = kzalloc(sizeof(*panel_mod), GFP_KERNEL); | 363 | panel_mod = devm_kzalloc(&pdev->dev, sizeof(*panel_mod), GFP_KERNEL); |
358 | if (!panel_mod) | 364 | if (!panel_mod) |
359 | return -ENOMEM; | 365 | return -ENOMEM; |
360 | 366 | ||
367 | bl_node = of_parse_phandle(node, "backlight", 0); | ||
368 | if (bl_node) { | ||
369 | panel_mod->backlight = of_find_backlight_by_node(bl_node); | ||
370 | of_node_put(bl_node); | ||
371 | |||
372 | if (!panel_mod->backlight) | ||
373 | return -EPROBE_DEFER; | ||
374 | |||
375 | dev_info(&pdev->dev, "found backlight\n"); | ||
376 | } | ||
377 | |||
378 | panel_mod->enable_gpio = devm_gpiod_get(&pdev->dev, "enable"); | ||
379 | if (IS_ERR(panel_mod->enable_gpio)) { | ||
380 | ret = PTR_ERR(panel_mod->enable_gpio); | ||
381 | if (ret != -ENOENT) { | ||
382 | dev_err(&pdev->dev, "failed to request enable GPIO\n"); | ||
383 | goto fail_backlight; | ||
384 | } | ||
385 | |||
386 | /* Optional GPIO is not here, continue silently. */ | ||
387 | panel_mod->enable_gpio = NULL; | ||
388 | } else { | ||
389 | ret = gpiod_direction_output(panel_mod->enable_gpio, 0); | ||
390 | if (ret < 0) { | ||
391 | dev_err(&pdev->dev, "failed to setup GPIO\n"); | ||
392 | goto fail_backlight; | ||
393 | } | ||
394 | dev_info(&pdev->dev, "found enable GPIO\n"); | ||
395 | } | ||
396 | |||
361 | mod = &panel_mod->base; | 397 | mod = &panel_mod->base; |
362 | pdev->dev.platform_data = mod; | 398 | pdev->dev.platform_data = mod; |
363 | 399 | ||
@@ -370,29 +406,30 @@ static int panel_probe(struct platform_device *pdev) | |||
370 | panel_mod->timings = of_get_display_timings(node); | 406 | panel_mod->timings = of_get_display_timings(node); |
371 | if (!panel_mod->timings) { | 407 | if (!panel_mod->timings) { |
372 | dev_err(&pdev->dev, "could not get panel timings\n"); | 408 | dev_err(&pdev->dev, "could not get panel timings\n"); |
409 | ret = -EINVAL; | ||
373 | goto fail_free; | 410 | goto fail_free; |
374 | } | 411 | } |
375 | 412 | ||
376 | panel_mod->info = of_get_panel_info(node); | 413 | panel_mod->info = of_get_panel_info(node); |
377 | if (!panel_mod->info) { | 414 | if (!panel_mod->info) { |
378 | dev_err(&pdev->dev, "could not get panel info\n"); | 415 | dev_err(&pdev->dev, "could not get panel info\n"); |
416 | ret = -EINVAL; | ||
379 | goto fail_timings; | 417 | goto fail_timings; |
380 | } | 418 | } |
381 | 419 | ||
382 | mod->preferred_bpp = panel_mod->info->bpp; | 420 | mod->preferred_bpp = panel_mod->info->bpp; |
383 | 421 | ||
384 | panel_mod->backlight = of_find_backlight_by_node(node); | ||
385 | if (panel_mod->backlight) | ||
386 | dev_info(&pdev->dev, "found backlight\n"); | ||
387 | |||
388 | return 0; | 422 | return 0; |
389 | 423 | ||
390 | fail_timings: | 424 | fail_timings: |
391 | display_timings_release(panel_mod->timings); | 425 | display_timings_release(panel_mod->timings); |
392 | 426 | ||
393 | fail_free: | 427 | fail_free: |
394 | kfree(panel_mod); | ||
395 | tilcdc_module_cleanup(mod); | 428 | tilcdc_module_cleanup(mod); |
429 | |||
430 | fail_backlight: | ||
431 | if (panel_mod->backlight) | ||
432 | put_device(&panel_mod->backlight->dev); | ||
396 | return ret; | 433 | return ret; |
397 | } | 434 | } |
398 | 435 | ||
@@ -400,12 +437,15 @@ static int panel_remove(struct platform_device *pdev) | |||
400 | { | 437 | { |
401 | struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); | 438 | struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); |
402 | struct panel_module *panel_mod = to_panel_module(mod); | 439 | struct panel_module *panel_mod = to_panel_module(mod); |
440 | struct backlight_device *backlight = panel_mod->backlight; | ||
441 | |||
442 | if (backlight) | ||
443 | put_device(&backlight->dev); | ||
403 | 444 | ||
404 | display_timings_release(panel_mod->timings); | 445 | display_timings_release(panel_mod->timings); |
405 | 446 | ||
406 | tilcdc_module_cleanup(mod); | 447 | tilcdc_module_cleanup(mod); |
407 | kfree(panel_mod->info); | 448 | kfree(panel_mod->info); |
408 | kfree(panel_mod); | ||
409 | 449 | ||
410 | return 0; | 450 | return 0; |
411 | } | 451 | } |