Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* [PATCH 11/11] fbdev: improve fb_mmap bounds checks
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel
  Cc: Tomi Valkeinen, Steve Glendinning, Bernie Thompson
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Improve fb_mmap bounds checks in gbefb, smscufx, udlfb and vfb drivers to
prevent possible uint overflows.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Steve Glendinning <steve.glendinning@smsc.com>
Cc: Bernie Thompson <bernie@plugable.com>
---
 drivers/video/gbefb.c   |    4 +++-
 drivers/video/smscufx.c |    6 +++++-
 drivers/video/udlfb.c   |    6 +++++-
 drivers/video/vfb.c     |    7 +++++--
 4 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/video/gbefb.c b/drivers/video/gbefb.c
index bda5e39..ceab370 100644
--- a/drivers/video/gbefb.c
+++ b/drivers/video/gbefb.c
@@ -1016,7 +1016,9 @@ static int gbefb_mmap(struct fb_info *info,
 	/* check range */
 	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
 		return -EINVAL;
-	if (offset + size > gbe_mem_size)
+	if (size > gbe_mem_size)
+		return -EINVAL;
+	if (offset > gbe_mem_size - size)
 		return -EINVAL;
 
 	/* remap using the fastest write-through mode on architecture */
diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c
index 97bd662..b2b33fc 100644
--- a/drivers/video/smscufx.c
+++ b/drivers/video/smscufx.c
@@ -782,7 +782,11 @@ static int ufx_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	unsigned long page, pos;
 
-	if (offset + size > info->fix.smem_len)
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+		return -EINVAL;
+	if (size > info->fix.smem_len)
+		return -EINVAL;
+	if (offset > info->fix.smem_len - size)
 		return -EINVAL;
 
 	pos = (unsigned long)info->fix.smem_start + offset;
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c
index 86d449e..ec03e72 100644
--- a/drivers/video/udlfb.c
+++ b/drivers/video/udlfb.c
@@ -324,7 +324,11 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	unsigned long page, pos;
 
-	if (offset + size > info->fix.smem_len)
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+		return -EINVAL;
+	if (size > info->fix.smem_len)
+		return -EINVAL;
+	if (offset > info->fix.smem_len - size)
 		return -EINVAL;
 
 	pos = (unsigned long)info->fix.smem_start + offset;
diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c
index 8bc1f93..ee5985e 100644
--- a/drivers/video/vfb.c
+++ b/drivers/video/vfb.c
@@ -420,9 +420,12 @@ static int vfb_mmap(struct fb_info *info,
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	unsigned long page, pos;
 
-	if (offset + size > info->fix.smem_len) {
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+		return -EINVAL;
+	if (size > info->fix.smem_len)
+		return -EINVAL;
+	if (offset > info->fix.smem_len - size)
 		return -EINVAL;
-	}
 
 	pos = (unsigned long)info->fix.smem_start + offset;
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 10/11] fbdev/ps3fb: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen, Geert Uytterhoeven
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>
---
 drivers/video/ps3fb.c |   15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/drivers/video/ps3fb.c b/drivers/video/ps3fb.c
index 920c27b..1bdeb08 100644
--- a/drivers/video/ps3fb.c
+++ b/drivers/video/ps3fb.c
@@ -705,21 +705,14 @@ static int ps3fb_pan_display(struct fb_var_screeninfo *var,
 
 static int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
-	unsigned long size, offset;
+	int r;
 
-	size = vma->vm_end - vma->vm_start;
-	offset = vma->vm_pgoff << PAGE_SHIFT;
-	if (offset + size > info->fix.smem_len)
-		return -EINVAL;
-
-	offset += info->fix.smem_start;
-	if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT,
-			    size, vma->vm_page_prot))
-		return -EAGAIN;
+	r = vm_ioremap_memory(vma, info->fix.smem_start, info->fix.smem_len);
 
 	dev_dbg(info->device, "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n",
 		offset, vma->vm_start);
-	return 0;
+
+	return r;
 }
 
     /*
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 09/11] fbdev/sgivwfb: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/sgivwfb.c |   20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c
index 2331fad..b2a8912 100644
--- a/drivers/video/sgivwfb.c
+++ b/drivers/video/sgivwfb.c
@@ -705,23 +705,17 @@ static int sgivwfb_setcolreg(u_int regno, u_int red, u_int green,
 static int sgivwfb_mmap(struct fb_info *info,
 			struct vm_area_struct *vma)
 {
-	unsigned long size = vma->vm_end - vma->vm_start;
-	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	int r;
 
-	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
-		return -EINVAL;
-	if (offset + size > sgivwfb_mem_size)
-		return -EINVAL;
-	offset += sgivwfb_mem_phys;
 	pgprot_val(vma->vm_page_prot) -	    pgprot_val(vma->vm_page_prot) | _PAGE_PCD;
-	vma->vm_flags |= VM_IO;
-	if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT,
-						size, vma->vm_page_prot))
-		return -EAGAIN;
+		pgprot_val(vma->vm_page_prot) | _PAGE_PCD;
+
+	r = vm_iomap_memory(vma, sgivwfb_mem_phys, sgivwfb_mem_size);
+
 	printk(KERN_DEBUG "sgivwfb: mmap framebuffer P(%lx)->V(%lx)\n",
 	       offset, vma->vm_start);
-	return 0;
+
+	return r;
 }
 
 int __init sgivwfb_setup(char *options)
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 08/11] fbdev/vermillion: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen, Alan Hourihane
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Alan Hourihane <alanh@fairlite.demon.co.uk>
---
 drivers/video/vermilion/vermilion.c |   14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/drivers/video/vermilion/vermilion.c b/drivers/video/vermilion/vermilion.c
index 0aa516f..09a1366 100644
--- a/drivers/video/vermilion/vermilion.c
+++ b/drivers/video/vermilion/vermilion.c
@@ -1003,24 +1003,18 @@ static int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 static int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
 	struct vml_info *vinfo = container_of(info, struct vml_info, info);
-	unsigned long size = vma->vm_end - vma->vm_start;
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
 	int ret;
 
-	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
-		return -EINVAL;
-	if (offset + size > vinfo->vram_contig_size)
-		return -EINVAL;
 	ret = vmlfb_vram_offset(vinfo, offset);
 	if (ret)
 		return -EINVAL;
-	offset += vinfo->vram_start;
+
 	pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
 	pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
-	if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT,
-						size, vma->vm_page_prot))
-		return -EAGAIN;
-	return 0;
+
+	return vm_iomap_memory(vma, vinfo->vram_start,
+			vinfo->vram_contig_size);
 }
 
 static int vmlfb_sync(struct fb_info *info)
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 07/11] fbdev/sa1100fb: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen, Russell King
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/video/sa1100fb.c |   16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index cfbde5e..f34c858 100644
--- a/drivers/video/sa1100fb.c
+++ b/drivers/video/sa1100fb.c
@@ -556,7 +556,7 @@ static int sa1100fb_mmap(struct fb_info *info,
 			 struct vm_area_struct *vma)
 {
 	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
-	unsigned long start, len, off = vma->vm_pgoff << PAGE_SHIFT;
+	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
 
 	if (off < info->fix.smem_len) {
 		vma->vm_pgoff += 1; /* skip over the palette */
@@ -564,19 +564,9 @@ static int sa1100fb_mmap(struct fb_info *info,
 					     fbi->map_dma, fbi->map_size);
 	}
 
-	start = info->fix.mmio_start;
-	len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
-
-	if ((vma->vm_end - vma->vm_start + off) > len)
-		return -EINVAL;
-
-	off += start & PAGE_MASK;
-	vma->vm_pgoff = off >> PAGE_SHIFT;
-	vma->vm_flags |= VM_IO;
 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-	return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-				   vma->vm_end - vma->vm_start,
-				   vma->vm_page_prot);
+
+	return vm_iomap_memory(vma, info->fix.mmio_start, info->fix.mmio_len);
 }
 
 static struct fb_ops sa1100fb_ops = {
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 06/11] fbdev/fb-puv3: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen, Guan Xuetao
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Guan Xuetao <gxt@mprc.pku.edu.cn>
---
 drivers/video/fb-puv3.c |   14 +-------------
 1 file changed, 1 insertion(+), 13 deletions(-)

