diff options
author | Pawel Moll <pawel.moll@arm.com> | 2014-06-24 07:55:11 -0400 |
---|---|---|
committer | Tomi Valkeinen <tomi.valkeinen@ti.com> | 2014-07-29 07:12:12 -0400 |
commit | d10715be03bd8bad59ddc50236cb140c3bd73c7b (patch) | |
tree | 587203bb2ae8ae20a755b9eeda5cac8181aa4c95 /drivers/video | |
parent | b1f46dd1079d78aa46b8bc740d3e24117335f150 (diff) |
video: ARM CLCD: Add DT support
This patch adds basic DT bindings for the PL11x CLCD cells
and make their fbdev driver use them.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/fbdev/Kconfig | 1 | ||||
-rw-r--r-- | drivers/video/fbdev/amba-clcd.c | 263 |
2 files changed, 264 insertions, 0 deletions
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 4a7098fb32c3..6f451ad34afc 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig | |||
@@ -280,6 +280,7 @@ config FB_ARMCLCD | |||
280 | select FB_CFB_FILLRECT | 280 | select FB_CFB_FILLRECT |
281 | select FB_CFB_COPYAREA | 281 | select FB_CFB_COPYAREA |
282 | select FB_CFB_IMAGEBLIT | 282 | select FB_CFB_IMAGEBLIT |
283 | select VIDEOMODE_HELPERS if OF | ||
283 | help | 284 | help |
284 | This framebuffer device driver is for the ARM PrimeCell PL110 | 285 | This framebuffer device driver is for the ARM PrimeCell PL110 |
285 | Colour LCD controller. ARM PrimeCells provide the building | 286 | Colour LCD controller. ARM PrimeCells provide the building |
diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c index 14d6b3793e0a..23b35194dee3 100644 --- a/drivers/video/fbdev/amba-clcd.c +++ b/drivers/video/fbdev/amba-clcd.c | |||
@@ -26,6 +26,13 @@ | |||
26 | #include <linux/amba/clcd.h> | 26 | #include <linux/amba/clcd.h> |
27 | #include <linux/clk.h> | 27 | #include <linux/clk.h> |
28 | #include <linux/hardirq.h> | 28 | #include <linux/hardirq.h> |
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/of.h> | ||
31 | #include <linux/of_address.h> | ||
32 | #include <linux/of_graph.h> | ||
33 | #include <video/display_timing.h> | ||
34 | #include <video/of_display_timing.h> | ||
35 | #include <video/videomode.h> | ||
29 | 36 | ||
30 | #include <asm/sizes.h> | 37 | #include <asm/sizes.h> |
31 | 38 | ||
@@ -543,6 +550,259 @@ static int clcdfb_register(struct clcd_fb *fb) | |||
543 | return ret; | 550 | return ret; |
544 | } | 551 | } |
545 | 552 | ||
553 | #ifdef CONFIG_OF | ||
554 | static int clcdfb_of_get_dpi_panel_mode(struct device_node *node, | ||
555 | struct fb_videomode *mode) | ||
556 | { | ||
557 | int err; | ||
558 | struct display_timing timing; | ||
559 | struct videomode video; | ||
560 | |||
561 | err = of_get_display_timing(node, "panel-timing", &timing); | ||
562 | if (err) | ||
563 | return err; | ||
564 | |||
565 | videomode_from_timing(&timing, &video); | ||
566 | |||
567 | err = fb_videomode_from_videomode(&video, mode); | ||
568 | if (err) | ||
569 | return err; | ||
570 | |||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode) | ||
575 | { | ||
576 | return snprintf(buf, size, "%ux%u@%u", mode->xres, mode->yres, | ||
577 | mode->refresh); | ||
578 | } | ||
579 | |||
580 | static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint, | ||
581 | struct fb_videomode *mode) | ||
582 | { | ||
583 | int err; | ||
584 | struct device_node *panel; | ||
585 | char *name; | ||
586 | int len; | ||
587 | |||
588 | panel = of_graph_get_remote_port_parent(endpoint); | ||
589 | if (!panel) | ||
590 | return -ENODEV; | ||
591 | |||
592 | /* Only directly connected DPI panels supported for now */ | ||
593 | if (of_device_is_compatible(panel, "panel-dpi")) | ||
594 | err = clcdfb_of_get_dpi_panel_mode(panel, mode); | ||
595 | else | ||
596 | err = -ENOENT; | ||
597 | if (err) | ||
598 | return err; | ||
599 | |||
600 | len = clcdfb_snprintf_mode(NULL, 0, mode); | ||
601 | name = devm_kzalloc(dev, len + 1, GFP_KERNEL); | ||
602 | clcdfb_snprintf_mode(name, len + 1, mode); | ||
603 | mode->name = name; | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0) | ||
609 | { | ||
610 | static struct { | ||
611 | unsigned int part; | ||
612 | u32 r0, g0, b0; | ||
613 | u32 caps; | ||
614 | } panels[] = { | ||
615 | { 0x110, 1, 7, 13, CLCD_CAP_5551 }, | ||
616 | { 0x110, 0, 8, 16, CLCD_CAP_888 }, | ||
617 | { 0x111, 4, 14, 20, CLCD_CAP_444 }, | ||
618 | { 0x111, 3, 11, 19, CLCD_CAP_444 | CLCD_CAP_5551 }, | ||
619 | { 0x111, 3, 10, 19, CLCD_CAP_444 | CLCD_CAP_5551 | | ||
620 | CLCD_CAP_565 }, | ||
621 | { 0x111, 0, 8, 16, CLCD_CAP_444 | CLCD_CAP_5551 | | ||
622 | CLCD_CAP_565 | CLCD_CAP_888 }, | ||
623 | }; | ||
624 | int i; | ||
625 | |||
626 | /* Bypass pixel clock divider, data output on the falling edge */ | ||
627 | fb->panel->tim2 = TIM2_BCD | TIM2_IPC; | ||
628 | |||
629 | /* TFT display, vert. comp. interrupt at the start of the back porch */ | ||
630 | fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1); | ||
631 | |||
632 | fb->panel->caps = 0; | ||
633 | |||
634 | /* Match the setup with known variants */ | ||
635 | for (i = 0; i < ARRAY_SIZE(panels) && !fb->panel->caps; i++) { | ||
636 | if (amba_part(fb->dev) != panels[i].part) | ||
637 | continue; | ||
638 | if (g0 != panels[i].g0) | ||
639 | continue; | ||
640 | if (r0 == panels[i].r0 && b0 == panels[i].b0) | ||
641 | fb->panel->caps = panels[i].caps & CLCD_CAP_RGB; | ||
642 | if (r0 == panels[i].b0 && b0 == panels[i].r0) | ||
643 | fb->panel->caps = panels[i].caps & CLCD_CAP_BGR; | ||
644 | } | ||
645 | |||
646 | return fb->panel->caps ? 0 : -EINVAL; | ||
647 | } | ||
648 | |||
649 | static int clcdfb_of_init_display(struct clcd_fb *fb) | ||
650 | { | ||
651 | struct device_node *endpoint; | ||
652 | int err; | ||
653 | u32 max_bandwidth; | ||
654 | u32 tft_r0b0g0[3]; | ||
655 | |||
656 | fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL); | ||
657 | if (!fb->panel) | ||
658 | return -ENOMEM; | ||
659 | |||
660 | endpoint = of_graph_get_next_endpoint(fb->dev->dev.of_node, NULL); | ||
661 | if (!endpoint) | ||
662 | return -ENODEV; | ||
663 | |||
664 | err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, &fb->panel->mode); | ||
665 | if (err) | ||
666 | return err; | ||
667 | |||
668 | err = of_property_read_u32(fb->dev->dev.of_node, "max-memory-bandwidth", | ||
669 | &max_bandwidth); | ||
670 | if (!err) | ||
671 | fb->panel->bpp = 8 * max_bandwidth / (fb->panel->mode.xres * | ||
672 | fb->panel->mode.yres * fb->panel->mode.refresh); | ||
673 | else | ||
674 | fb->panel->bpp = 32; | ||
675 | |||
676 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
677 | fb->panel->cntl |= CNTL_BEBO; | ||
678 | #endif | ||
679 | fb->panel->width = -1; | ||
680 | fb->panel->height = -1; | ||
681 | |||
682 | if (of_property_read_u32_array(endpoint, | ||
683 | "arm,pl11x,tft-r0g0b0-pads", | ||
684 | tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0) | ||
685 | return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0], | ||
686 | tft_r0b0g0[1], tft_r0b0g0[2]); | ||
687 | |||
688 | return -ENOENT; | ||
689 | } | ||
690 | |||
691 | static int clcdfb_of_vram_setup(struct clcd_fb *fb) | ||
692 | { | ||
693 | int err; | ||
694 | struct device_node *memory; | ||
695 | u64 size; | ||
696 | |||
697 | err = clcdfb_of_init_display(fb); | ||
698 | if (err) | ||
699 | return err; | ||
700 | |||
701 | memory = of_parse_phandle(fb->dev->dev.of_node, "memory-region", 0); | ||
702 | if (!memory) | ||
703 | return -ENODEV; | ||
704 | |||
705 | fb->fb.screen_base = of_iomap(memory, 0); | ||
706 | if (!fb->fb.screen_base) | ||
707 | return -ENOMEM; | ||
708 | |||
709 | fb->fb.fix.smem_start = of_translate_address(memory, | ||
710 | of_get_address(memory, 0, &size, NULL)); | ||
711 | fb->fb.fix.smem_len = size; | ||
712 | |||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | static int clcdfb_of_vram_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) | ||
717 | { | ||
718 | unsigned long off, user_size, kernel_size; | ||
719 | |||
720 | |||
721 | off = vma->vm_pgoff << PAGE_SHIFT; | ||
722 | user_size = vma->vm_end - vma->vm_start; | ||
723 | kernel_size = fb->fb.fix.smem_len; | ||
724 | |||
725 | if (off >= kernel_size || user_size > (kernel_size - off)) | ||
726 | return -ENXIO; | ||
727 | |||
728 | return remap_pfn_range(vma, vma->vm_start, | ||
729 | __phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff, | ||
730 | user_size, | ||
731 | pgprot_writecombine(vma->vm_page_prot)); | ||
732 | } | ||
733 | |||
734 | static void clcdfb_of_vram_remove(struct clcd_fb *fb) | ||
735 | { | ||
736 | iounmap(fb->fb.screen_base); | ||
737 | } | ||
738 | |||
739 | static int clcdfb_of_dma_setup(struct clcd_fb *fb) | ||
740 | { | ||
741 | unsigned long framesize; | ||
742 | dma_addr_t dma; | ||
743 | int err; | ||
744 | |||
745 | err = clcdfb_of_init_display(fb); | ||
746 | if (err) | ||
747 | return err; | ||
748 | |||
749 | framesize = fb->panel->mode.xres * fb->panel->mode.yres * | ||
750 | fb->panel->bpp / 8; | ||
751 | fb->fb.screen_base = dma_alloc_coherent(&fb->dev->dev, framesize, | ||
752 | &dma, GFP_KERNEL); | ||
753 | if (!fb->fb.screen_base) | ||
754 | return -ENOMEM; | ||
755 | |||
756 | fb->fb.fix.smem_start = dma; | ||
757 | fb->fb.fix.smem_len = framesize; | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static int clcdfb_of_dma_mmap(struct clcd_fb *fb, struct vm_area_struct *vma) | ||
763 | { | ||
764 | return dma_mmap_writecombine(&fb->dev->dev, vma, fb->fb.screen_base, | ||
765 | fb->fb.fix.smem_start, fb->fb.fix.smem_len); | ||
766 | } | ||
767 | |||
768 | static void clcdfb_of_dma_remove(struct clcd_fb *fb) | ||
769 | { | ||
770 | dma_free_coherent(&fb->dev->dev, fb->fb.fix.smem_len, | ||
771 | fb->fb.screen_base, fb->fb.fix.smem_start); | ||
772 | } | ||
773 | |||
774 | static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev) | ||
775 | { | ||
776 | struct clcd_board *board = devm_kzalloc(&dev->dev, sizeof(*board), | ||
777 | GFP_KERNEL); | ||
778 | struct device_node *node = dev->dev.of_node; | ||
779 | |||
780 | if (!board) | ||
781 | return NULL; | ||
782 | |||
783 | board->name = of_node_full_name(node); | ||
784 | board->caps = CLCD_CAP_ALL; | ||
785 | board->check = clcdfb_check; | ||
786 | board->decode = clcdfb_decode; | ||
787 | if (of_find_property(node, "memory-region", NULL)) { | ||
788 | board->setup = clcdfb_of_vram_setup; | ||
789 | board->mmap = clcdfb_of_vram_mmap; | ||
790 | board->remove = clcdfb_of_vram_remove; | ||
791 | } else { | ||
792 | board->setup = clcdfb_of_dma_setup; | ||
793 | board->mmap = clcdfb_of_dma_mmap; | ||
794 | board->remove = clcdfb_of_dma_remove; | ||
795 | } | ||
796 | |||
797 | return board; | ||
798 | } | ||
799 | #else | ||
800 | static struct clcd_board *clcdfb_of_get_board(struct amba_dev *dev) | ||
801 | { | ||
802 | return NULL; | ||
803 | } | ||
804 | #endif | ||
805 | |||
546 | static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) | 806 | static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) |
547 | { | 807 | { |
548 | struct clcd_board *board = dev_get_platdata(&dev->dev); | 808 | struct clcd_board *board = dev_get_platdata(&dev->dev); |
@@ -550,6 +810,9 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) | |||
550 | int ret; | 810 | int ret; |
551 | 811 | ||
552 | if (!board) | 812 | if (!board) |
813 | board = clcdfb_of_get_board(dev); | ||
814 | |||
815 | if (!board) | ||
553 | return -EINVAL; | 816 | return -EINVAL; |
554 | 817 | ||
555 | ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); | 818 | ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); |