drm/rockchip: gem: reorder pages if page chunk less than 8

Change-Id: I03a91d2f9c017086b3cb35edeaf6b7913b147b9b
Signed-off-by: Simon Xue <xxm@rock-chips.com>
This commit is contained in:
Simon Xue
2021-04-07 17:07:29 +08:00
committed by Tao Huang
parent e381404955
commit 773a33a1cc

View File

@@ -16,6 +16,13 @@
#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
struct page_info {
struct page *page;
struct list_head list;
};
#define PG_ROUND 8
static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
{
struct drm_device *drm = rk_obj->base.dev;
@@ -73,23 +80,119 @@ static int rockchip_gem_iommu_unmap(struct rockchip_gem_object *rk_obj)
return 0;
}
static void rockchip_gem_free_list(struct list_head lists[])
{
struct page_info *info, *tmp_info;
int i;
for (i = 0; i < PG_ROUND; i++) {
list_for_each_entry_safe(info, tmp_info, &lists[i], list) {
list_del(&info->list);
kfree(info);
}
}
}
static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
{
struct drm_device *drm = rk_obj->base.dev;
int ret, i;
struct scatterlist *s;
unsigned int cur_page;
struct page **pages, **dst_pages;
int j;
int n_pages;
unsigned long chunk_pages;
unsigned long remain;
struct list_head lists[PG_ROUND];
dma_addr_t phys;
int end = 0;
unsigned int bit12_14;
unsigned int block_index[PG_ROUND] = {0};
struct page_info *info;
unsigned int maximum;
rk_obj->pages = drm_gem_get_pages(&rk_obj->base);
if (IS_ERR(rk_obj->pages))
return PTR_ERR(rk_obj->pages);
for (i = 0; i < PG_ROUND; i++)
INIT_LIST_HEAD(&lists[i]);
pages = drm_gem_get_pages(&rk_obj->base);
if (IS_ERR(pages))
return PTR_ERR(pages);
rk_obj->pages = pages;
rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
n_pages = rk_obj->num_pages;
dst_pages = __vmalloc(sizeof(struct page *) * n_pages,
GFP_KERNEL | __GFP_HIGHMEM);
if (!dst_pages) {
ret = -ENOMEM;
goto err_put_pages;
}
cur_page = 0;
remain = n_pages;
/* look for the end of the current chunk */
while (remain) {
for (j = cur_page + 1; j < n_pages; ++j) {
if (page_to_pfn(pages[j]) !=
page_to_pfn(pages[j - 1]) + 1)
break;
}
chunk_pages = j - cur_page;
if (chunk_pages > 7) {
for (i = 0; i < chunk_pages; i++)
dst_pages[end + i] = pages[cur_page + i];
end += chunk_pages;
} else {
for (i = 0; i < chunk_pages; i++) {
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
ret = -ENOMEM;
goto err_put_list;
}
INIT_LIST_HEAD(&info->list);
info->page = pages[cur_page + i];
phys = page_to_phys(info->page);
bit12_14 = (phys >> 12) & 0x7;
list_add_tail(&info->list, &lists[bit12_14]);
block_index[bit12_14]++;
}
}
cur_page = j;
remain -= chunk_pages;
}
maximum = block_index[0];
for (i = 1; i < PG_ROUND; i++)
maximum = max(maximum, block_index[i]);
for (i = 0; i < maximum; i++) {
for (j = 0; j < PG_ROUND; j++) {
if (!list_empty(&lists[j])) {
struct page_info *info;
info = list_first_entry(&lists[j],
struct page_info, list);
dst_pages[end++] = info->page;
list_del(&info->list);
kfree(info);
}
}
}
DRM_DEBUG_KMS("%s, %d, end = %d, n_pages = %d\n", __func__, __LINE__,
end, n_pages);
rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->base.dev,
rk_obj->pages, rk_obj->num_pages);
dst_pages, rk_obj->num_pages);
if (IS_ERR(rk_obj->sgt)) {
ret = PTR_ERR(rk_obj->sgt);
goto err_put_pages;
goto err_put_list;
}
/*
@@ -104,8 +207,13 @@ static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
dma_sync_sgtable_for_device(drm->dev, rk_obj->sgt, DMA_TO_DEVICE);
kvfree(pages);
return 0;
err_put_list:
rockchip_gem_free_list(lists);
kvfree(dst_pages);
err_put_pages:
drm_gem_put_pages(&rk_obj->base, rk_obj->pages, false, false);
return ret;