diff --git a/drivers/video/fb-puv3.c b/drivers/video/fb-puv3.c
index 7d106f1f..27fc956 100644
--- a/drivers/video/fb-puv3.c
+++ b/drivers/video/fb-puv3.c
@@ -640,21 +640,9 @@ static int unifb_pan_display(struct fb_var_screeninfo *var,
 int unifb_mmap(struct fb_info *info,
 		    struct vm_area_struct *vma)
 {
-	unsigned long size = vma->vm_end - vma->vm_start;
-	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-	unsigned long pos = info->fix.smem_start + offset;
-
-	if (offset + size > info->fix.smem_len)
-		return -EINVAL;
-
 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
-	if (io_remap_pfn_range(vma, vma->vm_start, pos >> PAGE_SHIFT, size,
-				vma->vm_page_prot))
-		return -EAGAIN;
-
-	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
-	return 0;
+	return vm_iomap_memory(vma, info->fix.smem_start, info->fix.smem_len);
 }
 
 static struct fb_ops unifb_ops = {
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 05/11] fbdev/controlfb: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/controlfb.c |   50 ++++++++++++++++++---------------------------
 1 file changed, 20 insertions(+), 30 deletions(-)

diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c
index 0c189b3..67b77b4 100644
--- a/drivers/video/controlfb.c
+++ b/drivers/video/controlfb.c
@@ -285,36 +285,26 @@ static int controlfb_pan_display(struct fb_var_screeninfo *var,
 static int controlfb_mmap(struct fb_info *info,
                        struct vm_area_struct *vma)
 {
-       unsigned long off, start;
-       u32 len;
-
-       off = vma->vm_pgoff << PAGE_SHIFT;
-
-       /* frame buffer memory */
-       start = info->fix.smem_start;
-       len = PAGE_ALIGN((start & ~PAGE_MASK)+info->fix.smem_len);
-       if (off >= len) {
-               /* memory mapped io */
-               off -= len;
-               if (info->var.accel_flags)
-                       return -EINVAL;
-               start = info->fix.mmio_start;
-               len = PAGE_ALIGN((start & ~PAGE_MASK)+info->fix.mmio_len);
-	       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-       } else {
-               /* framebuffer */
-	       vma->vm_page_prot = pgprot_cached_wthru(vma->vm_page_prot);
-       }
-       start &= PAGE_MASK;
-       if ((vma->vm_end - vma->vm_start + off) > len)
-       		return -EINVAL;
-       off += start;
-       vma->vm_pgoff = off >> PAGE_SHIFT;
-       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-           vma->vm_end - vma->vm_start, vma->vm_page_prot))
-               return -EAGAIN;
-
-       return 0;
+	unsigned long mmio_pgoff;
+	unsigned long start;
+	u32 len;
+
+	start = info->fix.smem_start;
+	len = info->fix.smem_len;
+	mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
+	if (vma->vm_pgoff >= mmio_pgoff) {
+		if (info->var.accel_flags)
+			return -EINVAL;
+		vma->vm_pgoff -= mmio_pgoff;
+		start = info->fix.mmio_start;
+		len = info->fix.mmio_len;
+		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	} else {
+		/* framebuffer */
+		vma->vm_page_prot = pgprot_cached_wthru(vma->vm_page_prot);
+	}
+
+	return vm_iomap_memory(vma, start, len);
 }
 
 static int controlfb_blank(int blank_mode, struct fb_info *info)
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 04/11] fbdev/au1200fb: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen, Ralf Baechle, Manuel Lauss
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Manuel Lauss <manuel.lauss@googlemail.com>
---
 drivers/video/au1200fb.c |   26 +-------------------------
 1 file changed, 1 insertion(+), 25 deletions(-)

diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c
index 1b59054..6ce6b83 100644
--- a/drivers/video/au1200fb.c
+++ b/drivers/video/au1200fb.c
@@ -1235,36 +1235,12 @@ static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi)
 static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 
 {
-	unsigned int len;
-	unsigned long start=0, off;
 	struct au1200fb_device *fbdev = info->par;
 
-	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
-		return -EINVAL;
-	}
-
-	start = fbdev->fb_phys & PAGE_MASK;
-	len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
-	off = vma->vm_pgoff << PAGE_SHIFT;
-
-	if ((vma->vm_end - vma->vm_start + off) > len) {
-		return -EINVAL;
-	}
-
-	off += start;
-	vma->vm_pgoff = off >> PAGE_SHIFT;
-
 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 	pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */
 
-	vma->vm_flags |= VM_IO;
-
-	return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-				  vma->vm_end - vma->vm_start,
-				  vma->vm_page_prot);
-
-	return 0;
+	return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
 }
 
 static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata)
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 03/11] fbdev/au1100fb: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/au1100fb.c |   28 +---------------------------
 1 file changed, 1 insertion(+), 27 deletions(-)

diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
index ddabaa8..0473b11 100644
--- a/drivers/video/au1100fb.c
+++ b/drivers/video/au1100fb.c
@@ -375,39 +375,13 @@ void au1100fb_fb_rotate(struct fb_info *fbi, int angle)
 int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
 {
 	struct au1100fb_device *fbdev;
-	unsigned int len;
-	unsigned long start=0, off;
 
 	fbdev = to_au1100fb_device(fbi);
 
-	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
-		return -EINVAL;
-	}
-
-	start = fbdev->fb_phys & PAGE_MASK;
-	len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
-
-	off = vma->vm_pgoff << PAGE_SHIFT;
-
-	if ((vma->vm_end - vma->vm_start + off) > len) {
-		return -EINVAL;
-	}
-
-	off += start;
-	vma->vm_pgoff = off >> PAGE_SHIFT;
-
 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 	pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6
 
-	vma->vm_flags |= VM_IO;
-
-	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-				vma->vm_end - vma->vm_start,
-				vma->vm_page_prot)) {
-		return -EAGAIN;
-	}
-
-	return 0;
+	return vm_iomap_memory(vma, fbdev->fb_phys, fbdev->fb_len);
 }
 
 static struct fb_ops au1100fb_ops -- 
1.7.10.4


^ permalink raw reply related

* [PATCH 02/11] fbdev/omapfb: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen
In-Reply-To: <1366699018-5439-1-git-send-email-tomi.valkeinen@ti.com>

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/omapfb/omapfb-main.c |   30 +++++++-----------------------
 1 file changed, 7 insertions(+), 23 deletions(-)

diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index ca585ef..717f13a 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -1101,41 +1101,25 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
 	struct omapfb_info *ofbi = FB2OFB(fbi);
 	struct fb_fix_screeninfo *fix = &fbi->fix;
 	struct omapfb2_mem_region *rg;
-	unsigned long off;
 	unsigned long start;
 	u32 len;
-	int r = -EINVAL;
-
-	if (vma->vm_end - vma->vm_start = 0)
-		return 0;
-	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
-		return -EINVAL;
-	off = vma->vm_pgoff << PAGE_SHIFT;
+	int r;
 
 	rg = omapfb_get_mem_region(ofbi->region);
 
 	start = omapfb_get_region_paddr(ofbi);
 	len = fix->smem_len;
-	if (off >= len)
-		goto error;
-	if ((vma->vm_end - vma->vm_start + off) > len)
-		goto error;
-
-	off += start;
 
-	DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off);
+	DBG("user mmap region start %lx, len %d, off %lx\n", start, len,
+			vma->vm_pgoff << PAGE_SHIFT);
 
-	vma->vm_pgoff = off >> PAGE_SHIFT;
-	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
 	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
 	vma->vm_ops = &mmap_user_ops;
 	vma->vm_private_data = rg;
-	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-			       vma->vm_end - vma->vm_start,
-			       vma->vm_page_prot)) {
-		r = -EAGAIN;
+
+	r = vm_iomap_memory(vma, start, len);
+	if (r)
 		goto error;
-	}
 
 	/* vm_ops.open won't be called for mmap itself. */
 	atomic_inc(&rg->map_count);
@@ -1144,7 +1128,7 @@ static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
 
 	return 0;
 
- error:
+error:
 	omapfb_put_mem_region(ofbi->region);
 
 	return r;
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 01/11] fbdev/fbmem: use vm_iomap_memory()
From: Tomi Valkeinen @ 2013-04-23  6:36 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel; +Cc: Tomi Valkeinen

Use vm_iomap_memory() instead of [io_]remap_pfn_range().
vm_iomap_memory() gives us much simpler API to map memory to userspace,
and reduces possibilities for bugs.

Original patch from Linus Torvalds <torvalds@linux-foundation.org>

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/fbmem.c |   36 ++++++++++++------------------------
 1 file changed, 12 insertions(+), 24 deletions(-)

diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 7c25408..0598aa9 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1373,15 +1373,12 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
 {
 	struct fb_info *info = file_fb_info(file);
 	struct fb_ops *fb;
-	unsigned long off;
+	unsigned long mmio_pgoff;
 	unsigned long start;
 	u32 len;
 
 	if (!info)
 		return -ENODEV;
-	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
-		return -EINVAL;
-	off = vma->vm_pgoff << PAGE_SHIFT;
 	fb = info->fbops;
 	if (!fb)
 		return -ENODEV;
@@ -1393,32 +1390,23 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
 		return res;
 	}
 
-	/* frame buffer memory */
+	/* Map mmio if pgoff points past the fb */
 	start = info->fix.smem_start;
