aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/amba-clcd.c
diff options
context:
space:
mode:
authorPawel Moll <pawel.moll@arm.com>2014-06-24 07:55:11 -0400
committerTomi Valkeinen <tomi.valkeinen@ti.com>2014-07-29 07:12:12 -0400
commitd10715be03bd8bad59ddc50236cb140c3bd73c7b (patch)
tree587203bb2ae8ae20a755b9eeda5cac8181aa4c95 /drivers/video/fbdev/amba-clcd.c
parentb1f46dd1079d78aa46b8bc740d3e24117335f150 (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/fbdev/amba-clcd.c')
-rw-r--r--drivers/video/fbdev/amba-clcd.c263
1 files changed, 263 insertions, 0 deletions
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
554static 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
574static 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
580static 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
608static 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
649static 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
691static 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
716static 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
734static void clcdfb_of_vram_remove(struct clcd_fb *fb)
735{
736 iounmap(fb->fb.screen_base);
737}
738
739static 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
762static 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
768static 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
774static 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
800static struct clcd_board *clcdfb_of_get_board(struct amba_dev *dev)
801{
802 return NULL;
803}
804#endif
805
546static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) 806static 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));