Hi,
I found current gma500 fbdev driver
does not support the virtual
mapping for the fb pages, instead it
only uses stolen pages and
supports high resolution console by
reducing the color depth. It
works well with fbcon for high resolution
envirnment.
However, other programs, such as plymouth(a
boot animation program)
may also use fbdev driver to flush screen
in high resolution
envirnment, in that case, reducing the
color depth will decrease the
quality or result in other problem.
Noticed that Alan's patch(commit
dffc9ceb55695f121adc57dd1fde7304c3afe81e)
killed the virtual mapping
support for short of vmap space. But
the vmalloc space(128M) should
be enough for most high resolution envirnments.
So I'm considering add the virual mapping
support for fbdev driver.
The following patch has not done completely
yet, still having console
flicking problem need to be solved.
I'm not sure whether the patch will
be useful for others or if there
is something I missed. Need your comments.
Thanks.
Signed-off-by: Jiang Biao<jiang.biao2@zte.com.cn>
---
drivers/gpu/drm/gma500/framebuffer.c
| 110 ++++++++++++++++++++++-------------
drivers/gpu/drm/gma500/framebuffer.h
| 2 +
2 files changed, 73 insertions(+),
39 deletions(-)
diff --git a/drivers/gpu/drm/gma500/framebuffer.c
b/drivers/gpu/drm/gma500/framebuffer.c
index 0fcdce0..21f14df 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -117,34 +117,43 @@ static int psbfb_vm_fault(struct
vm_area_struct *vma, struct vm_fault *vmf)
struct
psb_framebuffer *psbfb = vma->vm_private_data;
struct
drm_device *dev = psbfb->base.dev;
struct
drm_psb_private *dev_priv = dev->dev_private;
- int
page_num;
- int
i;
- unsigned
long address;
int
ret;
unsigned
long pfn;
- unsigned
long phys_addr = (unsigned long)dev_priv->stolen_base +
-
psbfb->gtt->offset;
-
- page_num
= (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
- address
= (unsigned long)vmf->virtual_address - (vmf->pgoff << PAGE_SHIFT);
-
- vma->vm_page_prot
= pgprot_noncached(vma->vm_page_prot);
-
- for
(i = 0; i < page_num; i++) {
-
pfn = (phys_addr >> PAGE_SHIFT);
-
-
ret = vm_insert_mixed(vma, address,
-
__pfn_to_pfn_t(pfn, PFN_DEV));
-
if (unlikely((ret == -EBUSY) || (ret
!= 0 && i > 0)))
-
break;
-
else if (unlikely(ret != 0)) {
-
ret
= (ret == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS;
-
return
ret;
+ struct
gtt_range *r;
+ pgoff_t
page_offset;
+ r
= psbfb->gtt;
+
+ mutex_lock(&dev->struct_mutex);
+ if
(r->mmapping == 0) {
+
ret = psb_gtt_pin(r);
+
if (ret < 0) {
+
goto fail;
}
-
address += PAGE_SIZE;
-
phys_addr += PAGE_SIZE;
+
r->mmapping = 1;
+ }
+
+ page_offset
= ((unsigned long) vmf->virtual_address - vma->vm_start)
+
>> PAGE_SHIFT;
+
+ if
(r->stolen)
+
pfn = (dev_priv->stolen_base + r->offset)
>> PAGE_SHIFT;
+ else
+
pfn = page_to_pfn(r->pages[page_offset]);
+
+ ret
= vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
+
+fail:
+ mutex_unlock(&dev->struct_mutex);
+ switch
(ret) {
+ case
0:
+ case
-ERESTARTSYS:
+ case
-EINTR:
+
return VM_FAULT_NOPAGE;
+ case
-ENOMEM:
+
return VM_FAULT_OOM;
+ default:
+
return VM_FAULT_SIGBUS;
}
- return
VM_FAULT_NOPAGE;
}
static void psbfb_vm_open(struct
vm_area_struct *vma)
@@ -180,7 +189,7 @@ static int psbfb_mmap(struct
fb_info *info, struct vm_area_struct *vma)
*/
vma->vm_ops
= &psbfb_vm_ops;
vma->vm_private_data
= (void *)psbfb;
- vma->vm_flags
|= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_flags
|= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP;
return
0;
}
@@ -317,7 +326,17 @@ static struct gtt_range
*psbfb_alloc(struct drm_device *dev, int aligned_size)
drm_gem_private_object_init(dev, &backing->gem,
aligned_size);
return backing;
}
- return
NULL;
+ /*
Next try using GEM host memory */
+ backing
= psb_gtt_alloc_range(dev, aligned_size, "fb(gem)", 0, PAGE_SIZE);
+ if (backing
== NULL)
+
return NULL;
+
+ /* Now
back it with an object */
+ if (drm_gem_object_init(dev,
&backing->gem, aligned_size) != 0) {
+
psb_gtt_free_range(dev, backing);
+
return NULL;
+ }
+ return
backing;
}
/**
@@ -397,7 +416,8 @@ static int psbfb_create(struct
psb_fbdev *fbdev,
return
-ENOMEM;
}
- memset(dev_priv->vram_addr
+ backing->offset, 0, size);
+ if
(backing->stolen)
+
memset(dev_priv->vram_addr + backing->offset,
0, size);
info
= drm_fb_helper_alloc_fbi(&fbdev->psb_fb_helper);
if
(IS_ERR(info)) {
@@ -434,8 +454,20 @@ static int psbfb_create(struct
psb_fbdev *fbdev,
info->fix.ywrapstep
= gtt_roll;
info->fix.ypanstep
= 0;
- /*
Accessed stolen memory directly */
- info->screen_base
= dev_priv->vram_addr + backing->offset;
+ if
(backing->stolen) {
+
info->screen_base = dev_priv->vram_addr
+ backing->offset;
+ }
else {
+
psb_gtt_pin(backing);
+
backing->mapping = 1;
+
info->screen_base = vm_map_ram(backing->pages,
backing->npage, -1, PAGE_KERNEL);
+
if (info->screen_base == NULL) {
+
ret
= -ENOMEM;
+
goto
err_free_range;
+
}
+
psbfb->vm_map = 1;
+
+ }
+
info->screen_size
= size;
if
(dev_priv->gtt.stolen_size) {
@@ -458,7 +490,10 @@ static int psbfb_create(struct
psb_fbdev *fbdev,
err_release:
drm_fb_helper_release_fbi(&fbdev->psb_fb_helper);
err_free_range:
- psb_gtt_free_range(dev,
backing);
+ if
(backing->stolen)
+
psb_gtt_free_range(dev, backing);
+ else
if(psbfb->vm_map)
+
vm_unmap_ram(info->screen_base, backing->npage);
return
ret;
}
@@ -523,15 +558,6 @@ static int psbfb_probe(struct
drm_fb_helper *helper,
if
(bytespp == 3) /* no 24bit packed */
bytespp = 4;
- /*
If the mode will not fit in 32bit then switch to 16bit to get
-
a console on full resolution. The X mode setting server will
-
allocate its own 32bit GEM framebuffer */
- if
(ALIGN(sizes->fb_width * bytespp, 64) * sizes->fb_height >
-
dev_priv->vram_stolen_size)
{
-
sizes->surface_bpp = 16;
-
sizes->surface_depth = 16;
- }
-
return
psbfb_create(psb_fbdev, sizes);
}
@@ -545,6 +571,12 @@ static int psb_fbdev_destroy(struct
drm_device *dev, struct psb_fbdev *fbdev)
{
struct
psb_framebuffer *psbfb = &fbdev->pfb;
+ info
= fbdev->psb_fb_helper.fbdev;
+ if
(psbfb->vm_map) {
+
vm_unmap_ram(info->screen_base, psbfb->gtt->npage);
+
psb_gtt_unpin(psbfb->gtt);
+ }
+
drm_fb_helper_unregister_fbi(&fbdev->psb_fb_helper);
drm_fb_helper_release_fbi(&fbdev->psb_fb_helper);
diff --git a/drivers/gpu/drm/gma500/framebuffer.h
b/drivers/gpu/drm/gma500/framebuffer.h
index 395f20b..50b384e 100644
--- a/drivers/gpu/drm/gma500/framebuffer.h
+++ b/drivers/gpu/drm/gma500/framebuffer.h
@@ -32,6 +32,8 @@ struct psb_framebuffer
{
struct
address_space *addr_space;
struct
fb_info *fbdev;
struct
gtt_range *gtt;
+ bool
vm_map;
+
};
struct psb_fbdev {
--
2.1.0