-	len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
-	if (off >= len) {
-		/* memory mapped io */
-		off -= len;
-		if (info->var.accel_flags) {
-			mutex_unlock(&info->mm_lock);
+	len = info->fix.smem_len;
+	mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
+	if (vma->vm_pgoff >= mmio_pgoff) {
+		if (info->var.accel_flags)
 			return -EINVAL;
-		}
+		vma->vm_pgoff -= mmio_pgoff;
 		start = info->fix.mmio_start;
-		len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
+		len = info->fix.mmio_len;
 	}
 	mutex_unlock(&info->mm_lock);
-	start &= PAGE_MASK;
-	if ((vma->vm_end - vma->vm_start + off) > len)
-		return -EINVAL;
-	off += start;
-	vma->vm_pgoff = off >> PAGE_SHIFT;
-	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by io_remap_pfn_range()*/
+
 	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
-	fb_pgprotect(file, vma, off);
-	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
-		return -EAGAIN;
-	return 0;
+	fb_pgprotect(file, vma, start);
+
+	return vm_iomap_memory(vma, start, len);
 }
 
 static int
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH v4 2/2] video: imxfb: Add DT support
From: Jean-Christophe PLAGNIOL-VILLARD @ 2013-04-22 15:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130421081115.GA31179@pengutronix.de>

