diff options
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 72 |
1 files changed, 61 insertions, 11 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 0e2b8fd24df1..92ea0ab44ce2 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
@@ -33,6 +33,8 @@ struct sh_mobile_lcdc_chan { | |||
33 | struct fb_info info; | 33 | struct fb_info info; |
34 | dma_addr_t dma_handle; | 34 | dma_addr_t dma_handle; |
35 | struct fb_deferred_io defio; | 35 | struct fb_deferred_io defio; |
36 | unsigned long frame_end; | ||
37 | wait_queue_head_t frame_end_wait; | ||
36 | }; | 38 | }; |
37 | 39 | ||
38 | struct sh_mobile_lcdc_priv { | 40 | struct sh_mobile_lcdc_priv { |
@@ -226,7 +228,10 @@ static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) | |||
226 | static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) | 228 | static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) |
227 | { | 229 | { |
228 | struct sh_mobile_lcdc_priv *priv = data; | 230 | struct sh_mobile_lcdc_priv *priv = data; |
231 | struct sh_mobile_lcdc_chan *ch; | ||
229 | unsigned long tmp; | 232 | unsigned long tmp; |
233 | int is_sub; | ||
234 | int k; | ||
230 | 235 | ||
231 | /* acknowledge interrupt */ | 236 | /* acknowledge interrupt */ |
232 | tmp = lcdc_read(priv, _LDINTR); | 237 | tmp = lcdc_read(priv, _LDINTR); |
@@ -234,8 +239,24 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) | |||
234 | tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */ | 239 | tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */ |
235 | lcdc_write(priv, _LDINTR, tmp); | 240 | lcdc_write(priv, _LDINTR, tmp); |
236 | 241 | ||
237 | /* disable clocks */ | 242 | /* figure out if this interrupt is for main or sub lcd */ |
238 | sh_mobile_lcdc_clk_off(priv); | 243 | is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0; |
244 | |||
245 | /* wake up channel and disable clocks*/ | ||
246 | for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { | ||
247 | ch = &priv->ch[k]; | ||
248 | |||
249 | if (!ch->enabled) | ||
250 | continue; | ||
251 | |||
252 | if (is_sub == lcdc_chan_is_sublcd(ch)) { | ||
253 | ch->frame_end = 1; | ||
254 | wake_up(&ch->frame_end_wait); | ||
255 | |||
256 | sh_mobile_lcdc_clk_off(priv); | ||
257 | } | ||
258 | } | ||
259 | |||
239 | return IRQ_HANDLED; | 260 | return IRQ_HANDLED; |
240 | } | 261 | } |
241 | 262 | ||
@@ -446,22 +467,29 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
446 | { | 467 | { |
447 | struct sh_mobile_lcdc_chan *ch; | 468 | struct sh_mobile_lcdc_chan *ch; |
448 | struct sh_mobile_lcdc_board_cfg *board_cfg; | 469 | struct sh_mobile_lcdc_board_cfg *board_cfg; |
449 | unsigned long tmp; | ||
450 | int k; | 470 | int k; |
451 | 471 | ||
452 | /* tell the board code to disable the panel */ | 472 | /* clean up deferred io and ask board code to disable panel */ |
453 | for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { | 473 | for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { |
454 | ch = &priv->ch[k]; | 474 | ch = &priv->ch[k]; |
455 | board_cfg = &ch->cfg.board_cfg; | ||
456 | if (board_cfg->display_off) | ||
457 | board_cfg->display_off(board_cfg->board_data); | ||
458 | 475 | ||
459 | /* cleanup deferred io if SYS bus */ | 476 | /* deferred io mode: |
460 | tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; | 477 | * flush frame, and wait for frame end interrupt |
461 | if (ch->ldmt1r_value & (1 << 12) && tmp) { | 478 | * clean up deferred io and enable clock |
479 | */ | ||
480 | if (ch->info.fbdefio) { | ||
481 | ch->frame_end = 0; | ||
482 | schedule_delayed_work(&ch->info.deferred_work, 0); | ||
483 | wait_event(ch->frame_end_wait, ch->frame_end); | ||
462 | fb_deferred_io_cleanup(&ch->info); | 484 | fb_deferred_io_cleanup(&ch->info); |
463 | ch->info.fbdefio = NULL; | 485 | ch->info.fbdefio = NULL; |
486 | sh_mobile_lcdc_clk_on(priv); | ||
464 | } | 487 | } |
488 | |||
489 | board_cfg = &ch->cfg.board_cfg; | ||
490 | if (board_cfg->display_off) | ||
491 | board_cfg->display_off(board_cfg->board_data); | ||
492 | |||
465 | } | 493 | } |
466 | 494 | ||
467 | /* stop the lcdc */ | 495 | /* stop the lcdc */ |
@@ -654,6 +682,26 @@ static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) | |||
654 | return 0; | 682 | return 0; |
655 | } | 683 | } |
656 | 684 | ||
685 | static int sh_mobile_lcdc_suspend(struct device *dev) | ||
686 | { | ||
687 | struct platform_device *pdev = to_platform_device(dev); | ||
688 | |||
689 | sh_mobile_lcdc_stop(platform_get_drvdata(pdev)); | ||
690 | return 0; | ||
691 | } | ||
692 | |||
693 | static int sh_mobile_lcdc_resume(struct device *dev) | ||
694 | { | ||
695 | struct platform_device *pdev = to_platform_device(dev); | ||
696 | |||
697 | return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); | ||
698 | } | ||
699 | |||
700 | static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { | ||
701 | .suspend = sh_mobile_lcdc_suspend, | ||
702 | .resume = sh_mobile_lcdc_resume, | ||
703 | }; | ||
704 | |||
657 | static int sh_mobile_lcdc_remove(struct platform_device *pdev); | 705 | static int sh_mobile_lcdc_remove(struct platform_device *pdev); |
658 | 706 | ||
659 | static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) | 707 | static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) |
@@ -689,7 +737,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
689 | } | 737 | } |
690 | 738 | ||
691 | error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED, | 739 | error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED, |
692 | pdev->dev.bus_id, priv); | 740 | dev_name(&pdev->dev), priv); |
693 | if (error) { | 741 | if (error) { |
694 | dev_err(&pdev->dev, "unable to request irq\n"); | 742 | dev_err(&pdev->dev, "unable to request irq\n"); |
695 | goto err1; | 743 | goto err1; |
@@ -709,6 +757,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
709 | dev_err(&pdev->dev, "unsupported interface type\n"); | 757 | dev_err(&pdev->dev, "unsupported interface type\n"); |
710 | goto err1; | 758 | goto err1; |
711 | } | 759 | } |
760 | init_waitqueue_head(&priv->ch[i].frame_end_wait); | ||
712 | 761 | ||
713 | switch (pdata->ch[i].chan) { | 762 | switch (pdata->ch[i].chan) { |
714 | case LCDC_CHAN_MAINLCD: | 763 | case LCDC_CHAN_MAINLCD: |
@@ -862,6 +911,7 @@ static struct platform_driver sh_mobile_lcdc_driver = { | |||
862 | .driver = { | 911 | .driver = { |
863 | .name = "sh_mobile_lcdc_fb", | 912 | .name = "sh_mobile_lcdc_fb", |
864 | .owner = THIS_MODULE, | 913 | .owner = THIS_MODULE, |
914 | .pm = &sh_mobile_lcdc_dev_pm_ops, | ||
865 | }, | 915 | }, |
866 | .probe = sh_mobile_lcdc_probe, | 916 | .probe = sh_mobile_lcdc_probe, |
867 | .remove = sh_mobile_lcdc_remove, | 917 | .remove = sh_mobile_lcdc_remove, |