diff options
Diffstat (limited to 'drivers/video/omap2/dss/venc.c')
-rw-r--r-- | drivers/video/omap2/dss/venc.c | 183 |
1 files changed, 146 insertions, 37 deletions
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 980f919ed987..173c66430dad 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c | |||
@@ -33,11 +33,13 @@ | |||
33 | #include <linux/seq_file.h> | 33 | #include <linux/seq_file.h> |
34 | #include <linux/platform_device.h> | 34 | #include <linux/platform_device.h> |
35 | #include <linux/regulator/consumer.h> | 35 | #include <linux/regulator/consumer.h> |
36 | #include <linux/pm_runtime.h> | ||
36 | 37 | ||
37 | #include <video/omapdss.h> | 38 | #include <video/omapdss.h> |
38 | #include <plat/cpu.h> | 39 | #include <plat/cpu.h> |
39 | 40 | ||
40 | #include "dss.h" | 41 | #include "dss.h" |
42 | #include "dss_features.h" | ||
41 | 43 | ||
42 | /* Venc registers */ | 44 | /* Venc registers */ |
43 | #define VENC_REV_ID 0x00 | 45 | #define VENC_REV_ID 0x00 |
@@ -292,6 +294,9 @@ static struct { | |||
292 | struct mutex venc_lock; | 294 | struct mutex venc_lock; |
293 | u32 wss_data; | 295 | u32 wss_data; |
294 | struct regulator *vdda_dac_reg; | 296 | struct regulator *vdda_dac_reg; |
297 | |||
298 | struct clk *tv_clk; | ||
299 | struct clk *tv_dac_clk; | ||
295 | } venc; | 300 | } venc; |
296 | 301 | ||
297 | static inline void venc_write_reg(int idx, u32 val) | 302 | static inline void venc_write_reg(int idx, u32 val) |
@@ -380,14 +385,25 @@ static void venc_reset(void) | |||
380 | #endif | 385 | #endif |
381 | } | 386 | } |
382 | 387 | ||
383 | static void venc_enable_clocks(int enable) | 388 | static int venc_runtime_get(void) |
389 | { | ||
390 | int r; | ||
391 | |||
392 | DSSDBG("venc_runtime_get\n"); | ||
393 | |||
394 | r = pm_runtime_get_sync(&venc.pdev->dev); | ||
395 | WARN_ON(r < 0); | ||
396 | return r < 0 ? r : 0; | ||
397 | } | ||
398 | |||
399 | static void venc_runtime_put(void) | ||
384 | { | 400 | { |
385 | if (enable) | 401 | int r; |
386 | dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK | | 402 | |
387 | DSS_CLK_VIDFCK); | 403 | DSSDBG("venc_runtime_put\n"); |
388 | else | 404 | |
389 | dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK | | 405 | r = pm_runtime_put(&venc.pdev->dev); |
390 | DSS_CLK_VIDFCK); | 406 | WARN_ON(r < 0); |
391 | } | 407 | } |
392 | 408 | ||
393 | static const struct venc_config *venc_timings_to_config( | 409 | static const struct venc_config *venc_timings_to_config( |
@@ -406,8 +422,6 @@ static void venc_power_on(struct omap_dss_device *dssdev) | |||
406 | { | 422 | { |
407 | u32 l; | 423 | u32 l; |
408 | 424 | ||
409 | venc_enable_clocks(1); | ||
410 | |||
411 | venc_reset(); | 425 | venc_reset(); |
412 | venc_write_config(venc_timings_to_config(&dssdev->panel.timings)); | 426 | venc_write_config(venc_timings_to_config(&dssdev->panel.timings)); |
413 | 427 | ||
@@ -448,8 +462,6 @@ static void venc_power_off(struct omap_dss_device *dssdev) | |||
448 | dssdev->platform_disable(dssdev); | 462 | dssdev->platform_disable(dssdev); |
449 | 463 | ||
450 | regulator_disable(venc.vdda_dac_reg); | 464 | regulator_disable(venc.vdda_dac_reg); |
451 | |||
452 | venc_enable_clocks(0); | ||
453 | } | 465 | } |
454 | 466 | ||
455 | 467 | ||
@@ -487,6 +499,10 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) | |||
487 | goto err1; | 499 | goto err1; |
488 | } | 500 | } |
489 | 501 | ||
502 | r = venc_runtime_get(); | ||
503 | if (r) | ||
504 | goto err1; | ||
505 | |||
490 | venc_power_on(dssdev); | 506 | venc_power_on(dssdev); |
491 | 507 | ||
492 | venc.wss_data = 0; | 508 | venc.wss_data = 0; |
@@ -520,6 +536,8 @@ static void venc_panel_disable(struct omap_dss_device *dssdev) | |||
520 | 536 | ||
521 | venc_power_off(dssdev); | 537 | venc_power_off(dssdev); |
522 | 538 | ||
539 | venc_runtime_put(); | ||
540 | |||
523 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | 541 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
524 | 542 | ||
525 | omap_dss_stop_device(dssdev); | 543 | omap_dss_stop_device(dssdev); |
@@ -538,20 +556,6 @@ static int venc_panel_resume(struct omap_dss_device *dssdev) | |||
538 | return venc_panel_enable(dssdev); | 556 | return venc_panel_enable(dssdev); |
539 | } | 557 | } |
540 | 558 | ||
541 | static enum omap_dss_update_mode venc_get_update_mode( | ||
542 | struct omap_dss_device *dssdev) | ||
543 | { | ||
544 | return OMAP_DSS_UPDATE_AUTO; | ||
545 | } | ||
546 | |||
547 | static int venc_set_update_mode(struct omap_dss_device *dssdev, | ||
548 | enum omap_dss_update_mode mode) | ||
549 | { | ||
550 | if (mode != OMAP_DSS_UPDATE_AUTO) | ||
551 | return -EINVAL; | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static void venc_get_timings(struct omap_dss_device *dssdev, | 559 | static void venc_get_timings(struct omap_dss_device *dssdev, |
556 | struct omap_video_timings *timings) | 560 | struct omap_video_timings *timings) |
557 | { | 561 | { |
@@ -598,6 +602,7 @@ static u32 venc_get_wss(struct omap_dss_device *dssdev) | |||
598 | static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) | 602 | static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) |
599 | { | 603 | { |
600 | const struct venc_config *config; | 604 | const struct venc_config *config; |
605 | int r; | ||
601 | 606 | ||
602 | DSSDBG("venc_set_wss\n"); | 607 | DSSDBG("venc_set_wss\n"); |
603 | 608 | ||
@@ -608,16 +613,19 @@ static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) | |||
608 | /* Invert due to VENC_L21_WC_CTL:INV=1 */ | 613 | /* Invert due to VENC_L21_WC_CTL:INV=1 */ |
609 | venc.wss_data = (wss ^ 0xfffff) << 8; | 614 | venc.wss_data = (wss ^ 0xfffff) << 8; |
610 | 615 | ||
611 | venc_enable_clocks(1); | 616 | r = venc_runtime_get(); |
617 | if (r) | ||
618 | goto err; | ||
612 | 619 | ||
613 | venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | | 620 | venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | |
614 | venc.wss_data); | 621 | venc.wss_data); |
615 | 622 | ||
616 | venc_enable_clocks(0); | 623 | venc_runtime_put(); |
617 | 624 | ||
625 | err: | ||
618 | mutex_unlock(&venc.venc_lock); | 626 | mutex_unlock(&venc.venc_lock); |
619 | 627 | ||
620 | return 0; | 628 | return r; |
621 | } | 629 | } |
622 | 630 | ||
623 | static struct omap_dss_driver venc_driver = { | 631 | static struct omap_dss_driver venc_driver = { |
@@ -632,9 +640,6 @@ static struct omap_dss_driver venc_driver = { | |||
632 | .get_resolution = omapdss_default_get_resolution, | 640 | .get_resolution = omapdss_default_get_resolution, |
633 | .get_recommended_bpp = omapdss_default_get_recommended_bpp, | 641 | .get_recommended_bpp = omapdss_default_get_recommended_bpp, |
634 | 642 | ||
635 | .set_update_mode = venc_set_update_mode, | ||
636 | .get_update_mode = venc_get_update_mode, | ||
637 | |||
638 | .get_timings = venc_get_timings, | 643 | .get_timings = venc_get_timings, |
639 | .set_timings = venc_set_timings, | 644 | .set_timings = venc_set_timings, |
640 | .check_timings = venc_check_timings, | 645 | .check_timings = venc_check_timings, |
@@ -673,7 +678,8 @@ void venc_dump_regs(struct seq_file *s) | |||
673 | { | 678 | { |
674 | #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) | 679 | #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) |
675 | 680 | ||
676 | venc_enable_clocks(1); | 681 | if (venc_runtime_get()) |
682 | return; | ||
677 | 683 | ||
678 | DUMPREG(VENC_F_CONTROL); | 684 | DUMPREG(VENC_F_CONTROL); |
679 | DUMPREG(VENC_VIDOUT_CTRL); | 685 | DUMPREG(VENC_VIDOUT_CTRL); |
@@ -717,16 +723,56 @@ void venc_dump_regs(struct seq_file *s) | |||
717 | DUMPREG(VENC_OUTPUT_CONTROL); | 723 | DUMPREG(VENC_OUTPUT_CONTROL); |
718 | DUMPREG(VENC_OUTPUT_TEST); | 724 | DUMPREG(VENC_OUTPUT_TEST); |
719 | 725 | ||
720 | venc_enable_clocks(0); | 726 | venc_runtime_put(); |
721 | 727 | ||
722 | #undef DUMPREG | 728 | #undef DUMPREG |
723 | } | 729 | } |
724 | 730 | ||
731 | static int venc_get_clocks(struct platform_device *pdev) | ||
732 | { | ||
733 | struct clk *clk; | ||
734 | |||
735 | clk = clk_get(&pdev->dev, "fck"); | ||
736 | if (IS_ERR(clk)) { | ||
737 | DSSERR("can't get fck\n"); | ||
738 | return PTR_ERR(clk); | ||
739 | } | ||
740 | |||
741 | venc.tv_clk = clk; | ||
742 | |||
743 | if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) { | ||
744 | if (cpu_is_omap34xx() || cpu_is_omap3630()) | ||
745 | clk = clk_get(&pdev->dev, "dss_96m_fck"); | ||
746 | else | ||
747 | clk = clk_get(&pdev->dev, "tv_dac_clk"); | ||
748 | if (IS_ERR(clk)) { | ||
749 | DSSERR("can't get tv_dac_clk\n"); | ||
750 | clk_put(venc.tv_clk); | ||
751 | return PTR_ERR(clk); | ||
752 | } | ||
753 | } else { | ||
754 | clk = NULL; | ||
755 | } | ||
756 | |||
757 | venc.tv_dac_clk = clk; | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static void venc_put_clocks(void) | ||
763 | { | ||
764 | if (venc.tv_clk) | ||
765 | clk_put(venc.tv_clk); | ||
766 | if (venc.tv_dac_clk) | ||
767 | clk_put(venc.tv_dac_clk); | ||
768 | } | ||
769 | |||
725 | /* VENC HW IP initialisation */ | 770 | /* VENC HW IP initialisation */ |
726 | static int omap_venchw_probe(struct platform_device *pdev) | 771 | static int omap_venchw_probe(struct platform_device *pdev) |
727 | { | 772 | { |
728 | u8 rev_id; | 773 | u8 rev_id; |
729 | struct resource *venc_mem; | 774 | struct resource *venc_mem; |
775 | int r; | ||
730 | 776 | ||
731 | venc.pdev = pdev; | 777 | venc.pdev = pdev; |
732 | 778 | ||
@@ -737,22 +783,40 @@ static int omap_venchw_probe(struct platform_device *pdev) | |||
737 | venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); | 783 | venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); |
738 | if (!venc_mem) { | 784 | if (!venc_mem) { |
739 | DSSERR("can't get IORESOURCE_MEM VENC\n"); | 785 | DSSERR("can't get IORESOURCE_MEM VENC\n"); |
740 | return -EINVAL; | 786 | r = -EINVAL; |
787 | goto err_ioremap; | ||
741 | } | 788 | } |
742 | venc.base = ioremap(venc_mem->start, resource_size(venc_mem)); | 789 | venc.base = ioremap(venc_mem->start, resource_size(venc_mem)); |
743 | if (!venc.base) { | 790 | if (!venc.base) { |
744 | DSSERR("can't ioremap VENC\n"); | 791 | DSSERR("can't ioremap VENC\n"); |
745 | return -ENOMEM; | 792 | r = -ENOMEM; |
793 | goto err_ioremap; | ||
746 | } | 794 | } |
747 | 795 | ||
748 | venc_enable_clocks(1); | 796 | r = venc_get_clocks(pdev); |
797 | if (r) | ||
798 | goto err_get_clk; | ||
799 | |||
800 | pm_runtime_enable(&pdev->dev); | ||
801 | |||
802 | r = venc_runtime_get(); | ||
803 | if (r) | ||
804 | goto err_get_venc; | ||
749 | 805 | ||
750 | rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); | 806 | rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); |
751 | dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); | 807 | dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id); |
752 | 808 | ||
753 | venc_enable_clocks(0); | 809 | venc_runtime_put(); |
754 | 810 | ||
755 | return omap_dss_register_driver(&venc_driver); | 811 | return omap_dss_register_driver(&venc_driver); |
812 | |||
813 | err_get_venc: | ||
814 | pm_runtime_disable(&pdev->dev); | ||
815 | venc_put_clocks(); | ||
816 | err_get_clk: | ||
817 | iounmap(venc.base); | ||
818 | err_ioremap: | ||
819 | return r; | ||
756 | } | 820 | } |
757 | 821 | ||
758 | static int omap_venchw_remove(struct platform_device *pdev) | 822 | static int omap_venchw_remove(struct platform_device *pdev) |
@@ -763,16 +827,61 @@ static int omap_venchw_remove(struct platform_device *pdev) | |||
763 | } | 827 | } |
764 | omap_dss_unregister_driver(&venc_driver); | 828 | omap_dss_unregister_driver(&venc_driver); |
765 | 829 | ||
830 | pm_runtime_disable(&pdev->dev); | ||
831 | venc_put_clocks(); | ||
832 | |||
766 | iounmap(venc.base); | 833 | iounmap(venc.base); |
767 | return 0; | 834 | return 0; |
768 | } | 835 | } |
769 | 836 | ||
837 | static int venc_runtime_suspend(struct device *dev) | ||
838 | { | ||
839 | if (venc.tv_dac_clk) | ||
840 | clk_disable(venc.tv_dac_clk); | ||
841 | clk_disable(venc.tv_clk); | ||
842 | |||
843 | dispc_runtime_put(); | ||
844 | dss_runtime_put(); | ||
845 | |||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | static int venc_runtime_resume(struct device *dev) | ||
850 | { | ||
851 | int r; | ||
852 | |||
853 | r = dss_runtime_get(); | ||
854 | if (r < 0) | ||
855 | goto err_get_dss; | ||
856 | |||
857 | r = dispc_runtime_get(); | ||
858 | if (r < 0) | ||
859 | goto err_get_dispc; | ||
860 | |||
861 | clk_enable(venc.tv_clk); | ||
862 | if (venc.tv_dac_clk) | ||
863 | clk_enable(venc.tv_dac_clk); | ||
864 | |||
865 | return 0; | ||
866 | |||
867 | err_get_dispc: | ||
868 | dss_runtime_put(); | ||
869 | err_get_dss: | ||
870 | return r; | ||
871 | } | ||
872 | |||
873 | static const struct dev_pm_ops venc_pm_ops = { | ||
874 | .runtime_suspend = venc_runtime_suspend, | ||
875 | .runtime_resume = venc_runtime_resume, | ||
876 | }; | ||
877 | |||
770 | static struct platform_driver omap_venchw_driver = { | 878 | static struct platform_driver omap_venchw_driver = { |
771 | .probe = omap_venchw_probe, | 879 | .probe = omap_venchw_probe, |
772 | .remove = omap_venchw_remove, | 880 | .remove = omap_venchw_remove, |
773 | .driver = { | 881 | .driver = { |
774 | .name = "omapdss_venc", | 882 | .name = "omapdss_venc", |
775 | .owner = THIS_MODULE, | 883 | .owner = THIS_MODULE, |
884 | .pm = &venc_pm_ops, | ||
776 | }, | 885 | }, |
777 | }; | 886 | }; |
778 | 887 | ||