On 10:11 Sun 21 Apr     , Markus Pargmann wrote:
> On Thu, Apr 18, 2013 at 06:06:14PM +0200, Jean-Christophe PLAGNIOL-VILLARD wrote:
> > On 15:03 Thu 18 Apr     , Markus Pargmann wrote:
> > > Add devicetree support for imx framebuffer driver. It uses the generic
> > > display bindings and helper functions.
> > > 
> > > Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
> > > Cc: Fabio Estevam <festevam@gmail.com>
> > > Cc: Mark Rutland <mark.rutland@arm.com>
> > > ---
> > > 
> > > Notes:
> > >     Changes in v4:
> > >     - Remove eukrea specific dmacr property.
> > >     - Add optional dmacr property. If not present, the dmacr reset value is not
> > >       changed.
> > >     
> > >     Changes in v3:
> > >     - Fix returncodes of of_read_mode function and print error messages
> > >     - Introduce a lower bound check for bits per pixel
> > >     - Calculate correct bytes per pixel value before using it for the calculation of
> > >     	memory size
> > >     - Change DT property names
> > >     
> > >     Changes in v2:
> > >     - Removed pwmr register property
> > >     - Cleanup of devicetree binding documentation
> > >     - Use default values for pwmr and lscr1
> > > 
> > >  .../devicetree/bindings/video/fsl,imx-fb.txt       |  51 ++++++
> > >  drivers/video/imxfb.c                              | 194 +++++++++++++++++----
> > >  2 files changed, 210 insertions(+), 35 deletions(-)
> > >  create mode 100644 Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> > > 
> > > diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> > > new file mode 100644
> > > index 0000000..aff16a4
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt
> > > @@ -0,0 +1,51 @@
> > > +Freescale imx21 Framebuffer
> > > +
> > > +This framebuffer driver supports devices imx1, imx21, imx25, and imx27.
> > > +
> > > +Required properties:
> > > +- compatible : "fsl,<chip>-fb", chip should be imx1 or imx21
> > > +- reg : Should contain 1 register ranges(address and length)
> > > +- interrupts : One interrupt of the fb dev
> > > +
> > > +Required nodes:
> > > +- display: Phandle to a display node as described in
> > > +	Documentation/devicetree/bindings/video/display-timing.txt
> > > +	Additional, the display node has to define properties:
> > > +	- fsl,bpp: Bits per pixel
> > > +	- fsl,pcr: LCDC PCR value
> > > +
> > > +Optional properties:
> > > +- fsl,dmacr: DMA Control Register value. This is optional. By default, the
> > > +	register is not modified as recommended by the datasheet.
> > > +- fsl,lscr1: LCDC Sharp Configuration Register value.
> > > +
> > > +Example:
> > > +
> > > +	imxfb: fb@10021000 {
> > > +		compatible = "fsl,imx27-fb", "fsl,imx21-fb";
> > you put both when in the doc you request one
> 
> Thanks, fixed.
> 
> > > +		interrupts = <61>;
> > > +		reg = <0x10021000 0x1000>;
> > > +		display = <&display0>;
> > > +	};
> > > +
> > > +	...
> > > +
> > > +	display0: display0 {
> > > +		model = "Primeview-PD050VL1";
> > > +		native-mode = <&timing_disp0>;
> > > +		fsl,bpp = <16>;		/* non-standard but required */
> > there is a generic binding bit-per-pixel use a cross other IP
> 
> I can't find a generic binding for that. There are only 2 other drivers
> using a "bpp" property, but they both use it within different contexts.
> tilcdc panel uses it within a "panel-info" property and via,vt8500-fb
> within modes. So I think it would be better to use a clear distinction
> because there is no generic binding. That was also suggested in comments
> on version 2 of this patch:
> https://patchwork.kernel.org/patch/2220511/

via use bits-per-pixel
atmel ditto
mxs ditto IIRC

Best Regards,
J.
> 
> Regards,
> 
> Markus
> 
> > > +		fsl,pcr = <0xf0c88080>;	/* non-standard but required */
> > > +		display-timings {
> > > +			timing_disp0: 640x480 {
> > > +				hactive = <640>;
> > > +				vactive = <480>;
> > > +				hback-porch = <112>;
> > > +				hfront-porch = <36>;
> > > +				hsync-len = <32>;
> > > +				vback-porch = <33>;
> > > +				vfront-porch = <33>;
> > > +				vsync-len = <2>;
> > > +				clock-frequency = <25000000>;
> > > +			};
> > > +		};
> > > +	};
> > > diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
> > > index ef2b587..e0230f8 100644
> > > --- a/drivers/video/imxfb.c
> > > +++ b/drivers/video/imxfb.c
> > > @@ -32,6 +32,12 @@
> > >  #include <linux/io.h>
> > >  #include <linux/math64.h>
> > >  #include <linux/uaccess.h>
> > > +#include <linux/of.h>
> > > +#include <linux/of_device.h>
> > > +
> > > +#include <video/of_display_timing.h>
> > > +#include <video/of_videomode.h>
> > > +#include <video/videomode.h>
> > >  
> > >  #include <linux/platform_data/video-imxfb.h>
> > >  
> > > @@ -117,10 +123,11 @@
> > >  #define LGWCR_GWAV(alpha)	(((alpha) & 0xff) << 24)
> > >  #define LGWCR_GWE	(1 << 22)
> > >  
> > > +#define IMXFB_LSCR1_DEFAULT 0x00120300
> > > +
> > >  /* Used fb-mode. Can be set on kernel command line, therefore file-static. */
> > >  static const char *fb_mode;
> > >  
> > > -
> > >  /*
> > >   * These are the bitfields for each
> > >   * display depth that we support.
> > > @@ -192,6 +199,19 @@ static struct platform_device_id imxfb_devtype[] = {
> > >  };
> > >  MODULE_DEVICE_TABLE(platform, imxfb_devtype);
> > >  
> > > +static struct of_device_id imxfb_of_dev_id[] = {
> > > +	{
> > > +		.compatible = "fsl,imx1-fb",
> > > +		.data = &imxfb_devtype[IMX1_FB],
> > > +	}, {
> > > +		.compatible = "fsl,imx21-fb",
> > > +		.data = &imxfb_devtype[IMX21_FB],
> > > +	}, {
> > > +		/* sentinel */
> > > +	}
> > > +};
> > > +MODULE_DEVICE_TABLE(of, imxfb_of_dev_id);
> > > +
> > >  static inline int is_imx1_fb(struct imxfb_info *fbi)
> > >  {
> > >  	return fbi->devtype = IMX1_FB;
> > > @@ -324,6 +344,9 @@ static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
> > >  	struct imx_fb_videomode *m;
> > >  	int i;
> > >  
> > > +	if (!fb_mode)
> > > +		return &fbi->mode[0];
> > > +
> > >  	for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {
> > >  		if (!strcmp(m->mode.name, fb_mode))
> > >  			return m;
> > > @@ -479,6 +502,9 @@ static int imxfb_bl_update_status(struct backlight_device *bl)
> > >  	struct imxfb_info *fbi = bl_get_data(bl);
> > >  	int brightness = bl->props.brightness;
> > >  
> > > +	if (!fbi->pwmr)
> > > +		return 0;
> > > +
> > >  	if (bl->props.power != FB_BLANK_UNBLANK)
> > >  		brightness = 0;
> > >  	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
> > > @@ -719,10 +745,14 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
> > >  
> > >  	writel(fbi->pcr, fbi->regs + LCDC_PCR);
> > >  #ifndef PWMR_BACKLIGHT_AVAILABLE
> > > -	writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
> > > +	if (fbi->pwmr)
> > > +		writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
> > >  #endif
> > >  	writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
> > > -	writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
> > > +
> > > +	/* dmacr = 0 is no valid value, as we need DMA control marks. */
> > > +	if (fbi->dmacr)
> > > +		writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
> > >  
> > >  	return 0;
> > >  }
> > > @@ -758,13 +788,12 @@ static int imxfb_resume(struct platform_device *dev)
> > >  #define imxfb_resume	NULL
> > >  #endif
> > >  
> > > -static int __init imxfb_init_fbinfo(struct platform_device *pdev)
> > > +static int imxfb_init_fbinfo(struct platform_device *pdev)
> > >  {
> > >  	struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
> > >  	struct fb_info *info = dev_get_drvdata(&pdev->dev);
> > >  	struct imxfb_info *fbi = info->par;
> > > -	struct imx_fb_videomode *m;
> > > -	int i;
> > > +	struct device_node *np;
> > >  
> > >  	pr_debug("%s\n",__func__);
> > >  
> > > @@ -795,41 +824,95 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
> > >  	info->fbops			= &imxfb_ops;
> > >  	info->flags			= FBINFO_FLAG_DEFAULT |
> > >  					  FBINFO_READS_FAST;
> > > -	info->var.grayscale		= pdata->cmap_greyscale;
> > > -	fbi->cmap_inverse		= pdata->cmap_inverse;
> > > -	fbi->cmap_static		= pdata->cmap_static;
> > > -	fbi->lscr1			= pdata->lscr1;
> > > -	fbi->dmacr			= pdata->dmacr;
> > > -	fbi->pwmr			= pdata->pwmr;
> > > -	fbi->lcd_power			= pdata->lcd_power;
> > > -	fbi->backlight_power		= pdata->backlight_power;
> > > -
> > > -	for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++)
> > > -		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
> > > -				m->mode.xres * m->mode.yres * m->bpp / 8);
> > > +	if (pdata) {
> > > +		info->var.grayscale		= pdata->cmap_greyscale;
> > > +		fbi->cmap_inverse		= pdata->cmap_inverse;
> > > +		fbi->cmap_static		= pdata->cmap_static;
> > > +		fbi->lscr1			= pdata->lscr1;
> > > +		fbi->dmacr			= pdata->dmacr;
> > > +		fbi->pwmr			= pdata->pwmr;
> > > +		fbi->lcd_power			= pdata->lcd_power;
> > > +		fbi->backlight_power		= pdata->backlight_power;
> > > +	} else {
> > > +		np = pdev->dev.of_node;
> > > +		info->var.grayscale = of_property_read_bool(np,
> > > +						"cmap-greyscale");
> > > +		fbi->cmap_inverse = of_property_read_bool(np, "cmap-inverse");
> > > +		fbi->cmap_static = of_property_read_bool(np, "cmap-static");
> > > +
> > > +		fbi->lscr1 = IMXFB_LSCR1_DEFAULT;
> > > +		of_property_read_u32(np, "fsl,lscr1", &fbi->lscr1);
> > > +
> > > +		of_property_read_u32(np, "fsl,dmacr", &fbi->dmacr);
> > > +
> > > +		/* These two function pointers could be used by some specific
> > > +		 * platforms. */
> > > +		fbi->lcd_power = NULL;
> > > +		fbi->backlight_power = NULL;
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int imxfb_of_read_mode(struct device *dev, struct device_node *np,
> > > +		struct imx_fb_videomode *imxfb_mode)
> > > +{
> > > +	int ret;
> > > +	struct fb_videomode *of_mode = &imxfb_mode->mode;
> > > +	u32 bpp;
> > > +	u32 pcr;
> > > +
> > > +	ret = of_property_read_string(np, "model", &of_mode->name);
> > > +	if (ret)
> > > +		of_mode->name = NULL;
> > > +
> > > +	ret = of_get_fb_videomode(np, of_mode, OF_USE_NATIVE_MODE);
> > > +	if (ret) {
> > > +		dev_err(dev, "Failed to get videomode from DT\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = of_property_read_u32(np, "fsl,bpp", &bpp);
> > > +	ret |= of_property_read_u32(np, "fsl,pcr", &pcr);
> > > +
> > > +	if (ret) {
> > > +		dev_err(dev, "Failed to read bpp and pcr from DT\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	if (bpp < 1 || bpp > 255) {
> > > +		dev_err(dev, "Bits per pixel have to be between 1 and 255\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	imxfb_mode->bpp = bpp;
> > > +	imxfb_mode->pcr = pcr;
> > >  
> > >  	return 0;
> > >  }
> > >  
> > > -static int __init imxfb_probe(struct platform_device *pdev)
> > > +static int imxfb_probe(struct platform_device *pdev)
> > >  {
> > >  	struct imxfb_info *fbi;
> > >  	struct fb_info *info;
> > >  	struct imx_fb_platform_data *pdata;
> > >  	struct resource *res;
> > > +	struct imx_fb_videomode *m;
> > > +	const struct of_device_id *of_id;
> > >  	int ret, i;
> > > +	int bytes_per_pixel;
> > >  
> > >  	dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
> > >  
> > > +	of_id = of_match_device(imxfb_of_dev_id, &pdev->dev);
> > > +	if (of_id)
> > > +		pdev->id_entry = of_id->data;
> > > +
> > >  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > >  	if (!res)
> > >  		return -ENODEV;
> > >  
> > >  	pdata = pdev->dev.platform_data;
> > > -	if (!pdata) {
> > > -		dev_err(&pdev->dev,"No platform_data available\n");
> > > -		return -ENOMEM;
> > > -	}
> > >  
> > >  	info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
> > >  	if (!info)
> > > @@ -837,15 +920,55 @@ static int __init imxfb_probe(struct platform_device *pdev)
> > >  
> > >  	fbi = info->par;
> > >  
> > > -	if (!fb_mode)
> > > -		fb_mode = pdata->mode[0].mode.name;
> > > -
> > >  	platform_set_drvdata(pdev, info);
> > >  
> > >  	ret = imxfb_init_fbinfo(pdev);
> > >  	if (ret < 0)
> > >  		goto failed_init;
> > >  
> > > +	if (pdata) {
> > > +		if (!fb_mode)
> > > +			fb_mode = pdata->mode[0].mode.name;
> > > +
> > > +		fbi->mode = pdata->mode;
> > > +		fbi->num_modes = pdata->num_modes;
> > > +	} else {
> > > +		struct device_node *display_np;
> > > +		fb_mode = NULL;
> > > +
> > > +		display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);
> > > +		if (!display_np) {
> > > +			dev_err(&pdev->dev, "No display defined in devicetree\n");
> > > +			ret = -EINVAL;
> > > +			goto failed_of_parse;
> > > +		}
> > > +
> > > +		/*
> > > +		 * imxfb does not support more modes, we choose only the native
> > > +		 * mode.
> > > +		 */
> > > +		fbi->num_modes = 1;
> > > +
> > > +		fbi->mode = devm_kzalloc(&pdev->dev,
> > > +				sizeof(struct imx_fb_videomode), GFP_KERNEL);
> > > +		if (!fbi->mode) {
> > > +			ret = -ENOMEM;
> > > +			goto failed_of_parse;
> > > +		}
> > > +
> > > +		ret = imxfb_of_read_mode(&pdev->dev, display_np, fbi->mode);
> > > +		if (ret)
> > > +			goto failed_of_parse;
> > > +	}
> > > +
> > > +	/* Calculate maximum bytes used per pixel. In most cases this should
> > > +	 * be the same as m->bpp/8 */
> > > +	m = &fbi->mode[0];
> > > +	bytes_per_pixel = (m->bpp + 7) / 8;
> > > +	for (i = 0; i < fbi->num_modes; i++, m++)
> > > +		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
> > > +				m->mode.xres * m->mode.yres * bytes_per_pixel);
> > > +
> > >  	res = request_mem_region(res->start, resource_size(res),
> > >  				DRIVER_NAME);
> > >  	if (!res) {
> > > @@ -878,7 +1001,8 @@ static int __init imxfb_probe(struct platform_device *pdev)
> > >  		goto failed_ioremap;
> > >  	}
> > >  
> > > -	if (!pdata->fixed_screen_cpu) {
> > > +	/* Seems not being used by anyone, so no support for oftree */
> > > +	if (!pdata || !pdata->fixed_screen_cpu) {
> > >  		fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
> > >  		fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
> > >  				fbi->map_size, &fbi->map_dma, GFP_KERNEL);
> > > @@ -903,18 +1027,16 @@ static int __init imxfb_probe(struct platform_device *pdev)
> > >  		info->fix.smem_start = fbi->screen_dma;
> > >  	}
> > >  
> > > -	if (pdata->init) {
> > > +	if (pdata && pdata->init) {
> > >  		ret = pdata->init(fbi->pdev);
> > >  		if (ret)
> > >  			goto failed_platform_init;
> > >  	}
> > >  
> > > -	fbi->mode = pdata->mode;
> > > -	fbi->num_modes = pdata->num_modes;
> > >  
> > >  	INIT_LIST_HEAD(&info->modelist);
> > > -	for (i = 0; i < pdata->num_modes; i++)
> > > -		fb_add_videomode(&pdata->mode[i].mode, &info->modelist);
> > > +	for (i = 0; i < fbi->num_modes; i++)
> > > +		fb_add_videomode(&fbi->mode[i].mode, &info->modelist);
> > >  
> > >  	/*
> > >  	 * This makes sure that our colour bitfield
> > > @@ -944,10 +1066,10 @@ static int __init imxfb_probe(struct platform_device *pdev)
> > >  failed_register:
> > >  	fb_dealloc_cmap(&info->cmap);
> > >  failed_cmap:
> > > -	if (pdata->exit)
> > > +	if (pdata && pdata->exit)
> > >  		pdata->exit(fbi->pdev);
> > >  failed_platform_init:
> > > -	if (!pdata->fixed_screen_cpu)
> > > +	if (pdata && !pdata->fixed_screen_cpu)
> > >  		dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
> > >  			fbi->map_dma);
> > >  failed_map:
> > > @@ -956,6 +1078,7 @@ failed_ioremap:
> > >  failed_getclock:
> > >  	release_mem_region(res->start, resource_size(res));
> > >  failed_req:
> > > +failed_of_parse:
> > >  	kfree(info->pseudo_palette);
> > >  failed_init:
> > >  	platform_set_drvdata(pdev, NULL);
> > > @@ -980,7 +1103,7 @@ static int imxfb_remove(struct platform_device *pdev)
> > >  	unregister_framebuffer(info);
> > >  
> > >  	pdata = pdev->dev.platform_data;
> > > -	if (pdata->exit)
> > > +	if (pdata && pdata->exit)
> > >  		pdata->exit(fbi->pdev);
> > >  
> > >  	fb_dealloc_cmap(&info->cmap);
> > > @@ -1009,6 +1132,7 @@ static struct platform_driver imxfb_driver = {
> > >  	.shutdown	= imxfb_shutdown,
> > >  	.driver		= {
> > >  		.name	= DRIVER_NAME,
> > > +		.of_match_table = imxfb_of_dev_id,
> > >  	},
> > >  	.id_table	= imxfb_devtype,
> > >  };
> > > -- 
> > > 1.8.1.5
> > > 
> > 
> 
> -- 
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Marek Vasut @ 2013-04-22 14:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <51750EB9.3020301@free-electrons.com>

Dear Maxime Ripard,

> Hi Marek,
> 
> Le 22/04/2013 11:16, Marek Vasut a écrit :
> > Dear Maxime Ripard,
> > 
> >> The current code always registers as a 32 bits display, and uses the
> >> hardware to drop the MSB of each color to abjust to the interface width
> >> used by the panel.
> >> 
> >> This results on 18 bits (and probably 16 bits display as well) in colors
> >> being displayed poorly, because the MSB are obviously the most important
> >> bits for each color definition.
> >> 
> >> The default controller behaviour when using an interface width smaller
> >> than the color depth is to drop the LSBs of each color, which makes more
> >> sense because you lose the least important part of the color definition.
> >> 
> >> So, to fix the colors display, just get back to the default controller
> >> behaviour.
> >> 
> >> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > 
> > Did you receive my latest email? Check M28EVK (imx28-m28evk.dts), it uses
> > 18bit LCD and works without this patch I think.
> 
> Thanks for the pointer. You mentionned in your other mail that it was
> wired on 24bits but that the screen is actually 18 bits. I went to
> search for the schematics to look at the wirings to see what could
> differ, but I couldn't find any for the M28EVK. Are they publicly
> available?

Yes, [1] page 6 .

[1] http://www.denx-cs.de/doku/sites/default/files/M28EVK_V20_0.pdf

Best regards,
Marek Vasut

^ permalink raw reply

* Re: [RFC v2] video: ARM CLCD: Add DT & CDF support
From: Russell King - ARM Linux @ 2013-04-22 14:28 UTC (permalink / raw)
  To: Pawel Moll
  Cc: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
	linux-arm-kernel, Laurent Pinchart, Linus Walleij
In-Reply-To: <1366306402-21651-1-git-send-email-pawel.moll@arm.com>

On Thu, Apr 18, 2013 at 06:33:21PM +0100, Pawel Moll wrote:
> This patch adds basic DT bindings for the PL11x CLCD cells
> and make their fbdev driver use them, together with the
> Common Display Framework.
> 
> The DT provides information about the hardware configuration
> and limitations (eg. the largest supported resolution)
> but the video modes come exclusively from the Common
> Display Framework drivers, referenced to by the standard CDF
> binding.
> 
> Signed-off-by: Pawel Moll <pawel.moll@arm.com>

Much better.

I will point out though that there be all sorts of worms here when you
come to the previous ARM evaluation boards (which is why the capabilities
stuff got written in the first place) where there's a horrid mixture of
BGR/RGB ordering at various levels of the system - some of which must be
set correctly because the CLCD output isn't strictly used as R bits
G bits and B bits (to support different formats from the CLCD's native
formats.)

^ permalink raw reply

* Re: [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Maxime Ripard @ 2013-04-22 10:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201304221116.18080.marex@denx.de>

Hi Marek,

Le 22/04/2013 11:16, Marek Vasut a écrit :
> Dear Maxime Ripard,
> 
>> The current code always registers as a 32 bits display, and uses the
>> hardware to drop the MSB of each color to abjust to the interface width
>> used by the panel.
>>
>> This results on 18 bits (and probably 16 bits display as well) in colors
>> being displayed poorly, because the MSB are obviously the most important
>> bits for each color definition.
>>
>> The default controller behaviour when using an interface width smaller
>> than the color depth is to drop the LSBs of each color, which makes more
>> sense because you lose the least important part of the color definition.
>>
>> So, to fix the colors display, just get back to the default controller
>> behaviour.
>>
>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> Did you receive my latest email? Check M28EVK (imx28-m28evk.dts), it uses 18bit 
> LCD and works without this patch I think.

Thanks for the pointer. You mentionned in your other mail that it was
wired on 24bits but that the screen is actually 18 bits. I went to
search for the schematics to look at the wirings to see what could
differ, but I couldn't find any for the M28EVK. Are they publicly available?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply

* Re: [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Maxime Ripard @ 2013-04-22 10:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201304221108.17339.jbe@pengutronix.de>

Hi Juergen,

Le 22/04/2013 11:08, Juergen Beisert a écrit :
> Hi Maxime,
> 
> Maxime Ripard wrote:
>> The current code always registers as a 32 bits display, and uses the
>> hardware to drop the MSB of each color to abjust to the interface width
>> used by the panel.
>>
>> This results on 18 bits (and probably 16 bits display as well) in colors
>> being displayed poorly, because the MSB are obviously the most important
>> bits for each color definition.
> 
> But the "def_rgb666" bitfield description reports correctly to use bit 0..5 
> for each colour component in this mode. Maybe your userland program has a bug 
> and forgets to honor the bitfield description?

Hmmm, that might be the case actually, yes, I'll check into this and let
you know. Thanks!

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH 4/4] ssd1307fb: Make use of horizontal addressing mode
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

By default, the ssd1307 controller uses an addressing mode called page
addressing. This mode only increments the column cursor in memory when
writing data but will not increments the page cursor when we are at the
end of the page.

However, the controller supports another addressing mode, called
horizontal addressing, that will maintain both the page and column
cursors when writing data to the controller.

That means that we can just remove the code that increments the current
page address and reset the column cursor when reaching the end of the
line, allowing to have a lower data overhead, and a simpler driver.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/video/ssd1307fb.c |   51 +++++++++++++++++++++++++++++++++------------
 1 file changed, 38 insertions(+), 13 deletions(-)

diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index cab5f7c..259c9ee 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -19,6 +19,12 @@
 #define SSD1307FB_DATA			0x40
 #define SSD1307FB_COMMAND		0x80
 
+#define SSD1307FB_SET_ADDRESS_MODE	0x20
+#define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL	(0x00)
+#define SSD1307FB_SET_ADDRESS_MODE_VERTICAL	(0x01)
+#define SSD1307FB_SET_ADDRESS_MODE_PAGE		(0x02)
+#define SSD1307FB_SET_COL_RANGE		0x21
+#define SSD1307FB_SET_PAGE_RANGE	0x22
 #define SSD1307FB_CONTRAST		0x81
 #define	SSD1307FB_CHARGE_PUMP		0x8d
 #define SSD1307FB_SEG_REMAP_ON		0xa1
@@ -135,9 +141,15 @@ static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
 
 static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 {
+	struct ssd1307fb_array *array;
 	u8 *vmem = par->info->screen_base;
 	int i, j, k;
 
+	array = ssd1307fb_alloc_array(par->width * par->height / 8,
+				      SSD1307FB_DATA);
+	if (!array)
+		return;
+
 	/*
 	 * The screen is divided in pages, each having a height of 8
 	 * pixels, and the width of the screen. When sending a byte of
@@ -168,29 +180,22 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 	 */
 
 	for (i = 0; i < (par->height / 8); i++) {
-		struct ssd1307fb_array *array;
-		ssd1307fb_write_cmd(par->client,
-				    SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset);
-		ssd1307fb_write_cmd(par->client, 0x00);
-		ssd1307fb_write_cmd(par->client, 0x10);
-
-		array = ssd1307fb_alloc_array(par->width, SSD1307FB_DATA);
-
 		for (j = 0; j < par->width; j++) {
-			array->data[j] = 0;
+			u32 array_idx = i * par->width + j;
+			array->data[array_idx] = 0;
 			for (k = 0; k < 8; k++) {
 				u32 page_length = par->width * i;
 				u32 index = page_length + (par->width * k + j) / 8;
 				u8 byte = *(vmem + index);
 				u8 bit = byte & (1 << (j % 8));
 				bit = bit >> (j % 8);
-				array->data[j] |= bit << k;
+				array->data[array_idx] |= bit << k;
 			}
 		}
-
-		ssd1307fb_write_array(par->client, array, par->width);
-		kfree(array);
 	}
+
+	ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
+	kfree(array);
 }
 
 
@@ -371,6 +376,26 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
 	if (ret < 0)
 		return ret;
 
+	/* Switch to horizontal addressing mode */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE);
+	ret = ret & ssd1307fb_write_cmd(par->client,
+					SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL);
+	if (ret < 0)
+		return ret;
+
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
+	ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1);
+	if (ret < 0)
+		return ret;
+
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x0);
+	ret = ret & ssd1307fb_write_cmd(par->client,
+					par->page_offset + (par->height / 8) - 1);
+	if (ret < 0)
+		return ret;
+
 	/* Turn on the display */
 	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
 	if (ret < 0)
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 3/4] ssd1307fb: Speed up the communication with the controller
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

The code until now was sending only 1pixel-wide page segment at once,
and started a new transfer every time. It has proven very inefficient,
because for one byte to display on the screen, we had to actually send 3
bytes over I2C: the address, the type of data that was going to the
controller, and then the actual data.

This patches changes that by sending a whole page at once, avoiding most
of this expensive overhead.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/video/ssd1307fb.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 35f5348..cab5f7c 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -168,23 +168,28 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 	 */
 
 	for (i = 0; i < (par->height / 8); i++) {
+		struct ssd1307fb_array *array;
 		ssd1307fb_write_cmd(par->client,
 				    SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset);
 		ssd1307fb_write_cmd(par->client, 0x00);
 		ssd1307fb_write_cmd(par->client, 0x10);
 
+		array = ssd1307fb_alloc_array(par->width, SSD1307FB_DATA);
+
 		for (j = 0; j < par->width; j++) {
-			u8 buf = 0;
+			array->data[j] = 0;
 			for (k = 0; k < 8; k++) {
 				u32 page_length = par->width * i;
 				u32 index = page_length + (par->width * k + j) / 8;
 				u8 byte = *(vmem + index);
 				u8 bit = byte & (1 << (j % 8));
 				bit = bit >> (j % 8);
-				buf |= bit << k;
+				array->data[j] |= bit << k;
 			}
-			ssd1307fb_write_data(par->client, buf);
 		}
+
+		ssd1307fb_write_array(par->client, array, par->width);
+		kfree(array);
 	}
 }
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 2/4] ssd1307fb: Rework the communication functions
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

To efficiently send a whole page to the display, we need to be able to
manipulate more easily the data arrays that has to be sent to the OLED
controller. As such, this patch introduces a ssd1307fb_array structure
that handles both the small header to be sent over i2c, which contains
the type of information sent, and the raw bytes after that.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/video/ssd1307fb.c |   77 +++++++++++++++++++++++++++++----------------
 1 file changed, 50 insertions(+), 27 deletions(-)

diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 5ab5281..35f5348 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -51,6 +51,11 @@ struct ssd1307fb_par {
 	u32 width;
 };
 
+struct ssd1307fb_array {
+	u8	type;
+	u8	data[0];
+};
+
 static struct fb_fix_screeninfo ssd1307fb_fix = {
 	.id		= "Solomon SSD1307",
 	.type		= FB_TYPE_PACKED_PIXELS,
@@ -65,49 +70,67 @@ static struct fb_var_screeninfo ssd1307fb_var = {
 	.bits_per_pixel	= 1,
 };
 
-static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len)
+static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type)
 {
-	u8 *buf;
-	int ret = 0;
-
-	buf = kzalloc(len + 1, GFP_KERNEL);
-	if (!buf) {
-		dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
-		return -ENOMEM;
-	}
+	struct ssd1307fb_array *array;
 
-	buf[0] = type;
-	memcpy(buf + 1, cmd, len);
+	array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL);
+	if (!array)
+		return NULL;
 
-	ret = i2c_master_send(client, buf, len + 1);
-	if (ret != len + 1) {
-		dev_err(&client->dev, "Couldn't send I2C command.\n");
-		goto error;
-	}
+	array->type = type;
 
-error:
-	kfree(buf);
-	return ret;
+	return array;
 }
 
-static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len)
+static int ssd1307fb_write_array(struct i2c_client *client,
+				 struct ssd1307fb_array *array, u32 len)
 {
-	return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
+	int ret;
+
+	len += sizeof(struct ssd1307fb_array);
+
+	ret = i2c_master_send(client, (u8 *)array, len);
+	if (ret != len) {
+		dev_err(&client->dev, "Couldn't send I2C command.\n");
+		return ret;
+	}
+
+	return 0;
 }
 
 static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
 {
-	return ssd1307fb_write_cmd_array(client, &cmd, 1);
-}
+	struct ssd1307fb_array *array;
+	int ret;
 
-static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len)
-{
-	return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
+	array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND);
+	if (!array)
+		return -ENOMEM;
+
+	array->data[0] = cmd;
+
+	ret = ssd1307fb_write_array(client, array, 1);
+	kfree(array);
+
+	return ret;
 }
 
 static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
 {
-	return ssd1307fb_write_data_array(client, &data, 1);
+	struct ssd1307fb_array *array;
+	int ret;
+
+	array = ssd1307fb_alloc_array(1, SSD1307FB_DATA);
+	if (!array)
+		return -ENOMEM;
+
+	array->data[0] = data;
+
+	ret = ssd1307fb_write_array(client, array, 1);
+	kfree(array);
+
+	return ret;
 }
 
 static void ssd1307fb_update_display(struct ssd1307fb_par *par)
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 1/4] ARM: cfa10036: dt: Change i2c0 clock frequency
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624945-5708-1-git-send-email-maxime.ripard@free-electrons.com>

The OLED display can work faster. Change the i2c controller clock
frequency to remove the tearing effect that can be seen on the display.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/imx28-cfa10036.dts |    1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index 40488f5a8..a524caf 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -67,6 +67,7 @@
 			i2c0: i2c@80058000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&i2c0_pins_b>;
+				clock-frequency = <400000>;
 				status = "okay";
 
 				ssd1306: oled@3c {
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 0/4] drivers: video: Optimisations for the ssd1307fb driver
From: Maxime Ripard @ 2013-04-22 10:02 UTC (permalink / raw)
  To: linux-arm-kernel

Hi everyone,

This is a patchset to improve significantly (by an order of magnitude)
the refreshing time of the SSD1306 and SSD1307.

We do so by sending the pixels by batches, instead of 8 at a time,
combined with some additionnal features found on these controllers
that allows to send a whole screen content at once.

This obviously removes the tearing effect that was previously seen
with these controllers.

This patchset depends on the "Add support for the Solomon SSD1306 OLED
Controller" I sent previously.

Thanks,
Maxime

Maxime Ripard (4):
  ARM: cfa10036: dt: Change i2c0 clock frequency
  ssd1307fb: Rework the communication functions
  ssd1307fb: Speed up the communication with the controller
  ssd1307fb: Make use of horizontal addressing mode

 arch/arm/boot/dts/imx28-cfa10036.dts |    1 +
 drivers/video/ssd1307fb.c            |  123 ++++++++++++++++++++++++----------
 2 files changed, 89 insertions(+), 35 deletions(-)

-- 
1.7.10.4


^ permalink raw reply

* [PATCH 2/2] ARM: dts: cfa10036: Change the OLED display to SSD1306
From: Maxime Ripard @ 2013-04-22  9:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624555-5616-1-git-send-email-maxime.ripard@free-electrons.com>

The SSD1307 was used in an early prototype that will never get
distributed. The final board now has a SSD1306 instead, that has its own
power generation unit and thus doesn't need any PWM. The panel attached
to it also changed.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/imx28-cfa10036.dts |   14 +++++---------
 arch/arm/boot/dts/imx28-cfa10049.dts |    4 ++--
 2 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index 1594694..40488f5a8 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -58,12 +58,6 @@
 		};
 
 		apbx@80040000 {
-			pwm: pwm@80064000 {
-				pinctrl-names = "default";
-				pinctrl-0 = <&pwm4_pins_a>;
-				status = "okay";
-			};
-
 			duart: serial@80074000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&duart_pins_b>;
@@ -75,11 +69,13 @@
 				pinctrl-0 = <&i2c0_pins_b>;
 				status = "okay";
 
-				ssd1307: oled@3c {
-					compatible = "solomon,ssd1307fb-i2c";
+				ssd1306: oled@3c {
+					compatible = "solomon,ssd1306fb-i2c";
 					reg = <0x3c>;
-					pwms = <&pwm 4 3000>;
 					reset-gpios = <&gpio2 7 0>;
+					solomon,height = <32>;
+					solomon,width = <128>;
+					solomon,page-offset = <0>;
 				};
 			};
 		};
diff --git a/arch/arm/boot/dts/imx28-cfa10049.dts b/arch/arm/boot/dts/imx28-cfa10049.dts
index a0d3e9f..ded7784 100644
--- a/arch/arm/boot/dts/imx28-cfa10049.dts
+++ b/arch/arm/boot/dts/imx28-cfa10049.dts
@@ -132,8 +132,8 @@
 
 		apbx@80040000 {
 			pwm: pwm@80064000 {
-				pinctrl-names = "default", "default";
-				pinctrl-1 = <&pwm3_pins_b>;
+				pinctrl-names = "default";
+				pinctrl-0 = <&pwm3_pins_b>;
 				status = "okay";
 			};
 
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH 1/2] video: ssd1307fb: Add support for SSD1306 OLED controller
From: Maxime Ripard @ 2013-04-22  9:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366624555-5616-1-git-send-email-maxime.ripard@free-electrons.com>

The Solomon SSD1306 OLED controller is very similar to the SSD1307,
except for the fact that the power is given through an external PWM for
the 1307, and while the 1306 can generate its own power without any PWM.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 .../devicetree/bindings/video/ssd1307fb.txt        |   10 +-
 drivers/video/ssd1307fb.c                          |  273 +++++++++++++++-----
 2 files changed, 209 insertions(+), 74 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
index 3d0060c..7a12542 100644
--- a/Documentation/devicetree/bindings/video/ssd1307fb.txt
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -1,13 +1,17 @@
 * Solomon SSD1307 Framebuffer Driver
 
 Required properties:
-  - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for
-    now is i2c.
+  - compatible: Should be "solomon,<chip>fb-<bus>". The only supported bus for
+    now is i2c, and the supported chips are ssd1306 and ssd1307.
   - reg: Should contain address of the controller on the I2C bus. Most likely
          0x3c or 0x3d
   - pwm: Should contain the pwm to use according to the OF device tree PWM
-         specification [0]
+         specification [0]. Only required for the ssd1307.
   - reset-gpios: Should contain the GPIO used to reset the OLED display
+  - solomon,height: Height in pixel of the screen driven by the controller
+  - solomon,width: Width in pixel of the screen driven by the controller
+  - solomon,page-offset: Offset of pages (band of 8 pixels) that the screen is
+    mapped to.
 
 Optional properties:
   - reset-active-low: Is the reset gpio is active on physical low?
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index 395cb6a..5ab5281 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -16,24 +16,39 @@
 #include <linux/pwm.h>
 #include <linux/delay.h>
 
-#define SSD1307FB_WIDTH			96
-#define SSD1307FB_HEIGHT		16
-
 #define SSD1307FB_DATA			0x40
 #define SSD1307FB_COMMAND		0x80
 
 #define SSD1307FB_CONTRAST		0x81
+#define	SSD1307FB_CHARGE_PUMP		0x8d
 #define SSD1307FB_SEG_REMAP_ON		0xa1
 #define SSD1307FB_DISPLAY_OFF		0xae
+#define SSD1307FB_SET_MULTIPLEX_RATIO	0xa8
 #define SSD1307FB_DISPLAY_ON		0xaf
 #define SSD1307FB_START_PAGE_ADDRESS	0xb0
+#define SSD1307FB_SET_DISPLAY_OFFSET	0xd3
+#define	SSD1307FB_SET_CLOCK_FREQ	0xd5
+#define	SSD1307FB_SET_PRECHARGE_PERIOD	0xd9
+#define	SSD1307FB_SET_COM_PINS_CONFIG	0xda
+#define	SSD1307FB_SET_VCOMH		0xdb
+
+struct ssd1307fb_par;
+
+struct ssd1307fb_ops {
+	int (*init)(struct ssd1307fb_par *);
+	int (*remove)(struct ssd1307fb_par *);
+};
 
 struct ssd1307fb_par {
 	struct i2c_client *client;
+	u32 height;
 	struct fb_info *info;
+	struct ssd1307fb_ops *ops;
+	u32 page_offset;
 	struct pwm_device *pwm;
 	u32 pwm_period;
 	int reset;
+	u32 width;
 };
 
 static struct fb_fix_screeninfo ssd1307fb_fix = {
@@ -43,15 +58,10 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
 	.xpanstep	= 0,
 	.ypanstep	= 0,
 	.ywrapstep	= 0,
-	.line_length	= SSD1307FB_WIDTH / 8,
 	.accel		= FB_ACCEL_NONE,
 };
 
 static struct fb_var_screeninfo ssd1307fb_var = {
-	.xres		= SSD1307FB_WIDTH,
-	.yres		= SSD1307FB_HEIGHT,
-	.xres_virtual	= SSD1307FB_WIDTH,
-	.yres_virtual	= SSD1307FB_HEIGHT,
 	.bits_per_pixel	= 1,
 };
 
@@ -134,16 +144,17 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 	 *  (5) A4 B4 C4 D4 E4 F4 G4 H4
 	 */
 
-	for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
-		ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
+	for (i = 0; i < (par->height / 8); i++) {
+		ssd1307fb_write_cmd(par->client,
+				    SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset);
 		ssd1307fb_write_cmd(par->client, 0x00);
 		ssd1307fb_write_cmd(par->client, 0x10);
 
-		for (j = 0; j < SSD1307FB_WIDTH; j++) {
+		for (j = 0; j < par->width; j++) {
 			u8 buf = 0;
 			for (k = 0; k < 8; k++) {
-				u32 page_length = SSD1307FB_WIDTH * i;
-				u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
+				u32 page_length = par->width * i;
+				u32 index = page_length + (par->width * k + j) / 8;
 				u8 byte = *(vmem + index);
 				u8 bit = byte & (1 << (j % 8));
 				bit = bit >> (j % 8);
@@ -227,16 +238,147 @@ static struct fb_deferred_io ssd1307fb_defio = {
 	.deferred_io	= ssd1307fb_deferred_io,
 };
 
+static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par)
+{
+	int ret;
+
+	par->pwm = pwm_get(&par->client->dev, NULL);
+	if (IS_ERR(par->pwm)) {
+		dev_err(&par->client->dev, "Could not get PWM from device tree!\n");
+		return PTR_ERR(par->pwm);
+	}
+
+	par->pwm_period = pwm_get_period(par->pwm);
+	/* Enable the PWM */
+	pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+	pwm_enable(par->pwm);
+
+	dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
+		par->pwm->pwm, par->pwm_period);
+
+	/* Map column 127 of the OLED to segment 0 */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+	if (ret < 0)
+		return ret;
+
+	/* Turn on the display */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par)
+{
+	pwm_disable(par->pwm);
+	pwm_put(par->pwm);
+	return 0;
+}
+
+static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = {
+	.init	= ssd1307fb_ssd1307_init,
+	.remove	= ssd1307fb_ssd1307_remove,
+};
+
+static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par)
+{
+	int ret;
+
+	/* Set initial contrast */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x7f);
+	if (ret < 0)
+		return ret;
+
+	/* Set COM direction */
+	ret = ssd1307fb_write_cmd(par->client, 0xc8);
+	if (ret < 0)
+		return ret;
+
+	/* Set segment re-map */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON);
+	if (ret < 0)
+		return ret;
+
+	/* Set multiplex ratio value */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO);
+	ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1);
+	if (ret < 0)
+		return ret;
+
+	/* set display offset value */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET);
+	ret = ssd1307fb_write_cmd(par->client, 0x20);
+	if (ret < 0)
+		return ret;
+
+	/* Set clock frequency */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0xf0);
+	if (ret < 0)
+		return ret;
+
+	/* Set precharge period in number of ticks from the internal clock */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+	if (ret < 0)
+		return ret;
+
+	/* Set COM pins configuration */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x22);
+	if (ret < 0)
+		return ret;
+
+	/* Set VCOMH */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x49);
+	if (ret < 0)
+		return ret;
+
+	/* Turn on the DC-DC Charge Pump */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP);
+	ret = ret & ssd1307fb_write_cmd(par->client, 0x14);
+	if (ret < 0)
+		return ret;
+
+	/* Turn on the display */
+	ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = {
+	.init	= ssd1307fb_ssd1306_init,
+};
+
+static const struct of_device_id ssd1307fb_of_match[] = {
+	{
+		.compatible = "solomon,ssd1306fb-i2c",
+		.data = (void *)&ssd1307fb_ssd1306_ops,
+	},
+	{
+		.compatible = "solomon,ssd1307fb-i2c",
+		.data = (void *)&ssd1307fb_ssd1307_ops,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
+
 static int ssd1307fb_probe(struct i2c_client *client,
 			   const struct i2c_device_id *id)
 {
 	struct fb_info *info;
-	u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
+	struct device_node *node = client->dev.of_node;
+	u32 vmem_size;
 	struct ssd1307fb_par *par;
 	u8 *vmem;
 	int ret;
 
-	if (!client->dev.of_node) {
+	if (!node) {
 		dev_err(&client->dev, "No device tree data found!\n");
 		return -EINVAL;
 	}
@@ -247,6 +389,31 @@ static int ssd1307fb_probe(struct i2c_client *client,
 		return -ENOMEM;
 	}
 
+	par = info->par;
+	par->info = info;
+	par->client = client;
+
+	par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match,
+							   &client->dev)->data;
+
+	par->reset = of_get_named_gpio(client->dev.of_node,
+					 "reset-gpios", 0);
+	if (!gpio_is_valid(par->reset)) {
+		ret = -EINVAL;
+		goto fb_alloc_error;
+	}
+
+	if (of_property_read_u32(node, "solomon,width", &par->width))
+		par->width = 96;
+
+	if (of_property_read_u32(node, "solomon,height", &par->height))
+		par->width = 16;
+
+	if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset))
+		par->page_offset = 1;
+
+	vmem_size = par->width * par->height / 8;
+
 	vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
 	if (!vmem) {
 		dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
@@ -256,9 +423,15 @@ static int ssd1307fb_probe(struct i2c_client *client,
 
 	info->fbops = &ssd1307fb_ops;
 	info->fix = ssd1307fb_fix;
+	info->fix.line_length = par->width / 8;
 	info->fbdefio = &ssd1307fb_defio;
 
 	info->var = ssd1307fb_var;
+	info->var.xres = par->width;
+	info->var.xres_virtual = par->width;
+	info->var.yres = par->height;
+	info->var.yres_virtual = par->height;
+
 	info->var.red.length = 1;
 	info->var.red.offset = 0;
 	info->var.green.length = 1;
@@ -272,17 +445,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
 
 	fb_deferred_io_init(info);
 
-	par = info->par;
-	par->info = info;
-	par->client = client;
-
-	par->reset = of_get_named_gpio(client->dev.of_node,
-					 "reset-gpios", 0);
-	if (!gpio_is_valid(par->reset)) {
-		ret = -EINVAL;
-		goto reset_oled_error;
-	}
-
 	ret = devm_gpio_request_one(&client->dev, par->reset,
 				    GPIOF_OUT_INIT_HIGH,
 				    "oled-reset");
@@ -293,23 +455,6 @@ static int ssd1307fb_probe(struct i2c_client *client,
 		goto reset_oled_error;
 	}
 
-	par->pwm = pwm_get(&client->dev, NULL);
-	if (IS_ERR(par->pwm)) {
-		dev_err(&client->dev, "Could not get PWM from device tree!\n");
-		ret = PTR_ERR(par->pwm);
-		goto pwm_error;
-	}
-
-	par->pwm_period = pwm_get_period(par->pwm);
-
-	dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
-
-	ret = register_framebuffer(info);
-	if (ret) {
-		dev_err(&client->dev, "Couldn't register the framebuffer\n");
-		goto fbreg_error;
-	}
-
 	i2c_set_clientdata(client, info);
 
 	/* Reset the screen */
@@ -318,34 +463,25 @@ static int ssd1307fb_probe(struct i2c_client *client,
 	gpio_set_value(par->reset, 1);
 	udelay(4);
 
-	/* Enable the PWM */
-	pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
-	pwm_enable(par->pwm);
-
-	/* Map column 127 of the OLED to segment 0 */
-	ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
-	if (ret < 0) {
-		dev_err(&client->dev, "Couldn't remap the screen.\n");
-		goto remap_error;
+	if (par->ops->init) {
+		ret = par->ops->init(par);
+		if (ret)
+			goto reset_oled_error;
 	}
 
-	/* Turn on the display */
-	ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
-	if (ret < 0) {
-		dev_err(&client->dev, "Couldn't turn the display on.\n");
-		goto remap_error;
+	ret = register_framebuffer(info);
+	if (ret) {
+		dev_err(&client->dev, "Couldn't register the framebuffer\n");
+		goto panel_init_error;
 	}
 
 	dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
 
 	return 0;
 
-remap_error:
-	unregister_framebuffer(info);
-	pwm_disable(par->pwm);
-fbreg_error:
-	pwm_put(par->pwm);
-pwm_error:
+panel_init_error:
+	if (par->ops->remove)
+		par->ops->remove(par);
 reset_oled_error:
 	fb_deferred_io_cleanup(info);
 fb_alloc_error:
@@ -359,8 +495,8 @@ static int ssd1307fb_remove(struct i2c_client *client)
 	struct ssd1307fb_par *par = info->par;
 
 	unregister_framebuffer(info);
-	pwm_disable(par->pwm);
-	pwm_put(par->pwm);
+	if (par->ops->remove)
+		par->ops->remove(par);
 	fb_deferred_io_cleanup(info);
 	framebuffer_release(info);
 
@@ -368,17 +504,12 @@ static int ssd1307fb_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+	{ "ssd1306fb", 0 },
 	{ "ssd1307fb", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
 
-static const struct of_device_id ssd1307fb_of_match[] = {
-	{ .compatible = "solomon,ssd1307fb-i2c" },
-	{},
-};
-MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
-
 static struct i2c_driver ssd1307fb_driver = {
 	.probe = ssd1307fb_probe,
 	.remove = ssd1307fb_remove,
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv2 0/2] Add support for the Solomon SSD1306 OLED Controller
From: Maxime Ripard @ 2013-04-22  9:55 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

This patchset adds the needed bits to the ssd1307fb driver to support the
very similar Solomon SSD1306 controller.

Since the SSD1307 has been replaced by the SSD1306 in the latest CFA-10036
production samples, we also need to change the device tree accordingly.

Thanks,
Maxime

Changes from v1:
  - Fix style errors

Maxime Ripard (2):
  video: ssd1307fb: Add support for SSD1306 OLED controller
  ARM: dts: cfa10036: Change the OLED display to SSD1306

 .../devicetree/bindings/video/ssd1307fb.txt        |   10 +-
 arch/arm/boot/dts/imx28-cfa10036.dts               |   14 +-
 arch/arm/boot/dts/imx28-cfa10049.dts               |    4 +-
 drivers/video/ssd1307fb.c                          |  273 +++++++++++++++-----
 4 files changed, 216 insertions(+), 85 deletions(-)

-- 
1.7.10.4


^ permalink raw reply

* Re: [PATCH RESEND] video: mxsfb: Fix colors display on lower color depth
From: Marek Vasut @ 2013-04-22  9:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1366618310-7068-1-git-send-email-maxime.ripard@free-electrons.com>

Dear Maxime Ripard,

> The current code always registers as a 32 bits display, and uses the
> hardware to drop the MSB of each color to abjust to the interface width
> used by the panel.
> 
> This results on 18 bits (and probably 16 bits display as well) in colors
> being displayed poorly, because the MSB are obviously the most important
> bits for each color definition.
> 
> The default controller behaviour when using an interface width smaller
> than the color depth is to drop the LSBs of each color, which makes more
> sense because you lose the least important part of the color definition.
> 
> So, to fix the colors display, just get back to the default controller
> behaviour.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Did you receive my latest email? Check M28EVK (imx28-m28evk.dts), it uses 18bit 
LCD and works without this patch I think.

Best regards,
Marek Vasut

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox