drm-misc-next for 5.11:
UAPI Changes: Cross-subsystem Changes: * char/agp: Disable frontend without CONFIG_DRM_LEGACY * mm: Fix fput in mmap error path; Introduce vma_set_file() to change vma->vm_file Core Changes: * dma-buf: Use sgtables in system heap; Move heap helpers to CMA-heap code; Skip sync for unmapped buffers; Alloc higher order pages is available; Respect num_fences when initializing shared fence list * doc: Improvements around DRM modes and SCALING_FILTER * Pass full state to connector atomic functions + callee updates * Cleanups * shmem: Map pages with caching by default; Cleanups * ttm: Fix DMA32 for global page pool * fbdev: Cleanups * fb-helper: Update framebuffer after userspace writes; Unmap console buffer during shutdown; Rework damage handling of shadow framebuffer Driver Changes: * amdgpu: Multi-hop fixes, Clenaups * imx: Fix rotation for Vivante tiled formats; Support nearest-neighour skaling; Cleanups * mcde: Fix RGB formats; Support DPI output; Cleanups * meson: HDMI clock fixes * panel: Add driver and bindings for Innolux N125HCE-GN1 * panel/s6e63m0: More backlight levels; Fix init; Cleanups * via: Clenunps * virtio: Use fence ID for handling fences; Cleanups -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAl/AuLYACgkQaA3BHVML eiMzvQgAmE1FGqUSNPGFjWpWN+hYDqEnpa3MXBVZ4sOqXxmzaI+SdnmexsaQ6v+F 37VSlk3OFa69gYV6kQp9NbfYc0E7TZ9CusU6WNcxo36GpInsWmuIHJI3BhlCgpKm hDDRylv5c4+e6RlGaz+yS73zkddmrMPi4HqarMP+8c2+UJxaDe1Huv22MKE7PJK3 noBck4Afelk51kn9bpeGj+WXvpEY+CMzXA7cvx1U26INZZyIdontZXJEeN6hAxzP yyhzOWPMH1X9Wfe8Qj1Ua9r27btG/0SKfhseovLU9Cjm1FGBjRiAnkxZua/ScwIL H7JcYplEkiLXuxnDAJOAZkvF6EcaJQ== =TatG -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2020-11-27-1' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for 5.11: UAPI Changes: Cross-subsystem Changes: * char/agp: Disable frontend without CONFIG_DRM_LEGACY * mm: Fix fput in mmap error path; Introduce vma_set_file() to change vma->vm_file Core Changes: * dma-buf: Use sgtables in system heap; Move heap helpers to CMA-heap code; Skip sync for unmapped buffers; Alloc higher order pages is available; Respect num_fences when initializing shared fence list * doc: Improvements around DRM modes and SCALING_FILTER * Pass full state to connector atomic functions + callee updates * Cleanups * shmem: Map pages with caching by default; Cleanups * ttm: Fix DMA32 for global page pool * fbdev: Cleanups * fb-helper: Update framebuffer after userspace writes; Unmap console buffer during shutdown; Rework damage handling of shadow framebuffer Driver Changes: * amdgpu: Multi-hop fixes, Clenaups * imx: Fix rotation for Vivante tiled formats; Support nearest-neighour skaling; Cleanups * mcde: Fix RGB formats; Support DPI output; Cleanups * meson: HDMI clock fixes * panel: Add driver and bindings for Innolux N125HCE-GN1 * panel/s6e63m0: More backlight levels; Fix init; Cleanups * via: Clenunps * virtio: Use fence ID for handling fences; Cleanups Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20201127083055.GA29139@linux-uq9g
This commit is contained in:
commit
5fbd41d3bf
Documentation/devicetree/bindings/display/panel
drivers
char/agp
dma-buf
gpu/drm
amd
drm_atomic_helper.cdrm_blend.cdrm_bufs.cdrm_client.cdrm_crtc.cdrm_fb_helper.cdrm_gem_shmem_helper.cetnaviv
i915
imx/dcss
lima
mcde
meson
mgag200
msm
nouveau/dispnv50
omapdrm
panel
panfrost
ttm
udl
v3d
vc4
vgem
via
virtio
vkms
staging/android
video/fbdev
include
drm
linux
uapi
mm
|
@ -159,6 +159,8 @@ properties:
|
|||
- innolux,g121x1-l03
|
||||
# Innolux Corporation 11.6" WXGA (1366x768) TFT LCD panel
|
||||
- innolux,n116bge
|
||||
# InnoLux 13.3" FHD (1920x1080) eDP TFT LCD panel
|
||||
- innolux,n125hce-gn1
|
||||
# InnoLux 15.6" WXGA TFT LCD panel
|
||||
- innolux,n156bge-l21
|
||||
# Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
agpgart-y := backend.o frontend.o generic.o isoch.o
|
||||
agpgart-y := backend.o generic.o isoch.o
|
||||
|
||||
ifeq ($(CONFIG_DRM_LEGACY),y)
|
||||
agpgart-$(CONFIG_COMPAT) += compat_ioctl.o
|
||||
agpgart-y += frontend.o
|
||||
endif
|
||||
|
||||
|
||||
obj-$(CONFIG_AGP) += agpgart.o
|
||||
obj-$(CONFIG_AGP_ALI) += ali-agp.o
|
||||
|
|
|
@ -186,8 +186,13 @@ int agp_add_bridge(struct agp_bridge_data *bridge);
|
|||
void agp_remove_bridge(struct agp_bridge_data *bridge);
|
||||
|
||||
/* Frontend routines. */
|
||||
#if IS_ENABLED(CONFIG_DRM_LEGACY)
|
||||
int agp_frontend_initialize(void);
|
||||
void agp_frontend_cleanup(void);
|
||||
#else
|
||||
static inline int agp_frontend_initialize(void) { return 0; }
|
||||
static inline void agp_frontend_cleanup(void) {}
|
||||
#endif
|
||||
|
||||
/* Generic routines. */
|
||||
void agp_generic_enable(struct agp_bridge_data *bridge, u32 mode);
|
||||
|
|
|
@ -1166,9 +1166,6 @@ EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access);
|
|||
int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
|
||||
unsigned long pgoff)
|
||||
{
|
||||
struct file *oldfile;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!dmabuf || !vma))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1186,22 +1183,10 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
|
|||
return -EINVAL;
|
||||
|
||||
/* readjust the vma */
|
||||
get_file(dmabuf->file);
|
||||
oldfile = vma->vm_file;
|
||||
vma->vm_file = dmabuf->file;
|
||||
vma_set_file(vma, dmabuf->file);
|
||||
vma->vm_pgoff = pgoff;
|
||||
|
||||
ret = dmabuf->ops->mmap(dmabuf, vma);
|
||||
if (ret) {
|
||||
/* restore old parameters on failure */
|
||||
vma->vm_file = oldfile;
|
||||
fput(dmabuf->file);
|
||||
} else {
|
||||
if (oldfile)
|
||||
fput(oldfile);
|
||||
}
|
||||
return ret;
|
||||
|
||||
return dmabuf->ops->mmap(dmabuf, vma);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_mmap);
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ int dma_resv_reserve_shared(struct dma_resv *obj, unsigned int num_fences)
|
|||
max = max(old->shared_count + num_fences,
|
||||
old->shared_max * 2);
|
||||
} else {
|
||||
max = 4;
|
||||
max = max(4ul, roundup_pow_of_two(num_fences));
|
||||
}
|
||||
|
||||
new = dma_resv_list_alloc(max);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y += heap-helpers.o
|
||||
obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += system_heap.o
|
||||
obj-$(CONFIG_DMABUF_HEAPS_CMA) += cma_heap.o
|
||||
|
|
|
@ -2,76 +2,305 @@
|
|||
/*
|
||||
* DMABUF CMA heap exporter
|
||||
*
|
||||
* Copyright (C) 2012, 2019 Linaro Ltd.
|
||||
* Copyright (C) 2012, 2019, 2020 Linaro Ltd.
|
||||
* Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
|
||||
*
|
||||
* Also utilizing parts of Andrew Davis' SRAM heap:
|
||||
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/cma.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-heap.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "heap-helpers.h"
|
||||
|
||||
struct cma_heap {
|
||||
struct dma_heap *heap;
|
||||
struct cma *cma;
|
||||
};
|
||||
|
||||
static void cma_heap_free(struct heap_helper_buffer *buffer)
|
||||
{
|
||||
struct cma_heap *cma_heap = dma_heap_get_drvdata(buffer->heap);
|
||||
unsigned long nr_pages = buffer->pagecount;
|
||||
struct page *cma_pages = buffer->priv_virt;
|
||||
struct cma_heap_buffer {
|
||||
struct cma_heap *heap;
|
||||
struct list_head attachments;
|
||||
struct mutex lock;
|
||||
unsigned long len;
|
||||
struct page *cma_pages;
|
||||
struct page **pages;
|
||||
pgoff_t pagecount;
|
||||
int vmap_cnt;
|
||||
void *vaddr;
|
||||
};
|
||||
|
||||
/* free page list */
|
||||
kfree(buffer->pages);
|
||||
/* release memory */
|
||||
cma_release(cma_heap->cma, cma_pages, nr_pages);
|
||||
struct dma_heap_attachment {
|
||||
struct device *dev;
|
||||
struct sg_table table;
|
||||
struct list_head list;
|
||||
bool mapped;
|
||||
};
|
||||
|
||||
static int cma_heap_attach(struct dma_buf *dmabuf,
|
||||
struct dma_buf_attachment *attachment)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a;
|
||||
int ret;
|
||||
|
||||
a = kzalloc(sizeof(*a), GFP_KERNEL);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sg_alloc_table_from_pages(&a->table, buffer->pages,
|
||||
buffer->pagecount, 0,
|
||||
buffer->pagecount << PAGE_SHIFT,
|
||||
GFP_KERNEL);
|
||||
if (ret) {
|
||||
kfree(a);
|
||||
return ret;
|
||||
}
|
||||
|
||||
a->dev = attachment->dev;
|
||||
INIT_LIST_HEAD(&a->list);
|
||||
a->mapped = false;
|
||||
|
||||
attachment->priv = a;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_add(&a->list, &buffer->attachments);
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cma_heap_detach(struct dma_buf *dmabuf,
|
||||
struct dma_buf_attachment *attachment)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_del(&a->list);
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
sg_free_table(&a->table);
|
||||
kfree(a);
|
||||
}
|
||||
|
||||
static struct sg_table *cma_heap_map_dma_buf(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
struct sg_table *table = &a->table;
|
||||
int ret;
|
||||
|
||||
ret = dma_map_sgtable(attachment->dev, table, direction, 0);
|
||||
if (ret)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
a->mapped = true;
|
||||
return table;
|
||||
}
|
||||
|
||||
static void cma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
|
||||
struct sg_table *table,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
|
||||
a->mapped = false;
|
||||
dma_unmap_sgtable(attachment->dev, table, direction, 0);
|
||||
}
|
||||
|
||||
static int cma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a;
|
||||
|
||||
if (buffer->vmap_cnt)
|
||||
invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_cpu(a->dev, &a->table, direction);
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a;
|
||||
|
||||
if (buffer->vmap_cnt)
|
||||
flush_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_device(a->dev, &a->table, direction);
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static vm_fault_t cma_heap_vm_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct cma_heap_buffer *buffer = vma->vm_private_data;
|
||||
|
||||
if (vmf->pgoff > buffer->pagecount)
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
vmf->page = buffer->pages[vmf->pgoff];
|
||||
get_page(vmf->page);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct dma_heap_vm_ops = {
|
||||
.fault = cma_heap_vm_fault,
|
||||
};
|
||||
|
||||
static int cma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
|
||||
if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
vma->vm_ops = &dma_heap_vm_ops;
|
||||
vma->vm_private_data = buffer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *cma_heap_do_vmap(struct cma_heap_buffer *buffer)
|
||||
{
|
||||
void *vaddr;
|
||||
|
||||
vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL);
|
||||
if (!vaddr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static int cma_heap_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
void *vaddr;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
if (buffer->vmap_cnt) {
|
||||
buffer->vmap_cnt++;
|
||||
dma_buf_map_set_vaddr(map, buffer->vaddr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vaddr = cma_heap_do_vmap(buffer);
|
||||
if (IS_ERR(vaddr)) {
|
||||
ret = PTR_ERR(vaddr);
|
||||
goto out;
|
||||
}
|
||||
buffer->vaddr = vaddr;
|
||||
buffer->vmap_cnt++;
|
||||
dma_buf_map_set_vaddr(map, buffer->vaddr);
|
||||
out:
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cma_heap_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
if (!--buffer->vmap_cnt) {
|
||||
vunmap(buffer->vaddr);
|
||||
buffer->vaddr = NULL;
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
dma_buf_map_clear(map);
|
||||
}
|
||||
|
||||
static void cma_heap_dma_buf_release(struct dma_buf *dmabuf)
|
||||
{
|
||||
struct cma_heap_buffer *buffer = dmabuf->priv;
|
||||
struct cma_heap *cma_heap = buffer->heap;
|
||||
|
||||
if (buffer->vmap_cnt > 0) {
|
||||
WARN(1, "%s: buffer still mapped in the kernel\n", __func__);
|
||||
vunmap(buffer->vaddr);
|
||||
buffer->vaddr = NULL;
|
||||
}
|
||||
|
||||
cma_release(cma_heap->cma, buffer->cma_pages, buffer->pagecount);
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
/* dmabuf heap CMA operations functions */
|
||||
static const struct dma_buf_ops cma_heap_buf_ops = {
|
||||
.attach = cma_heap_attach,
|
||||
.detach = cma_heap_detach,
|
||||
.map_dma_buf = cma_heap_map_dma_buf,
|
||||
.unmap_dma_buf = cma_heap_unmap_dma_buf,
|
||||
.begin_cpu_access = cma_heap_dma_buf_begin_cpu_access,
|
||||
.end_cpu_access = cma_heap_dma_buf_end_cpu_access,
|
||||
.mmap = cma_heap_mmap,
|
||||
.vmap = cma_heap_vmap,
|
||||
.vunmap = cma_heap_vunmap,
|
||||
.release = cma_heap_dma_buf_release,
|
||||
};
|
||||
|
||||
static int cma_heap_allocate(struct dma_heap *heap,
|
||||
unsigned long len,
|
||||
unsigned long fd_flags,
|
||||
unsigned long heap_flags)
|
||||
{
|
||||
struct cma_heap *cma_heap = dma_heap_get_drvdata(heap);
|
||||
struct heap_helper_buffer *helper_buffer;
|
||||
struct page *cma_pages;
|
||||
struct cma_heap_buffer *buffer;
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
size_t size = PAGE_ALIGN(len);
|
||||
unsigned long nr_pages = size >> PAGE_SHIFT;
|
||||
pgoff_t pagecount = size >> PAGE_SHIFT;
|
||||
unsigned long align = get_order(size);
|
||||
struct page *cma_pages;
|
||||
struct dma_buf *dmabuf;
|
||||
int ret = -ENOMEM;
|
||||
pgoff_t pg;
|
||||
|
||||
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&buffer->attachments);
|
||||
mutex_init(&buffer->lock);
|
||||
buffer->len = size;
|
||||
|
||||
if (align > CONFIG_CMA_ALIGNMENT)
|
||||
align = CONFIG_CMA_ALIGNMENT;
|
||||
|
||||
helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL);
|
||||
if (!helper_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
init_heap_helper_buffer(helper_buffer, cma_heap_free);
|
||||
helper_buffer->heap = heap;
|
||||
helper_buffer->size = len;
|
||||
|
||||
cma_pages = cma_alloc(cma_heap->cma, nr_pages, align, false);
|
||||
cma_pages = cma_alloc(cma_heap->cma, pagecount, align, false);
|
||||
if (!cma_pages)
|
||||
goto free_buf;
|
||||
goto free_buffer;
|
||||
|
||||
/* Clear the cma pages */
|
||||
if (PageHighMem(cma_pages)) {
|
||||
unsigned long nr_clear_pages = nr_pages;
|
||||
unsigned long nr_clear_pages = pagecount;
|
||||
struct page *page = cma_pages;
|
||||
|
||||
while (nr_clear_pages > 0) {
|
||||
|
@ -85,7 +314,6 @@ static int cma_heap_allocate(struct dma_heap *heap,
|
|||
*/
|
||||
if (fatal_signal_pending(current))
|
||||
goto free_cma;
|
||||
|
||||
page++;
|
||||
nr_clear_pages--;
|
||||
}
|
||||
|
@ -93,28 +321,30 @@ static int cma_heap_allocate(struct dma_heap *heap,
|
|||
memset(page_address(cma_pages), 0, size);
|
||||
}
|
||||
|
||||
helper_buffer->pagecount = nr_pages;
|
||||
helper_buffer->pages = kmalloc_array(helper_buffer->pagecount,
|
||||
sizeof(*helper_buffer->pages),
|
||||
GFP_KERNEL);
|
||||
if (!helper_buffer->pages) {
|
||||
buffer->pages = kmalloc_array(pagecount, sizeof(*buffer->pages), GFP_KERNEL);
|
||||
if (!buffer->pages) {
|
||||
ret = -ENOMEM;
|
||||
goto free_cma;
|
||||
}
|
||||
|
||||
for (pg = 0; pg < helper_buffer->pagecount; pg++)
|
||||
helper_buffer->pages[pg] = &cma_pages[pg];
|
||||
for (pg = 0; pg < pagecount; pg++)
|
||||
buffer->pages[pg] = &cma_pages[pg];
|
||||
|
||||
buffer->cma_pages = cma_pages;
|
||||
buffer->heap = cma_heap;
|
||||
buffer->pagecount = pagecount;
|
||||
|
||||
/* create the dmabuf */
|
||||
dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags);
|
||||
exp_info.ops = &cma_heap_buf_ops;
|
||||
exp_info.size = buffer->len;
|
||||
exp_info.flags = fd_flags;
|
||||
exp_info.priv = buffer;
|
||||
dmabuf = dma_buf_export(&exp_info);
|
||||
if (IS_ERR(dmabuf)) {
|
||||
ret = PTR_ERR(dmabuf);
|
||||
goto free_pages;
|
||||
}
|
||||
|
||||
helper_buffer->dmabuf = dmabuf;
|
||||
helper_buffer->priv_virt = cma_pages;
|
||||
|
||||
ret = dma_buf_fd(dmabuf, fd_flags);
|
||||
if (ret < 0) {
|
||||
dma_buf_put(dmabuf);
|
||||
|
@ -125,11 +355,12 @@ static int cma_heap_allocate(struct dma_heap *heap,
|
|||
return ret;
|
||||
|
||||
free_pages:
|
||||
kfree(helper_buffer->pages);
|
||||
kfree(buffer->pages);
|
||||
free_cma:
|
||||
cma_release(cma_heap->cma, cma_pages, nr_pages);
|
||||
free_buf:
|
||||
kfree(helper_buffer);
|
||||
cma_release(cma_heap->cma, cma_pages, pagecount);
|
||||
free_buffer:
|
||||
kfree(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,274 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <uapi/linux/dma-heap.h>
|
||||
|
||||
#include "heap-helpers.h"
|
||||
|
||||
void init_heap_helper_buffer(struct heap_helper_buffer *buffer,
|
||||
void (*free)(struct heap_helper_buffer *))
|
||||
{
|
||||
buffer->priv_virt = NULL;
|
||||
mutex_init(&buffer->lock);
|
||||
buffer->vmap_cnt = 0;
|
||||
buffer->vaddr = NULL;
|
||||
buffer->pagecount = 0;
|
||||
buffer->pages = NULL;
|
||||
INIT_LIST_HEAD(&buffer->attachments);
|
||||
buffer->free = free;
|
||||
}
|
||||
|
||||
struct dma_buf *heap_helper_export_dmabuf(struct heap_helper_buffer *buffer,
|
||||
int fd_flags)
|
||||
{
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
|
||||
exp_info.ops = &heap_helper_ops;
|
||||
exp_info.size = buffer->size;
|
||||
exp_info.flags = fd_flags;
|
||||
exp_info.priv = buffer;
|
||||
|
||||
return dma_buf_export(&exp_info);
|
||||
}
|
||||
|
||||
static void *dma_heap_map_kernel(struct heap_helper_buffer *buffer)
|
||||
{
|
||||
void *vaddr;
|
||||
|
||||
vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL);
|
||||
if (!vaddr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static void dma_heap_buffer_destroy(struct heap_helper_buffer *buffer)
|
||||
{
|
||||
if (buffer->vmap_cnt > 0) {
|
||||
WARN(1, "%s: buffer still mapped in the kernel\n", __func__);
|
||||
vunmap(buffer->vaddr);
|
||||
}
|
||||
|
||||
buffer->free(buffer);
|
||||
}
|
||||
|
||||
static void *dma_heap_buffer_vmap_get(struct heap_helper_buffer *buffer)
|
||||
{
|
||||
void *vaddr;
|
||||
|
||||
if (buffer->vmap_cnt) {
|
||||
buffer->vmap_cnt++;
|
||||
return buffer->vaddr;
|
||||
}
|
||||
vaddr = dma_heap_map_kernel(buffer);
|
||||
if (IS_ERR(vaddr))
|
||||
return vaddr;
|
||||
buffer->vaddr = vaddr;
|
||||
buffer->vmap_cnt++;
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static void dma_heap_buffer_vmap_put(struct heap_helper_buffer *buffer)
|
||||
{
|
||||
if (!--buffer->vmap_cnt) {
|
||||
vunmap(buffer->vaddr);
|
||||
buffer->vaddr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct dma_heaps_attachment {
|
||||
struct device *dev;
|
||||
struct sg_table table;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static int dma_heap_attach(struct dma_buf *dmabuf,
|
||||
struct dma_buf_attachment *attachment)
|
||||
{
|
||||
struct dma_heaps_attachment *a;
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
int ret;
|
||||
|
||||
a = kzalloc(sizeof(*a), GFP_KERNEL);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = sg_alloc_table_from_pages(&a->table, buffer->pages,
|
||||
buffer->pagecount, 0,
|
||||
buffer->pagecount << PAGE_SHIFT,
|
||||
GFP_KERNEL);
|
||||
if (ret) {
|
||||
kfree(a);
|
||||
return ret;
|
||||
}
|
||||
|
||||
a->dev = attachment->dev;
|
||||
INIT_LIST_HEAD(&a->list);
|
||||
|
||||
attachment->priv = a;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_add(&a->list, &buffer->attachments);
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma_heap_detach(struct dma_buf *dmabuf,
|
||||
struct dma_buf_attachment *attachment)
|
||||
{
|
||||
struct dma_heaps_attachment *a = attachment->priv;
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_del(&a->list);
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
sg_free_table(&a->table);
|
||||
kfree(a);
|
||||
}
|
||||
|
||||
static
|
||||
struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_heaps_attachment *a = attachment->priv;
|
||||
struct sg_table *table = &a->table;
|
||||
int ret;
|
||||
|
||||
ret = dma_map_sgtable(attachment->dev, table, direction, 0);
|
||||
if (ret)
|
||||
table = ERR_PTR(ret);
|
||||
return table;
|
||||
}
|
||||
|
||||
static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
|
||||
struct sg_table *table,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
dma_unmap_sgtable(attachment->dev, table, direction, 0);
|
||||
}
|
||||
|
||||
static vm_fault_t dma_heap_vm_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct heap_helper_buffer *buffer = vma->vm_private_data;
|
||||
|
||||
if (vmf->pgoff > buffer->pagecount)
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
vmf->page = buffer->pages[vmf->pgoff];
|
||||
get_page(vmf->page);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct dma_heap_vm_ops = {
|
||||
.fault = dma_heap_vm_fault,
|
||||
};
|
||||
|
||||
static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
{
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
|
||||
if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
vma->vm_ops = &dma_heap_vm_ops;
|
||||
vma->vm_private_data = buffer;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma_heap_dma_buf_release(struct dma_buf *dmabuf)
|
||||
{
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
|
||||
dma_heap_buffer_destroy(buffer);
|
||||
}
|
||||
|
||||
static int dma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heaps_attachment *a;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
|
||||
if (buffer->vmap_cnt)
|
||||
invalidate_kernel_vmap_range(buffer->vaddr, buffer->size);
|
||||
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
dma_sync_sg_for_cpu(a->dev, a->table.sgl, a->table.nents,
|
||||
direction);
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heaps_attachment *a;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
|
||||
if (buffer->vmap_cnt)
|
||||
flush_kernel_vmap_range(buffer->vaddr, buffer->size);
|
||||
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
dma_sync_sg_for_device(a->dev, a->table.sgl, a->table.nents,
|
||||
direction);
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dma_heap_dma_buf_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
|
||||
{
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
void *vaddr;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
vaddr = dma_heap_buffer_vmap_get(buffer);
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
if (!vaddr)
|
||||
return -ENOMEM;
|
||||
dma_buf_map_set_vaddr(map, vaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma_heap_dma_buf_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
|
||||
{
|
||||
struct heap_helper_buffer *buffer = dmabuf->priv;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
dma_heap_buffer_vmap_put(buffer);
|
||||
mutex_unlock(&buffer->lock);
|
||||
}
|
||||
|
||||
const struct dma_buf_ops heap_helper_ops = {
|
||||
.map_dma_buf = dma_heap_map_dma_buf,
|
||||
.unmap_dma_buf = dma_heap_unmap_dma_buf,
|
||||
.mmap = dma_heap_mmap,
|
||||
.release = dma_heap_dma_buf_release,
|
||||
.attach = dma_heap_attach,
|
||||
.detach = dma_heap_detach,
|
||||
.begin_cpu_access = dma_heap_dma_buf_begin_cpu_access,
|
||||
.end_cpu_access = dma_heap_dma_buf_end_cpu_access,
|
||||
.vmap = dma_heap_dma_buf_vmap,
|
||||
.vunmap = dma_heap_dma_buf_vunmap,
|
||||
};
|
|
@ -1,53 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* DMABUF Heaps helper code
|
||||
*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
* Copyright (C) 2019 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _HEAP_HELPERS_H
|
||||
#define _HEAP_HELPERS_H
|
||||
|
||||
#include <linux/dma-heap.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/**
|
||||
* struct heap_helper_buffer - helper buffer metadata
|
||||
* @heap: back pointer to the heap the buffer came from
|
||||
* @dmabuf: backing dma-buf for this buffer
|
||||
* @size: size of the buffer
|
||||
* @priv_virt pointer to heap specific private value
|
||||
* @lock mutext to protect the data in this structure
|
||||
* @vmap_cnt count of vmap references on the buffer
|
||||
* @vaddr vmap'ed virtual address
|
||||
* @pagecount number of pages in the buffer
|
||||
* @pages list of page pointers
|
||||
* @attachments list of device attachments
|
||||
*
|
||||
* @free heap callback to free the buffer
|
||||
*/
|
||||
struct heap_helper_buffer {
|
||||
struct dma_heap *heap;
|
||||
struct dma_buf *dmabuf;
|
||||
size_t size;
|
||||
|
||||
void *priv_virt;
|
||||
struct mutex lock;
|
||||
int vmap_cnt;
|
||||
void *vaddr;
|
||||
pgoff_t pagecount;
|
||||
struct page **pages;
|
||||
struct list_head attachments;
|
||||
|
||||
void (*free)(struct heap_helper_buffer *buffer);
|
||||
};
|
||||
|
||||
void init_heap_helper_buffer(struct heap_helper_buffer *buffer,
|
||||
void (*free)(struct heap_helper_buffer *));
|
||||
|
||||
struct dma_buf *heap_helper_export_dmabuf(struct heap_helper_buffer *buffer,
|
||||
int fd_flags);
|
||||
|
||||
extern const struct dma_buf_ops heap_helper_ops;
|
||||
#endif /* _HEAP_HELPERS_H */
|
|
@ -3,7 +3,11 @@
|
|||
* DMABUF System heap exporter
|
||||
*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
* Copyright (C) 2019 Linaro Ltd.
|
||||
* Copyright (C) 2019, 2020 Linaro Ltd.
|
||||
*
|
||||
* Portions based off of Andrew Davis' SRAM heap:
|
||||
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Andrew F. Davis <afd@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
|
@ -15,87 +19,404 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <asm/page.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "heap-helpers.h"
|
||||
static struct dma_heap *sys_heap;
|
||||
|
||||
struct dma_heap *sys_heap;
|
||||
struct system_heap_buffer {
|
||||
struct dma_heap *heap;
|
||||
struct list_head attachments;
|
||||
struct mutex lock;
|
||||
unsigned long len;
|
||||
struct sg_table sg_table;
|
||||
int vmap_cnt;
|
||||
void *vaddr;
|
||||
};
|
||||
|
||||
static void system_heap_free(struct heap_helper_buffer *buffer)
|
||||
struct dma_heap_attachment {
|
||||
struct device *dev;
|
||||
struct sg_table *table;
|
||||
struct list_head list;
|
||||
bool mapped;
|
||||
};
|
||||
|
||||
#define HIGH_ORDER_GFP (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \
|
||||
| __GFP_NORETRY) & ~__GFP_RECLAIM) \
|
||||
| __GFP_COMP)
|
||||
#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO | __GFP_COMP)
|
||||
static gfp_t order_flags[] = {HIGH_ORDER_GFP, LOW_ORDER_GFP, LOW_ORDER_GFP};
|
||||
/*
|
||||
* The selection of the orders used for allocation (1MB, 64K, 4K) is designed
|
||||
* to match with the sizes often found in IOMMUs. Using order 4 pages instead
|
||||
* of order 0 pages can significantly improve the performance of many IOMMUs
|
||||
* by reducing TLB pressure and time spent updating page tables.
|
||||
*/
|
||||
static const unsigned int orders[] = {8, 4, 0};
|
||||
#define NUM_ORDERS ARRAY_SIZE(orders)
|
||||
|
||||
static struct sg_table *dup_sg_table(struct sg_table *table)
|
||||
{
|
||||
pgoff_t pg;
|
||||
struct sg_table *new_table;
|
||||
int ret, i;
|
||||
struct scatterlist *sg, *new_sg;
|
||||
|
||||
for (pg = 0; pg < buffer->pagecount; pg++)
|
||||
__free_page(buffer->pages[pg]);
|
||||
kfree(buffer->pages);
|
||||
new_table = kzalloc(sizeof(*new_table), GFP_KERNEL);
|
||||
if (!new_table)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = sg_alloc_table(new_table, table->orig_nents, GFP_KERNEL);
|
||||
if (ret) {
|
||||
kfree(new_table);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
new_sg = new_table->sgl;
|
||||
for_each_sgtable_sg(table, sg, i) {
|
||||
sg_set_page(new_sg, sg_page(sg), sg->length, sg->offset);
|
||||
new_sg = sg_next(new_sg);
|
||||
}
|
||||
|
||||
return new_table;
|
||||
}
|
||||
|
||||
static int system_heap_attach(struct dma_buf *dmabuf,
|
||||
struct dma_buf_attachment *attachment)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a;
|
||||
struct sg_table *table;
|
||||
|
||||
a = kzalloc(sizeof(*a), GFP_KERNEL);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
table = dup_sg_table(&buffer->sg_table);
|
||||
if (IS_ERR(table)) {
|
||||
kfree(a);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
a->table = table;
|
||||
a->dev = attachment->dev;
|
||||
INIT_LIST_HEAD(&a->list);
|
||||
a->mapped = false;
|
||||
|
||||
attachment->priv = a;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_add(&a->list, &buffer->attachments);
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void system_heap_detach(struct dma_buf *dmabuf,
|
||||
struct dma_buf_attachment *attachment)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
list_del(&a->list);
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
sg_free_table(a->table);
|
||||
kfree(a->table);
|
||||
kfree(a);
|
||||
}
|
||||
|
||||
static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
struct sg_table *table = a->table;
|
||||
int ret;
|
||||
|
||||
ret = dma_map_sgtable(attachment->dev, table, direction, 0);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
a->mapped = true;
|
||||
return table;
|
||||
}
|
||||
|
||||
static void system_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
|
||||
struct sg_table *table,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_heap_attachment *a = attachment->priv;
|
||||
|
||||
a->mapped = false;
|
||||
dma_unmap_sgtable(attachment->dev, table, direction, 0);
|
||||
}
|
||||
|
||||
static int system_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
|
||||
if (buffer->vmap_cnt)
|
||||
invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_cpu(a->dev, a->table, direction);
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int system_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
struct dma_heap_attachment *a;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
|
||||
if (buffer->vmap_cnt)
|
||||
flush_kernel_vmap_range(buffer->vaddr, buffer->len);
|
||||
|
||||
list_for_each_entry(a, &buffer->attachments, list) {
|
||||
if (!a->mapped)
|
||||
continue;
|
||||
dma_sync_sgtable_for_device(a->dev, a->table, direction);
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
struct sg_table *table = &buffer->sg_table;
|
||||
unsigned long addr = vma->vm_start;
|
||||
struct sg_page_iter piter;
|
||||
int ret;
|
||||
|
||||
for_each_sgtable_page(table, &piter, vma->vm_pgoff) {
|
||||
struct page *page = sg_page_iter_page(&piter);
|
||||
|
||||
ret = remap_pfn_range(vma, addr, page_to_pfn(page), PAGE_SIZE,
|
||||
vma->vm_page_prot);
|
||||
if (ret)
|
||||
return ret;
|
||||
addr += PAGE_SIZE;
|
||||
if (addr >= vma->vm_end)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
|
||||
{
|
||||
struct sg_table *table = &buffer->sg_table;
|
||||
int npages = PAGE_ALIGN(buffer->len) / PAGE_SIZE;
|
||||
struct page **pages = vmalloc(sizeof(struct page *) * npages);
|
||||
struct page **tmp = pages;
|
||||
struct sg_page_iter piter;
|
||||
void *vaddr;
|
||||
|
||||
if (!pages)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for_each_sgtable_page(table, &piter, 0) {
|
||||
WARN_ON(tmp - pages >= npages);
|
||||
*tmp++ = sg_page_iter_page(&piter);
|
||||
}
|
||||
|
||||
vaddr = vmap(pages, npages, VM_MAP, PAGE_KERNEL);
|
||||
vfree(pages);
|
||||
|
||||
if (!vaddr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static int system_heap_vmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
void *vaddr;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
if (buffer->vmap_cnt) {
|
||||
buffer->vmap_cnt++;
|
||||
dma_buf_map_set_vaddr(map, buffer->vaddr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
vaddr = system_heap_do_vmap(buffer);
|
||||
if (IS_ERR(vaddr)) {
|
||||
ret = PTR_ERR(vaddr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer->vaddr = vaddr;
|
||||
buffer->vmap_cnt++;
|
||||
dma_buf_map_set_vaddr(map, buffer->vaddr);
|
||||
out:
|
||||
mutex_unlock(&buffer->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void system_heap_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
|
||||
mutex_lock(&buffer->lock);
|
||||
if (!--buffer->vmap_cnt) {
|
||||
vunmap(buffer->vaddr);
|
||||
buffer->vaddr = NULL;
|
||||
}
|
||||
mutex_unlock(&buffer->lock);
|
||||
dma_buf_map_clear(map);
|
||||
}
|
||||
|
||||
static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
|
||||
{
|
||||
struct system_heap_buffer *buffer = dmabuf->priv;
|
||||
struct sg_table *table;
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
table = &buffer->sg_table;
|
||||
for_each_sg(table->sgl, sg, table->nents, i) {
|
||||
struct page *page = sg_page(sg);
|
||||
|
||||
__free_pages(page, compound_order(page));
|
||||
}
|
||||
sg_free_table(table);
|
||||
kfree(buffer);
|
||||
}
|
||||
|
||||
static const struct dma_buf_ops system_heap_buf_ops = {
|
||||
.attach = system_heap_attach,
|
||||
.detach = system_heap_detach,
|
||||
.map_dma_buf = system_heap_map_dma_buf,
|
||||
.unmap_dma_buf = system_heap_unmap_dma_buf,
|
||||
.begin_cpu_access = system_heap_dma_buf_begin_cpu_access,
|
||||
.end_cpu_access = system_heap_dma_buf_end_cpu_access,
|
||||
.mmap = system_heap_mmap,
|
||||
.vmap = system_heap_vmap,
|
||||
.vunmap = system_heap_vunmap,
|
||||
.release = system_heap_dma_buf_release,
|
||||
};
|
||||
|
||||
static struct page *alloc_largest_available(unsigned long size,
|
||||
unsigned int max_order)
|
||||
{
|
||||
struct page *page;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_ORDERS; i++) {
|
||||
if (size < (PAGE_SIZE << orders[i]))
|
||||
continue;
|
||||
if (max_order < orders[i])
|
||||
continue;
|
||||
|
||||
page = alloc_pages(order_flags[i], orders[i]);
|
||||
if (!page)
|
||||
continue;
|
||||
return page;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int system_heap_allocate(struct dma_heap *heap,
|
||||
unsigned long len,
|
||||
unsigned long fd_flags,
|
||||
unsigned long heap_flags)
|
||||
{
|
||||
struct heap_helper_buffer *helper_buffer;
|
||||
struct system_heap_buffer *buffer;
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
unsigned long size_remaining = len;
|
||||
unsigned int max_order = orders[0];
|
||||
struct dma_buf *dmabuf;
|
||||
int ret = -ENOMEM;
|
||||
pgoff_t pg;
|
||||
struct sg_table *table;
|
||||
struct scatterlist *sg;
|
||||
struct list_head pages;
|
||||
struct page *page, *tmp_page;
|
||||
int i, ret = -ENOMEM;
|
||||
|
||||
helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL);
|
||||
if (!helper_buffer)
|
||||
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
init_heap_helper_buffer(helper_buffer, system_heap_free);
|
||||
helper_buffer->heap = heap;
|
||||
helper_buffer->size = len;
|
||||
INIT_LIST_HEAD(&buffer->attachments);
|
||||
mutex_init(&buffer->lock);
|
||||
buffer->heap = heap;
|
||||
buffer->len = len;
|
||||
|
||||
helper_buffer->pagecount = len / PAGE_SIZE;
|
||||
helper_buffer->pages = kmalloc_array(helper_buffer->pagecount,
|
||||
sizeof(*helper_buffer->pages),
|
||||
GFP_KERNEL);
|
||||
if (!helper_buffer->pages) {
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
for (pg = 0; pg < helper_buffer->pagecount; pg++) {
|
||||
INIT_LIST_HEAD(&pages);
|
||||
i = 0;
|
||||
while (size_remaining > 0) {
|
||||
/*
|
||||
* Avoid trying to allocate memory if the process
|
||||
* has been killed by by SIGKILL
|
||||
* has been killed by SIGKILL
|
||||
*/
|
||||
if (fatal_signal_pending(current))
|
||||
goto err1;
|
||||
goto free_buffer;
|
||||
|
||||
helper_buffer->pages[pg] = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
||||
if (!helper_buffer->pages[pg])
|
||||
goto err1;
|
||||
page = alloc_largest_available(size_remaining, max_order);
|
||||
if (!page)
|
||||
goto free_buffer;
|
||||
|
||||
list_add_tail(&page->lru, &pages);
|
||||
size_remaining -= page_size(page);
|
||||
max_order = compound_order(page);
|
||||
i++;
|
||||
}
|
||||
|
||||
table = &buffer->sg_table;
|
||||
if (sg_alloc_table(table, i, GFP_KERNEL))
|
||||
goto free_buffer;
|
||||
|
||||
sg = table->sgl;
|
||||
list_for_each_entry_safe(page, tmp_page, &pages, lru) {
|
||||
sg_set_page(sg, page, page_size(page), 0);
|
||||
sg = sg_next(sg);
|
||||
list_del(&page->lru);
|
||||
}
|
||||
|
||||
/* create the dmabuf */
|
||||
dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags);
|
||||
exp_info.ops = &system_heap_buf_ops;
|
||||
exp_info.size = buffer->len;
|
||||
exp_info.flags = fd_flags;
|
||||
exp_info.priv = buffer;
|
||||
dmabuf = dma_buf_export(&exp_info);
|
||||
if (IS_ERR(dmabuf)) {
|
||||
ret = PTR_ERR(dmabuf);
|
||||
goto err1;
|
||||
goto free_pages;
|
||||
}
|
||||
|
||||
helper_buffer->dmabuf = dmabuf;
|
||||
|
||||
ret = dma_buf_fd(dmabuf, fd_flags);
|
||||
if (ret < 0) {
|
||||
dma_buf_put(dmabuf);
|
||||
/* just return, as put will call release and that will free */
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err1:
|
||||
while (pg > 0)
|
||||
__free_page(helper_buffer->pages[--pg]);
|
||||
kfree(helper_buffer->pages);
|
||||
err0:
|
||||
kfree(helper_buffer);
|
||||
free_pages:
|
||||
for_each_sgtable_sg(table, sg, i) {
|
||||
struct page *p = sg_page(sg);
|
||||
|
||||
__free_pages(p, compound_order(p));
|
||||
}
|
||||
sg_free_table(table);
|
||||
free_buffer:
|
||||
list_for_each_entry_safe(page, tmp_page, &pages, lru)
|
||||
__free_pages(page, compound_order(page));
|
||||
kfree(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -107,7 +428,6 @@ static const struct dma_heap_ops system_heap_ops = {
|
|||
static int system_heap_create(void)
|
||||
{
|
||||
struct dma_heap_export_info exp_info;
|
||||
int ret = 0;
|
||||
|
||||
exp_info.name = "system";
|
||||
exp_info.ops = &system_heap_ops;
|
||||
|
@ -115,9 +435,9 @@ static int system_heap_create(void)
|
|||
|
||||
sys_heap = dma_heap_add(&exp_info);
|
||||
if (IS_ERR(sys_heap))
|
||||
ret = PTR_ERR(sys_heap);
|
||||
return PTR_ERR(sys_heap);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
module_init(system_heap_create);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1280,6 +1280,8 @@ int amdgpu_enable_vblank_kms(struct drm_crtc *crtc);
|
|||
void amdgpu_disable_vblank_kms(struct drm_crtc *crtc);
|
||||
long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
int amdgpu_info_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *filp);
|
||||
|
||||
/*
|
||||
* functions used by amdgpu_encoder.c
|
||||
|
|
|
@ -1533,8 +1533,6 @@ int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp);
|
||||
|
||||
const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
|
||||
DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
|
||||
|
|
|
@ -551,25 +551,12 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
|
|||
struct ttm_resource *old_mem = &bo->mem;
|
||||
int r;
|
||||
|
||||
if ((old_mem->mem_type == TTM_PL_SYSTEM &&
|
||||
new_mem->mem_type == TTM_PL_VRAM) ||
|
||||
(old_mem->mem_type == TTM_PL_VRAM &&
|
||||
new_mem->mem_type == TTM_PL_SYSTEM)) {
|
||||
hop->fpfn = 0;
|
||||
hop->lpfn = 0;
|
||||
hop->mem_type = TTM_PL_TT;
|
||||
hop->flags = 0;
|
||||
return -EMULTIHOP;
|
||||
}
|
||||
|
||||
if (new_mem->mem_type == TTM_PL_TT) {
|
||||
r = amdgpu_ttm_backend_bind(bo->bdev, bo->ttm, new_mem);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
amdgpu_bo_move_notify(bo, evict, new_mem);
|
||||
|
||||
/* Can't move a pinned BO */
|
||||
abo = ttm_to_amdgpu_bo(bo);
|
||||
if (WARN_ON_ONCE(abo->tbo.pin_count > 0))
|
||||
|
@ -579,24 +566,23 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
|
|||
|
||||
if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) {
|
||||
ttm_bo_move_null(bo, new_mem);
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
if (old_mem->mem_type == TTM_PL_SYSTEM &&
|
||||
new_mem->mem_type == TTM_PL_TT) {
|
||||
ttm_bo_move_null(bo, new_mem);
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (old_mem->mem_type == TTM_PL_TT &&
|
||||
new_mem->mem_type == TTM_PL_SYSTEM) {
|
||||
r = ttm_bo_wait_ctx(bo, ctx);
|
||||
if (r)
|
||||
goto fail;
|
||||
return r;
|
||||
|
||||
amdgpu_ttm_backend_unbind(bo->bdev, bo->ttm);
|
||||
ttm_resource_free(bo, &bo->mem);
|
||||
ttm_bo_assign_mem(bo, new_mem);
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (old_mem->mem_type == AMDGPU_PL_GDS ||
|
||||
|
@ -607,27 +593,37 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict,
|
|||
new_mem->mem_type == AMDGPU_PL_OA) {
|
||||
/* Nothing to save here */
|
||||
ttm_bo_move_null(bo, new_mem);
|
||||
return 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!adev->mman.buffer_funcs_enabled) {
|
||||
r = -ENODEV;
|
||||
goto memcpy;
|
||||
if (adev->mman.buffer_funcs_enabled) {
|
||||
if (((old_mem->mem_type == TTM_PL_SYSTEM &&
|
||||
new_mem->mem_type == TTM_PL_VRAM) ||
|
||||
(old_mem->mem_type == TTM_PL_VRAM &&
|
||||
new_mem->mem_type == TTM_PL_SYSTEM))) {
|
||||
hop->fpfn = 0;
|
||||
hop->lpfn = 0;
|
||||
hop->mem_type = TTM_PL_TT;
|
||||
hop->flags = 0;
|
||||
return -EMULTIHOP;
|
||||
}
|
||||
|
||||
r = amdgpu_move_blit(bo, evict, new_mem, old_mem);
|
||||
} else {
|
||||
r = -ENODEV;
|
||||
}
|
||||
|
||||
if (r) {
|
||||
memcpy:
|
||||
/* Check that all memory is CPU accessible */
|
||||
if (!amdgpu_mem_visible(adev, old_mem) ||
|
||||
!amdgpu_mem_visible(adev, new_mem)) {
|
||||
pr_err("Move buffer fallback to memcpy unavailable\n");
|
||||
goto fail;
|
||||
return r;
|
||||
}
|
||||
|
||||
r = ttm_bo_move_memcpy(bo, ctx, new_mem);
|
||||
if (r)
|
||||
goto fail;
|
||||
return r;
|
||||
}
|
||||
|
||||
if (bo->type == ttm_bo_type_device &&
|
||||
|
@ -639,14 +635,11 @@ memcpy:
|
|||
abo->flags &= ~AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
|
||||
}
|
||||
|
||||
out:
|
||||
/* update statistics */
|
||||
atomic64_add((u64)bo->num_pages << PAGE_SHIFT, &adev->num_bytes_moved);
|
||||
amdgpu_bo_move_notify(bo, evict, new_mem);
|
||||
return 0;
|
||||
fail:
|
||||
swap(*new_mem, bo->mem);
|
||||
amdgpu_bo_move_notify(bo, false, new_mem);
|
||||
swap(*new_mem, bo->mem);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_dp_mst_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
@ -252,8 +253,10 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
|
|||
|
||||
static struct drm_encoder *
|
||||
dm_mst_atomic_best_encoder(struct drm_connector *connector,
|
||||
struct drm_connector_state *connector_state)
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
|
||||
connector);
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct amdgpu_device *adev = drm_to_adev(dev);
|
||||
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(connector_state->crtc);
|
||||
|
|
|
@ -122,7 +122,8 @@ static int handle_conflicting_encoders(struct drm_atomic_state *state,
|
|||
continue;
|
||||
|
||||
if (funcs->atomic_best_encoder)
|
||||
new_encoder = funcs->atomic_best_encoder(connector, new_conn_state);
|
||||
new_encoder = funcs->atomic_best_encoder(connector,
|
||||
state);
|
||||
else if (funcs->best_encoder)
|
||||
new_encoder = funcs->best_encoder(connector);
|
||||
else
|
||||
|
@ -345,8 +346,7 @@ update_connector_routing(struct drm_atomic_state *state,
|
|||
funcs = connector->helper_private;
|
||||
|
||||
if (funcs->atomic_best_encoder)
|
||||
new_encoder = funcs->atomic_best_encoder(connector,
|
||||
new_connector_state);
|
||||
new_encoder = funcs->atomic_best_encoder(connector, state);
|
||||
else if (funcs->best_encoder)
|
||||
new_encoder = funcs->best_encoder(connector);
|
||||
else
|
||||
|
@ -1313,7 +1313,7 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev,
|
|||
|
||||
if (new_conn_state->writeback_job && new_conn_state->writeback_job->fb) {
|
||||
WARN_ON(connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK);
|
||||
funcs->atomic_commit(connector, new_conn_state);
|
||||
funcs->atomic_commit(connector, old_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,10 +196,10 @@
|
|||
* exposed and assumed to be black).
|
||||
*
|
||||
* SCALING_FILTER:
|
||||
*
|
||||
* Indicates scaling filter to be used for plane scaler
|
||||
*
|
||||
* The value of this property can be one of the following:
|
||||
*
|
||||
* Default:
|
||||
* Driver's default scaling filter
|
||||
* Nearest Neighbor:
|
||||
|
|
|
@ -77,6 +77,7 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
|
|||
if ((entry->map->offset & 0xffffffff) ==
|
||||
(map->offset & 0xffffffff))
|
||||
return entry;
|
||||
break;
|
||||
default: /* Make gcc happy */
|
||||
;
|
||||
}
|
||||
|
|
|
@ -314,9 +314,6 @@ drm_client_buffer_vmap(struct drm_client_buffer *buffer, struct dma_buf_map *map
|
|||
struct dma_buf_map *map = &buffer->map;
|
||||
int ret;
|
||||
|
||||
if (dma_buf_map_is_set(map))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* FIXME: The dependency on GEM here isn't required, we could
|
||||
* convert the driver handle to a dma-buf instead and use the
|
||||
|
@ -329,7 +326,6 @@ drm_client_buffer_vmap(struct drm_client_buffer *buffer, struct dma_buf_map *map
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
out:
|
||||
*map_copy = *map;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -233,11 +233,11 @@ struct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc)
|
|||
* Atomic property for setting the scaling filter for CRTC scaler
|
||||
*
|
||||
* The value of this property can be one of the following:
|
||||
*
|
||||
* Default:
|
||||
* Driver's default scaling filter
|
||||
* Nearest Neighbor:
|
||||
* Nearest Neighbor scaling filter
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -371,7 +371,7 @@ static void drm_fb_helper_resume_worker(struct work_struct *work)
|
|||
console_unlock();
|
||||
}
|
||||
|
||||
static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper,
|
||||
static void drm_fb_helper_damage_blit_real(struct drm_fb_helper *fb_helper,
|
||||
struct drm_clip_rect *clip,
|
||||
struct dma_buf_map *dst)
|
||||
{
|
||||
|
@ -391,40 +391,86 @@ static void drm_fb_helper_dirty_blit_real(struct drm_fb_helper *fb_helper,
|
|||
}
|
||||
}
|
||||
|
||||
static void drm_fb_helper_dirty_work(struct work_struct *work)
|
||||
static int drm_fb_helper_damage_blit(struct drm_fb_helper *fb_helper,
|
||||
struct drm_clip_rect *clip)
|
||||
{
|
||||
struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
|
||||
dirty_work);
|
||||
struct drm_clip_rect *clip = &helper->dirty_clip;
|
||||
struct drm_clip_rect clip_copy;
|
||||
unsigned long flags;
|
||||
struct dma_buf_map map;
|
||||
struct drm_client_buffer *buffer = fb_helper->buffer;
|
||||
struct dma_buf_map map, dst;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&helper->dirty_lock, flags);
|
||||
/*
|
||||
* We have to pin the client buffer to its current location while
|
||||
* flushing the shadow buffer. In the general case, concurrent
|
||||
* modesetting operations could try to move the buffer and would
|
||||
* fail. The modeset has to be serialized by acquiring the reservation
|
||||
* object of the underlying BO here.
|
||||
*
|
||||
* For fbdev emulation, we only have to protect against fbdev modeset
|
||||
* operations. Nothing else will involve the client buffer's BO. So it
|
||||
* is sufficient to acquire struct drm_fb_helper.lock here.
|
||||
*/
|
||||
mutex_lock(&fb_helper->lock);
|
||||
|
||||
ret = drm_client_buffer_vmap(buffer, &map);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
dst = map;
|
||||
drm_fb_helper_damage_blit_real(fb_helper, clip, &dst);
|
||||
|
||||
drm_client_buffer_vunmap(buffer);
|
||||
|
||||
out:
|
||||
mutex_unlock(&fb_helper->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void drm_fb_helper_damage_work(struct work_struct *work)
|
||||
{
|
||||
struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
|
||||
damage_work);
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct drm_clip_rect *clip = &helper->damage_clip;
|
||||
struct drm_clip_rect clip_copy;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&helper->damage_lock, flags);
|
||||
clip_copy = *clip;
|
||||
clip->x1 = clip->y1 = ~0;
|
||||
clip->x2 = clip->y2 = 0;
|
||||
spin_unlock_irqrestore(&helper->dirty_lock, flags);
|
||||
spin_unlock_irqrestore(&helper->damage_lock, flags);
|
||||
|
||||
/* call dirty callback only when it has been really touched */
|
||||
if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) {
|
||||
|
||||
/* Generic fbdev uses a shadow buffer */
|
||||
if (helper->buffer) {
|
||||
ret = drm_client_buffer_vmap(helper->buffer, &map);
|
||||
if (ret)
|
||||
/* Call damage handlers only if necessary */
|
||||
if (!(clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2))
|
||||
return;
|
||||
drm_fb_helper_dirty_blit_real(helper, &clip_copy, &map);
|
||||
|
||||
if (helper->buffer) {
|
||||
ret = drm_fb_helper_damage_blit(helper, &clip_copy);
|
||||
if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret))
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (helper->fb->funcs->dirty)
|
||||
helper->fb->funcs->dirty(helper->fb, NULL, 0, 0,
|
||||
&clip_copy, 1);
|
||||
|
||||
if (helper->buffer)
|
||||
drm_client_buffer_vunmap(helper->buffer);
|
||||
if (helper->fb->funcs->dirty) {
|
||||
ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
|
||||
if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
/*
|
||||
* Restore damage clip rectangle on errors. The next run
|
||||
* of the damage worker will perform the update.
|
||||
*/
|
||||
spin_lock_irqsave(&helper->damage_lock, flags);
|
||||
clip->x1 = min_t(u32, clip->x1, clip_copy.x1);
|
||||
clip->y1 = min_t(u32, clip->y1, clip_copy.y1);
|
||||
clip->x2 = max_t(u32, clip->x2, clip_copy.x2);
|
||||
clip->y2 = max_t(u32, clip->y2, clip_copy.y2);
|
||||
spin_unlock_irqrestore(&helper->damage_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -440,10 +486,10 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
|
|||
const struct drm_fb_helper_funcs *funcs)
|
||||
{
|
||||
INIT_LIST_HEAD(&helper->kernel_fb_list);
|
||||
spin_lock_init(&helper->dirty_lock);
|
||||
spin_lock_init(&helper->damage_lock);
|
||||
INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);
|
||||
INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
|
||||
helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
|
||||
INIT_WORK(&helper->damage_work, drm_fb_helper_damage_work);
|
||||
helper->damage_clip.x1 = helper->damage_clip.y1 = ~0;
|
||||
mutex_init(&helper->lock);
|
||||
helper->funcs = funcs;
|
||||
helper->dev = dev;
|
||||
|
@ -579,7 +625,7 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
|
|||
return;
|
||||
|
||||
cancel_work_sync(&fb_helper->resume_work);
|
||||
cancel_work_sync(&fb_helper->dirty_work);
|
||||
cancel_work_sync(&fb_helper->damage_work);
|
||||
|
||||
info = fb_helper->fbdev;
|
||||
if (info) {
|
||||
|
@ -614,30 +660,30 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper)
|
|||
fb->funcs->dirty;
|
||||
}
|
||||
|
||||
static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y,
|
||||
static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
|
||||
u32 width, u32 height)
|
||||
{
|
||||
struct drm_fb_helper *helper = info->par;
|
||||
struct drm_clip_rect *clip = &helper->dirty_clip;
|
||||
struct drm_clip_rect *clip = &helper->damage_clip;
|
||||
unsigned long flags;
|
||||
|
||||
if (!drm_fbdev_use_shadow_fb(helper))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&helper->dirty_lock, flags);
|
||||
spin_lock_irqsave(&helper->damage_lock, flags);
|
||||
clip->x1 = min_t(u32, clip->x1, x);
|
||||
clip->y1 = min_t(u32, clip->y1, y);
|
||||
clip->x2 = max_t(u32, clip->x2, x + width);
|
||||
clip->y2 = max_t(u32, clip->y2, y + height);
|
||||
spin_unlock_irqrestore(&helper->dirty_lock, flags);
|
||||
spin_unlock_irqrestore(&helper->damage_lock, flags);
|
||||
|
||||
schedule_work(&helper->dirty_work);
|
||||
schedule_work(&helper->damage_work);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_fb_helper_deferred_io() - fbdev deferred_io callback function
|
||||
* @info: fb_info struct pointer
|
||||
* @pagelist: list of dirty mmap framebuffer pages
|
||||
* @pagelist: list of mmap framebuffer pages that have to be flushed
|
||||
*
|
||||
* This function is used as the &fb_deferred_io.deferred_io
|
||||
* callback function for flushing the fbdev mmap writes.
|
||||
|
@ -662,7 +708,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
|
|||
y1 = min / info->fix.line_length;
|
||||
y2 = min_t(u32, DIV_ROUND_UP(max, info->fix.line_length),
|
||||
info->var.yres);
|
||||
drm_fb_helper_dirty(info, 0, y1, info->var.xres, y2 - y1);
|
||||
drm_fb_helper_damage(info, 0, y1, info->var.xres, y2 - y1);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_deferred_io);
|
||||
|
@ -699,8 +745,7 @@ ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
|
|||
|
||||
ret = fb_sys_write(info, buf, count, ppos);
|
||||
if (ret > 0)
|
||||
drm_fb_helper_dirty(info, 0, 0, info->var.xres,
|
||||
info->var.yres);
|
||||
drm_fb_helper_damage(info, 0, 0, info->var.xres, info->var.yres);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -717,8 +762,7 @@ void drm_fb_helper_sys_fillrect(struct fb_info *info,
|
|||
const struct fb_fillrect *rect)
|
||||
{
|
||||
sys_fillrect(info, rect);
|
||||
drm_fb_helper_dirty(info, rect->dx, rect->dy,
|
||||
rect->width, rect->height);
|
||||
drm_fb_helper_damage(info, rect->dx, rect->dy, rect->width, rect->height);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
|
||||
|
||||
|
@ -733,8 +777,7 @@ void drm_fb_helper_sys_copyarea(struct fb_info *info,
|
|||
const struct fb_copyarea *area)
|
||||
{
|
||||
sys_copyarea(info, area);
|
||||
drm_fb_helper_dirty(info, area->dx, area->dy,
|
||||
area->width, area->height);
|
||||
drm_fb_helper_damage(info, area->dx, area->dy, area->width, area->height);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
|
||||
|
||||
|
@ -749,8 +792,7 @@ void drm_fb_helper_sys_imageblit(struct fb_info *info,
|
|||
const struct fb_image *image)
|
||||
{
|
||||
sys_imageblit(info, image);
|
||||
drm_fb_helper_dirty(info, image->dx, image->dy,
|
||||
image->width, image->height);
|
||||
drm_fb_helper_damage(info, image->dx, image->dy, image->width, image->height);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
|
||||
|
||||
|
@ -765,8 +807,7 @@ void drm_fb_helper_cfb_fillrect(struct fb_info *info,
|
|||
const struct fb_fillrect *rect)
|
||||
{
|
||||
cfb_fillrect(info, rect);
|
||||
drm_fb_helper_dirty(info, rect->dx, rect->dy,
|
||||
rect->width, rect->height);
|
||||
drm_fb_helper_damage(info, rect->dx, rect->dy, rect->width, rect->height);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
|
||||
|
||||
|
@ -781,8 +822,7 @@ void drm_fb_helper_cfb_copyarea(struct fb_info *info,
|
|||
const struct fb_copyarea *area)
|
||||
{
|
||||
cfb_copyarea(info, area);
|
||||
drm_fb_helper_dirty(info, area->dx, area->dy,
|
||||
area->width, area->height);
|
||||
drm_fb_helper_damage(info, area->dx, area->dy, area->width, area->height);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
|
||||
|
||||
|
@ -797,8 +837,7 @@ void drm_fb_helper_cfb_imageblit(struct fb_info *info,
|
|||
const struct fb_image *image)
|
||||
{
|
||||
cfb_imageblit(info, image);
|
||||
drm_fb_helper_dirty(info, image->dx, image->dy,
|
||||
image->width, image->height);
|
||||
drm_fb_helper_damage(info, image->dx, image->dy, image->width, image->height);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
|
||||
|
||||
|
@ -1988,14 +2027,19 @@ static void drm_fbdev_cleanup(struct drm_fb_helper *fb_helper)
|
|||
if (!fb_helper->dev)
|
||||
return;
|
||||
|
||||
if (fbi && fbi->fbdefio) {
|
||||
if (fbi) {
|
||||
if (fbi->fbdefio)
|
||||
fb_deferred_io_cleanup(fbi);
|
||||
if (drm_fbdev_use_shadow_fb(fb_helper))
|
||||
shadow = fbi->screen_buffer;
|
||||
}
|
||||
|
||||
drm_fb_helper_fini(fb_helper);
|
||||
|
||||
if (shadow)
|
||||
vfree(shadow);
|
||||
else
|
||||
drm_client_buffer_vunmap(fb_helper->buffer);
|
||||
|
||||
drm_client_framebuffer_delete(fb_helper->buffer);
|
||||
}
|
||||
|
@ -2189,6 +2233,9 @@ static ssize_t drm_fbdev_fb_write(struct fb_info *info, const char __user *buf,
|
|||
if (ret > 0)
|
||||
*ppos += ret;
|
||||
|
||||
if (ret > 0)
|
||||
drm_fb_helper_damage(info, 0, 0, info->var.xres_virtual, info->var.yres_virtual);
|
||||
|
||||
return ret ? ret : err;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,13 +51,17 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
|
|||
if (!obj)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
if (!obj->funcs)
|
||||
obj->funcs = &drm_gem_shmem_funcs;
|
||||
|
||||
if (private)
|
||||
if (private) {
|
||||
drm_gem_private_object_init(dev, obj, size);
|
||||
else
|
||||
shmem->map_wc = false; /* dma-buf mappings use always writecombine */
|
||||
} else {
|
||||
ret = drm_gem_object_init(dev, obj, size);
|
||||
}
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
|
@ -65,7 +69,6 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
|
|||
if (ret)
|
||||
goto err_release;
|
||||
|
||||
shmem = to_drm_gem_shmem_obj(obj);
|
||||
mutex_init(&shmem->pages_lock);
|
||||
mutex_init(&shmem->vmap_lock);
|
||||
INIT_LIST_HEAD(&shmem->madv_list);
|
||||
|
@ -284,7 +287,7 @@ static int drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem, struct
|
|||
if (ret)
|
||||
goto err_zero_use;
|
||||
|
||||
if (!shmem->map_cached)
|
||||
if (shmem->map_wc)
|
||||
prot = pgprot_writecombine(prot);
|
||||
shmem->vaddr = vmap(shmem->pages, obj->size >> PAGE_SHIFT,
|
||||
VM_MAP, prot);
|
||||
|
@ -476,33 +479,6 @@ bool drm_gem_shmem_purge(struct drm_gem_object *obj)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_purge);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_create_object_cached - Create a shmem buffer object with
|
||||
* cached mappings
|
||||
* @dev: DRM device
|
||||
* @size: Size of the object to allocate
|
||||
*
|
||||
* By default, shmem buffer objects use writecombine mappings. This
|
||||
* function implements struct drm_driver.gem_create_object for shmem
|
||||
* buffer objects with cached mappings.
|
||||
*
|
||||
* Returns:
|
||||
* A struct drm_gem_shmem_object * on success or NULL negative on failure.
|
||||
*/
|
||||
struct drm_gem_object *
|
||||
drm_gem_shmem_create_object_cached(struct drm_device *dev, size_t size)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
|
||||
shmem = kzalloc(sizeof(*shmem), GFP_KERNEL);
|
||||
if (!shmem)
|
||||
return NULL;
|
||||
shmem->map_cached = true;
|
||||
|
||||
return &shmem->base;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_create_object_cached);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
|
||||
* @file: DRM file structure to create the dumb buffer for
|
||||
|
@ -626,7 +602,7 @@ int drm_gem_shmem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
|
|||
|
||||
vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND;
|
||||
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
||||
if (!shmem->map_cached)
|
||||
if (shmem->map_wc)
|
||||
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||||
vma->vm_ops = &drm_gem_shmem_vm_ops;
|
||||
|
||||
|
|
|
@ -145,10 +145,8 @@ static int etnaviv_gem_mmap_obj(struct etnaviv_gem_object *etnaviv_obj,
|
|||
* address_space (so unmap_mapping_range does what we want,
|
||||
* in particular in the case of mmap'd dmabufs)
|
||||
*/
|
||||
fput(vma->vm_file);
|
||||
get_file(etnaviv_obj->base.filp);
|
||||
vma->vm_pgoff = 0;
|
||||
vma->vm_file = etnaviv_obj->base.filp;
|
||||
vma_set_file(vma, etnaviv_obj->base.filp);
|
||||
|
||||
vma->vm_page_prot = vm_page_prot;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
@ -719,11 +720,13 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
|
|||
}
|
||||
|
||||
static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
|
||||
struct drm_connector_state *state)
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
|
||||
connector);
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct intel_dp *intel_dp = intel_connector->mst_port;
|
||||
struct intel_crtc *crtc = to_intel_crtc(state->crtc);
|
||||
struct intel_crtc *crtc = to_intel_crtc(connector_state->crtc);
|
||||
|
||||
return &intel_dp->mst_encoders[crtc->pipe]->base.base;
|
||||
}
|
||||
|
|
|
@ -114,8 +114,7 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
fput(vma->vm_file);
|
||||
vma->vm_file = get_file(obj->base.filp);
|
||||
vma_set_file(vma, obj->base.filp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -893,8 +893,9 @@ int i915_gem_mmap(struct file *filp, struct vm_area_struct *vma)
|
|||
* requires avoiding extraneous references to their filp, hence why
|
||||
* we prefer to use an anonymous file for their mmaps.
|
||||
*/
|
||||
fput(vma->vm_file);
|
||||
vma->vm_file = anon;
|
||||
vma_set_file(vma, anon);
|
||||
/* Drop the initial creation reference, the vma is now holding one. */
|
||||
fput(anon);
|
||||
|
||||
switch (mmo->mmap_type) {
|
||||
case I915_MMAP_TYPE_WC:
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define __DCSS_PRV_H__
|
||||
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_plane.h>
|
||||
#include <linux/io.h>
|
||||
#include <video/videomode.h>
|
||||
|
||||
|
@ -165,6 +166,8 @@ void dcss_ss_sync_set(struct dcss_ss *ss, struct videomode *vm,
|
|||
/* SCALER */
|
||||
int dcss_scaler_init(struct dcss_dev *dcss, unsigned long scaler_base);
|
||||
void dcss_scaler_exit(struct dcss_scaler *scl);
|
||||
void dcss_scaler_set_filter(struct dcss_scaler *scl, int ch_num,
|
||||
enum drm_scaling_filter scaling_filter);
|
||||
void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num,
|
||||
const struct drm_format_info *format,
|
||||
int src_xres, int src_yres, int dst_xres, int dst_yres,
|
||||
|
|
|
@ -103,15 +103,15 @@ static bool dcss_plane_can_rotate(const struct drm_format_info *format,
|
|||
bool mod_present, u64 modifier,
|
||||
unsigned int rotation)
|
||||
{
|
||||
bool linear_format = !mod_present ||
|
||||
(mod_present && modifier == DRM_FORMAT_MOD_LINEAR);
|
||||
bool linear_format = !mod_present || modifier == DRM_FORMAT_MOD_LINEAR;
|
||||
u32 supported_rotation = DRM_MODE_ROTATE_0;
|
||||
|
||||
if (!format->is_yuv && linear_format)
|
||||
supported_rotation = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
|
||||
DRM_MODE_REFLECT_MASK;
|
||||
else if (!format->is_yuv &&
|
||||
modifier == DRM_FORMAT_MOD_VIVANTE_TILED)
|
||||
(modifier == DRM_FORMAT_MOD_VIVANTE_TILED ||
|
||||
modifier == DRM_FORMAT_MOD_VIVANTE_SUPER_TILED))
|
||||
supported_rotation = DRM_MODE_ROTATE_MASK |
|
||||
DRM_MODE_REFLECT_MASK;
|
||||
else if (format->is_yuv && linear_format &&
|
||||
|
@ -257,7 +257,8 @@ static bool dcss_plane_needs_setup(struct drm_plane_state *state,
|
|||
state->src_h != old_state->src_h ||
|
||||
fb->format->format != old_fb->format->format ||
|
||||
fb->modifier != old_fb->modifier ||
|
||||
state->rotation != old_state->rotation;
|
||||
state->rotation != old_state->rotation ||
|
||||
state->scaling_filter != old_state->scaling_filter;
|
||||
}
|
||||
|
||||
static void dcss_plane_atomic_update(struct drm_plane *plane,
|
||||
|
@ -272,6 +273,7 @@ static void dcss_plane_atomic_update(struct drm_plane *plane,
|
|||
u32 src_w, src_h, dst_w, dst_h;
|
||||
struct drm_rect src, dst;
|
||||
bool enable = true;
|
||||
bool is_rotation_90_or_270;
|
||||
|
||||
if (!fb || !state->crtc || !state->visible)
|
||||
return;
|
||||
|
@ -309,8 +311,16 @@ static void dcss_plane_atomic_update(struct drm_plane *plane,
|
|||
|
||||
dcss_plane_atomic_set_base(dcss_plane);
|
||||
|
||||
is_rotation_90_or_270 = state->rotation & (DRM_MODE_ROTATE_90 |
|
||||
DRM_MODE_ROTATE_270);
|
||||
|
||||
dcss_scaler_set_filter(dcss->scaler, dcss_plane->ch_num,
|
||||
state->scaling_filter);
|
||||
|
||||
dcss_scaler_setup(dcss->scaler, dcss_plane->ch_num,
|
||||
state->fb->format, src_w, src_h,
|
||||
state->fb->format,
|
||||
is_rotation_90_or_270 ? src_h : src_w,
|
||||
is_rotation_90_or_270 ? src_w : src_h,
|
||||
dst_w, dst_h,
|
||||
drm_mode_vrefresh(&crtc_state->mode));
|
||||
|
||||
|
@ -388,6 +398,10 @@ struct dcss_plane *dcss_plane_init(struct drm_device *drm,
|
|||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
drm_plane_create_scaling_filter_property(&dcss_plane->base,
|
||||
BIT(DRM_SCALING_FILTER_DEFAULT) |
|
||||
BIT(DRM_SCALING_FILTER_NEAREST_NEIGHBOR));
|
||||
|
||||
drm_plane_create_rotation_property(&dcss_plane->base,
|
||||
DRM_MODE_ROTATE_0,
|
||||
DRM_MODE_ROTATE_0 |
|
||||
|
|
|
@ -77,6 +77,8 @@ struct dcss_scaler_ch {
|
|||
|
||||
u32 c_vstart;
|
||||
u32 c_hstart;
|
||||
|
||||
bool use_nn_interpolation;
|
||||
};
|
||||
|
||||
struct dcss_scaler {
|
||||
|
@ -243,6 +245,17 @@ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
|
|||
}
|
||||
}
|
||||
|
||||
static void dcss_scaler_nearest_neighbor_filter(bool use_5_taps,
|
||||
int coef[][PSC_NUM_TAPS])
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < PSC_STORED_PHASES; i++)
|
||||
for (j = 0; j < PSC_NUM_TAPS; j++)
|
||||
coef[i][j] = j == PSC_NUM_TAPS >> 1 ?
|
||||
(1 << PSC_COEFF_PRECISION) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dcss_scaler_filter_design() - Compute filter coefficients using
|
||||
* Gaussian filter.
|
||||
|
@ -253,7 +266,8 @@ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps,
|
|||
*/
|
||||
static void dcss_scaler_filter_design(int src_length, int dst_length,
|
||||
bool use_5_taps, bool phase0_identity,
|
||||
int coef[][PSC_NUM_TAPS])
|
||||
int coef[][PSC_NUM_TAPS],
|
||||
bool nn_interpolation)
|
||||
{
|
||||
int fc_q;
|
||||
|
||||
|
@ -263,6 +277,9 @@ static void dcss_scaler_filter_design(int src_length, int dst_length,
|
|||
else
|
||||
fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES);
|
||||
|
||||
if (nn_interpolation)
|
||||
dcss_scaler_nearest_neighbor_filter(use_5_taps, coef);
|
||||
else
|
||||
/* compute gaussian filter coefficients */
|
||||
dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef);
|
||||
}
|
||||
|
@ -653,12 +670,14 @@ static void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch,
|
|||
|
||||
/* horizontal luma */
|
||||
dcss_scaler_filter_design(src_xres, dst_xres, false,
|
||||
src_xres == dst_xres, coef);
|
||||
src_xres == dst_xres, coef,
|
||||
ch->use_nn_interpolation);
|
||||
dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);
|
||||
|
||||
/* vertical luma */
|
||||
dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,
|
||||
src_yres == dst_yres, coef);
|
||||
src_yres == dst_yres, coef,
|
||||
ch->use_nn_interpolation);
|
||||
|
||||
if (program_5_taps)
|
||||
dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
|
||||
|
@ -678,14 +697,14 @@ static void dcss_scaler_yuv_coef_set(struct dcss_scaler_ch *ch,
|
|||
/* horizontal chroma */
|
||||
dcss_scaler_filter_design(src_xres, dst_xres, false,
|
||||
(src_xres == dst_xres) && (ch->c_hstart == 0),
|
||||
coef);
|
||||
coef, ch->use_nn_interpolation);
|
||||
|
||||
dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HCHR, coef);
|
||||
|
||||
/* vertical chroma */
|
||||
dcss_scaler_filter_design(src_yres, dst_yres, program_5_taps,
|
||||
(src_yres == dst_yres) && (ch->c_vstart == 0),
|
||||
coef);
|
||||
coef, ch->use_nn_interpolation);
|
||||
if (program_5_taps)
|
||||
dcss_scaler_program_5_coef_set(ch, DCSS_SCALER_COEF_VCHR, coef);
|
||||
else
|
||||
|
@ -700,12 +719,14 @@ static void dcss_scaler_rgb_coef_set(struct dcss_scaler_ch *ch,
|
|||
|
||||
/* horizontal RGB */
|
||||
dcss_scaler_filter_design(src_xres, dst_xres, false,
|
||||
src_xres == dst_xres, coef);
|
||||
src_xres == dst_xres, coef,
|
||||
ch->use_nn_interpolation);
|
||||
dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_HLUM, coef);
|
||||
|
||||
/* vertical RGB */
|
||||
dcss_scaler_filter_design(src_yres, dst_yres, false,
|
||||
src_yres == dst_yres, coef);
|
||||
src_yres == dst_yres, coef,
|
||||
ch->use_nn_interpolation);
|
||||
dcss_scaler_program_7_coef_set(ch, DCSS_SCALER_COEF_VLUM, coef);
|
||||
}
|
||||
|
||||
|
@ -751,6 +772,14 @@ static void dcss_scaler_set_rgb10_order(struct dcss_scaler_ch *ch,
|
|||
ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS;
|
||||
}
|
||||
|
||||
void dcss_scaler_set_filter(struct dcss_scaler *scl, int ch_num,
|
||||
enum drm_scaling_filter scaling_filter)
|
||||
{
|
||||
struct dcss_scaler_ch *ch = &scl->ch[ch_num];
|
||||
|
||||
ch->use_nn_interpolation = scaling_filter == DRM_SCALING_FILTER_NEAREST_NEIGHBOR;
|
||||
}
|
||||
|
||||
void dcss_scaler_setup(struct dcss_scaler *scl, int ch_num,
|
||||
const struct drm_format_info *format,
|
||||
int src_xres, int src_yres, int dst_xres, int dst_yres,
|
||||
|
|
|
@ -225,7 +225,7 @@ struct drm_gem_object *lima_gem_create_object(struct drm_device *dev, size_t siz
|
|||
|
||||
mutex_init(&bo->lock);
|
||||
INIT_LIST_HEAD(&bo->va);
|
||||
|
||||
bo->base.map_wc = true;
|
||||
bo->base.base.funcs = &lima_gem_funcs;
|
||||
|
||||
return &bo->base.base;
|
||||
|
|
|
@ -4,6 +4,7 @@ config DRM_MCDE
|
|||
depends on CMA
|
||||
depends on ARM || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on COMMON_CLK
|
||||
select MFD_SYSCON
|
||||
select DRM_MIPI_DSI
|
||||
select DRM_BRIDGE
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
mcde_drm-y += mcde_drv.o mcde_dsi.o mcde_display.o
|
||||
mcde_drm-y += mcde_drv.o mcde_dsi.o mcde_clk_div.o mcde_display.o
|
||||
|
||||
obj-$(CONFIG_DRM_MCDE) += mcde_drm.o
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "mcde_drm.h"
|
||||
#include "mcde_display_regs.h"
|
||||
|
||||
/* The MCDE internal clock dividers for FIFO A and B */
|
||||
struct mcde_clk_div {
|
||||
struct clk_hw hw;
|
||||
struct mcde *mcde;
|
||||
u32 cr;
|
||||
u32 cr_div;
|
||||
};
|
||||
|
||||
static int mcde_clk_div_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
|
||||
struct mcde *mcde = cdiv->mcde;
|
||||
u32 val;
|
||||
|
||||
spin_lock(&mcde->fifo_crx1_lock);
|
||||
val = readl(mcde->regs + cdiv->cr);
|
||||
/*
|
||||
* Select the PLL72 (LCD) clock as parent
|
||||
* FIXME: implement other parents.
|
||||
*/
|
||||
val &= ~MCDE_CRX1_CLKSEL_MASK;
|
||||
val |= MCDE_CRX1_CLKSEL_CLKPLL72 << MCDE_CRX1_CLKSEL_SHIFT;
|
||||
/* Internal clock */
|
||||
val |= MCDE_CRA1_CLKTYPE_TVXCLKSEL1;
|
||||
|
||||
/* Clear then set the divider */
|
||||
val &= ~(MCDE_CRX1_BCD | MCDE_CRX1_PCD_MASK);
|
||||
val |= cdiv->cr_div;
|
||||
|
||||
writel(val, mcde->regs + cdiv->cr);
|
||||
spin_unlock(&mcde->fifo_crx1_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate, bool set_parent)
|
||||
{
|
||||
int best_div = 1, div;
|
||||
struct clk_hw *parent = clk_hw_get_parent(hw);
|
||||
unsigned long best_prate = 0;
|
||||
unsigned long best_diff = ~0ul;
|
||||
int max_div = (1 << MCDE_CRX1_PCD_BITS) - 1;
|
||||
|
||||
for (div = 1; div < max_div; div++) {
|
||||
unsigned long this_prate, div_rate, diff;
|
||||
|
||||
if (set_parent)
|
||||
this_prate = clk_hw_round_rate(parent, rate * div);
|
||||
else
|
||||
this_prate = *prate;
|
||||
div_rate = DIV_ROUND_UP_ULL(this_prate, div);
|
||||
diff = abs(rate - div_rate);
|
||||
|
||||
if (diff < best_diff) {
|
||||
best_div = div;
|
||||
best_diff = diff;
|
||||
best_prate = this_prate;
|
||||
}
|
||||
}
|
||||
|
||||
*prate = best_prate;
|
||||
return best_div;
|
||||
}
|
||||
|
||||
static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int div = mcde_clk_div_choose_div(hw, rate, prate, true);
|
||||
|
||||
return DIV_ROUND_UP_ULL(*prate, div);
|
||||
}
|
||||
|
||||
static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long prate)
|
||||
{
|
||||
struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
|
||||
struct mcde *mcde = cdiv->mcde;
|
||||
u32 cr;
|
||||
int div;
|
||||
|
||||
/*
|
||||
* If the MCDE is not powered we can't access registers.
|
||||
* It will come up with 0 in the divider register bits, which
|
||||
* means "divide by 2".
|
||||
*/
|
||||
if (!regulator_is_enabled(mcde->epod))
|
||||
return DIV_ROUND_UP_ULL(prate, 2);
|
||||
|
||||
cr = readl(mcde->regs + cdiv->cr);
|
||||
if (cr & MCDE_CRX1_BCD)
|
||||
return prate;
|
||||
|
||||
/* 0 in the PCD means "divide by 2", 1 means "divide by 3" etc */
|
||||
div = cr & MCDE_CRX1_PCD_MASK;
|
||||
div += 2;
|
||||
|
||||
return DIV_ROUND_UP_ULL(prate, div);
|
||||
}
|
||||
|
||||
static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long prate)
|
||||
{
|
||||
struct mcde_clk_div *cdiv = container_of(hw, struct mcde_clk_div, hw);
|
||||
int div = mcde_clk_div_choose_div(hw, rate, &prate, false);
|
||||
u32 cr = 0;
|
||||
|
||||
/*
|
||||
* We cache the CR bits to set the divide in the state so that
|
||||
* we can call this before we can even write to the hardware.
|
||||
*/
|
||||
if (div == 1) {
|
||||
/* Bypass clock divider */
|
||||
cr |= MCDE_CRX1_BCD;
|
||||
} else {
|
||||
div -= 2;
|
||||
cr |= div & MCDE_CRX1_PCD_MASK;
|
||||
}
|
||||
cdiv->cr_div = cr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops mcde_clk_div_ops = {
|
||||
.enable = mcde_clk_div_enable,
|
||||
.recalc_rate = mcde_clk_div_recalc_rate,
|
||||
.round_rate = mcde_clk_div_round_rate,
|
||||
.set_rate = mcde_clk_div_set_rate,
|
||||
};
|
||||
|
||||
int mcde_init_clock_divider(struct mcde *mcde)
|
||||
{
|
||||
struct device *dev = mcde->dev;
|
||||
struct mcde_clk_div *fifoa;
|
||||
struct mcde_clk_div *fifob;
|
||||
const char *parent_name;
|
||||
struct clk_init_data fifoa_init = {
|
||||
.name = "fifoa",
|
||||
.ops = &mcde_clk_div_ops,
|
||||
.parent_names = &parent_name,
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
};
|
||||
struct clk_init_data fifob_init = {
|
||||
.name = "fifob",
|
||||
.ops = &mcde_clk_div_ops,
|
||||
.parent_names = &parent_name,
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
};
|
||||
int ret;
|
||||
|
||||
spin_lock_init(&mcde->fifo_crx1_lock);
|
||||
parent_name = __clk_get_name(mcde->lcd_clk);
|
||||
|
||||
/* Allocate 2 clocks */
|
||||
fifoa = devm_kzalloc(dev, sizeof(*fifoa), GFP_KERNEL);
|
||||
if (!fifoa)
|
||||
return -ENOMEM;
|
||||
fifob = devm_kzalloc(dev, sizeof(*fifob), GFP_KERNEL);
|
||||
if (!fifob)
|
||||
return -ENOMEM;
|
||||
|
||||
fifoa->mcde = mcde;
|
||||
fifoa->cr = MCDE_CRA1;
|
||||
fifoa->hw.init = &fifoa_init;
|
||||
ret = devm_clk_hw_register(dev, &fifoa->hw);
|
||||
if (ret) {
|
||||
dev_err(dev, "error registering FIFO A clock divider\n");
|
||||
return ret;
|
||||
}
|
||||
mcde->fifoa_clk = fifoa->hw.clk;
|
||||
|
||||
fifob->mcde = mcde;
|
||||
fifob->cr = MCDE_CRB1;
|
||||
fifob->hw.init = &fifob_init;
|
||||
ret = devm_clk_hw_register(dev, &fifob->hw);
|
||||
if (ret) {
|
||||
dev_err(dev, "error registering FIFO B clock divider\n");
|
||||
return ret;
|
||||
}
|
||||
mcde->fifob_clk = fifob->hw.clk;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
@ -16,6 +17,7 @@
|
|||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_bridge.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
|
@ -57,10 +59,15 @@ enum mcde_overlay {
|
|||
MCDE_OVERLAY_5,
|
||||
};
|
||||
|
||||
enum mcde_dsi_formatter {
|
||||
enum mcde_formatter {
|
||||
MCDE_DSI_FORMATTER_0 = 0,
|
||||
MCDE_DSI_FORMATTER_1,
|
||||
MCDE_DSI_FORMATTER_2,
|
||||
MCDE_DSI_FORMATTER_3,
|
||||
MCDE_DSI_FORMATTER_4,
|
||||
MCDE_DSI_FORMATTER_5,
|
||||
MCDE_DPI_FORMATTER_0,
|
||||
MCDE_DPI_FORMATTER_1,
|
||||
};
|
||||
|
||||
void mcde_display_irq(struct mcde *mcde)
|
||||
|
@ -81,7 +88,7 @@ void mcde_display_irq(struct mcde *mcde)
|
|||
*
|
||||
* TODO: Currently only one DSI link is supported.
|
||||
*/
|
||||
if (mcde_dsi_irq(mcde->mdsi)) {
|
||||
if (!mcde->dpi_output && mcde_dsi_irq(mcde->mdsi)) {
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
|
@ -243,73 +250,70 @@ static int mcde_configure_extsrc(struct mcde *mcde, enum mcde_extsrc src,
|
|||
val = 0 << MCDE_EXTSRCXCONF_BUF_ID_SHIFT;
|
||||
val |= 1 << MCDE_EXTSRCXCONF_BUF_NB_SHIFT;
|
||||
val |= 0 << MCDE_EXTSRCXCONF_PRI_OVLID_SHIFT;
|
||||
/*
|
||||
* MCDE has inverse semantics from DRM on RBG/BGR which is why
|
||||
* all the modes are inversed here.
|
||||
*/
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_ARGB8888 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_XRGB8888 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_RGB888 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_BGR888:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_RGB888 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_ABGR4444:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_ARGB4444 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB4444:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_RGB444 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_XBGR4444:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_RGB444 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_IRGB1555 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_RGB565 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_BGR565:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_RGB565 <<
|
||||
MCDE_EXTSRCXCONF_BPP_SHIFT;
|
||||
val |= MCDE_EXTSRCXCONF_BGR;
|
||||
break;
|
||||
case DRM_FORMAT_YUV422:
|
||||
val |= MCDE_EXTSRCXCONF_BPP_YCBCR422 <<
|
||||
|
@ -556,6 +560,7 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch,
|
|||
<< MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_SHIFT;
|
||||
break;
|
||||
case MCDE_VIDEO_FORMATTER_FLOW:
|
||||
case MCDE_DPI_FORMATTER_FLOW:
|
||||
val = MCDE_CHNLXSYNCHMOD_SRC_SYNCH_HARDWARE
|
||||
<< MCDE_CHNLXSYNCHMOD_SRC_SYNCH_SHIFT;
|
||||
val |= MCDE_CHNLXSYNCHMOD_OUT_SYNCH_SRC_FORMATTER
|
||||
|
@ -564,7 +569,7 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch,
|
|||
default:
|
||||
dev_err(mcde->dev, "unknown flow mode %d\n",
|
||||
mcde->flow_mode);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
writel(val, mcde->regs + sync);
|
||||
|
@ -594,10 +599,35 @@ static void mcde_configure_channel(struct mcde *mcde, enum mcde_channel ch,
|
|||
mcde->regs + mux);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If using DPI configure the sync event.
|
||||
* TODO: this is for LCD only, it does not cover TV out.
|
||||
*/
|
||||
if (mcde->dpi_output) {
|
||||
u32 stripwidth;
|
||||
|
||||
stripwidth = 0xF000 / (mode->vdisplay * 4);
|
||||
dev_info(mcde->dev, "stripwidth: %d\n", stripwidth);
|
||||
|
||||
val = MCDE_SYNCHCONF_HWREQVEVENT_ACTIVE_VIDEO |
|
||||
(mode->hdisplay - 1 - stripwidth) << MCDE_SYNCHCONF_HWREQVCNT_SHIFT |
|
||||
MCDE_SYNCHCONF_SWINTVEVENT_ACTIVE_VIDEO |
|
||||
(mode->hdisplay - 1 - stripwidth) << MCDE_SYNCHCONF_SWINTVCNT_SHIFT;
|
||||
|
||||
switch (fifo) {
|
||||
case MCDE_FIFO_A:
|
||||
writel(val, mcde->regs + MCDE_SYNCHCONFA);
|
||||
break;
|
||||
case MCDE_FIFO_B:
|
||||
writel(val, mcde->regs + MCDE_SYNCHCONFB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mcde_configure_fifo(struct mcde *mcde, enum mcde_fifo fifo,
|
||||
enum mcde_dsi_formatter fmt,
|
||||
enum mcde_formatter fmt,
|
||||
int fifo_wtrmrk)
|
||||
{
|
||||
u32 val;
|
||||
|
@ -618,12 +648,49 @@ static void mcde_configure_fifo(struct mcde *mcde, enum mcde_fifo fifo,
|
|||
}
|
||||
|
||||
val = fifo_wtrmrk << MCDE_CTRLX_FIFOWTRMRK_SHIFT;
|
||||
/* We only support DSI formatting for now */
|
||||
val |= MCDE_CTRLX_FORMTYPE_DSI <<
|
||||
MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
|
||||
/* Select the formatter to use for this FIFO */
|
||||
val |= fmt << MCDE_CTRLX_FORMID_SHIFT;
|
||||
/*
|
||||
* Select the formatter to use for this FIFO
|
||||
*
|
||||
* The register definitions imply that different IDs should be used
|
||||
* by the DSI formatters depending on if they are in VID or CMD
|
||||
* mode, and the manual says they are dedicated but identical.
|
||||
* The vendor code uses them as it seems fit.
|
||||
*/
|
||||
switch (fmt) {
|
||||
case MCDE_DSI_FORMATTER_0:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DSI0VID << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
case MCDE_DSI_FORMATTER_1:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DSI0CMD << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
case MCDE_DSI_FORMATTER_2:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DSI1VID << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
case MCDE_DSI_FORMATTER_3:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DSI1CMD << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
case MCDE_DSI_FORMATTER_4:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DSI2VID << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
case MCDE_DSI_FORMATTER_5:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DSI << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DSI2CMD << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
case MCDE_DPI_FORMATTER_0:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DPITV << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DPIA << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
case MCDE_DPI_FORMATTER_1:
|
||||
val |= MCDE_CTRLX_FORMTYPE_DPITV << MCDE_CTRLX_FORMTYPE_SHIFT;
|
||||
val |= MCDE_CTRLX_FORMID_DPIB << MCDE_CTRLX_FORMID_SHIFT;
|
||||
break;
|
||||
}
|
||||
writel(val, mcde->regs + ctrl);
|
||||
|
||||
/* Blend source with Alpha 0xff on FIFO */
|
||||
|
@ -631,17 +698,54 @@ static void mcde_configure_fifo(struct mcde *mcde, enum mcde_fifo fifo,
|
|||
0xff << MCDE_CRX0_ALPHABLEND_SHIFT;
|
||||
writel(val, mcde->regs + cr0);
|
||||
|
||||
/* Set-up from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() */
|
||||
spin_lock(&mcde->fifo_crx1_lock);
|
||||
val = readl(mcde->regs + cr1);
|
||||
/*
|
||||
* Set-up from mcde_fmtr_dsi.c, fmtr_dsi_enable_video()
|
||||
* FIXME: a different clock needs to be selected for TV out.
|
||||
*/
|
||||
if (mcde->dpi_output) {
|
||||
struct drm_connector *connector = drm_panel_bridge_connector(mcde->bridge);
|
||||
u32 bus_format;
|
||||
|
||||
/* Use the MCDE clock for this FIFO */
|
||||
val = MCDE_CRX1_CLKSEL_MCDECLK << MCDE_CRX1_CLKSEL_SHIFT;
|
||||
/* Assume RGB888 24 bit if we have no further info */
|
||||
if (!connector->display_info.num_bus_formats) {
|
||||
dev_info(mcde->dev, "panel does not specify bus format, assume RGB888\n");
|
||||
bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
} else {
|
||||
bus_format = connector->display_info.bus_formats[0];
|
||||
}
|
||||
|
||||
/* TODO: when adding DPI support add OUTBPP etc here */
|
||||
/*
|
||||
* Set up the CDWIN and OUTBPP for the LCD
|
||||
*
|
||||
* FIXME: fill this in if you know the correspondance between the MIPI
|
||||
* DPI specification and the media bus formats.
|
||||
*/
|
||||
val &= ~MCDE_CRX1_CDWIN_MASK;
|
||||
val &= ~MCDE_CRX1_OUTBPP_MASK;
|
||||
switch (bus_format) {
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
val |= MCDE_CRX1_CDWIN_24BPP << MCDE_CRX1_CDWIN_SHIFT;
|
||||
val |= MCDE_CRX1_OUTBPP_24BPP << MCDE_CRX1_OUTBPP_SHIFT;
|
||||
break;
|
||||
default:
|
||||
dev_err(mcde->dev, "unknown bus format, assume RGB888\n");
|
||||
val |= MCDE_CRX1_CDWIN_24BPP << MCDE_CRX1_CDWIN_SHIFT;
|
||||
val |= MCDE_CRX1_OUTBPP_24BPP << MCDE_CRX1_OUTBPP_SHIFT;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Use the MCDE clock for DSI */
|
||||
val &= ~MCDE_CRX1_CLKSEL_MASK;
|
||||
val |= MCDE_CRX1_CLKSEL_MCDECLK << MCDE_CRX1_CLKSEL_SHIFT;
|
||||
}
|
||||
writel(val, mcde->regs + cr1);
|
||||
spin_unlock(&mcde->fifo_crx1_lock);
|
||||
};
|
||||
|
||||
static void mcde_configure_dsi_formatter(struct mcde *mcde,
|
||||
enum mcde_dsi_formatter fmt,
|
||||
enum mcde_formatter fmt,
|
||||
u32 formatter_frame,
|
||||
int pkt_size)
|
||||
{
|
||||
|
@ -681,6 +785,9 @@ static void mcde_configure_dsi_formatter(struct mcde *mcde,
|
|||
delay0 = MCDE_DSIVID2DELAY0;
|
||||
delay1 = MCDE_DSIVID2DELAY1;
|
||||
break;
|
||||
default:
|
||||
dev_err(mcde->dev, "tried to configure a non-DSI formatter as DSI\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -700,7 +807,9 @@ static void mcde_configure_dsi_formatter(struct mcde *mcde,
|
|||
MCDE_DSICONF0_PACKING_SHIFT;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB666_PACKED:
|
||||
val |= MCDE_DSICONF0_PACKING_RGB666_PACKED <<
|
||||
dev_err(mcde->dev,
|
||||
"we cannot handle the packed RGB666 format\n");
|
||||
val |= MCDE_DSICONF0_PACKING_RGB666 <<
|
||||
MCDE_DSICONF0_PACKING_SHIFT;
|
||||
break;
|
||||
case MIPI_DSI_FMT_RGB565:
|
||||
|
@ -860,73 +969,140 @@ static int mcde_dsi_get_pkt_div(int ppl, int fifo_size)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *cstate,
|
||||
struct drm_plane_state *plane_state)
|
||||
static void mcde_setup_dpi(struct mcde *mcde, const struct drm_display_mode *mode,
|
||||
int *fifo_wtrmrk_lvl)
|
||||
{
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_plane *plane = &pipe->plane;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct mcde *mcde = to_mcde(drm);
|
||||
const struct drm_display_mode *mode = &cstate->mode;
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
u32 format = fb->format->format;
|
||||
u32 formatter_ppl = mode->hdisplay; /* pixels per line */
|
||||
u32 formatter_lpf = mode->vdisplay; /* lines per frame */
|
||||
int pkt_size, fifo_wtrmrk;
|
||||
int cpp = fb->format->cpp[0];
|
||||
int formatter_cpp;
|
||||
struct drm_format_name_buf tmp;
|
||||
u32 formatter_frame;
|
||||
u32 pkt_div;
|
||||
struct drm_connector *connector = drm_panel_bridge_connector(mcde->bridge);
|
||||
u32 hsw, hfp, hbp;
|
||||
u32 vsw, vfp, vbp;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* This powers up the entire MCDE block and the DSI hardware */
|
||||
ret = regulator_enable(mcde->epod);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "can't re-enable EPOD regulator\n");
|
||||
return;
|
||||
}
|
||||
/* FIXME: we only support LCD, implement TV out */
|
||||
hsw = mode->hsync_end - mode->hsync_start;
|
||||
hfp = mode->hsync_start - mode->hdisplay;
|
||||
hbp = mode->htotal - mode->hsync_end;
|
||||
vsw = mode->vsync_end - mode->vsync_start;
|
||||
vfp = mode->vsync_start - mode->vdisplay;
|
||||
vbp = mode->vtotal - mode->vsync_end;
|
||||
|
||||
dev_info(drm->dev, "enable MCDE, %d x %d format %s\n",
|
||||
mode->hdisplay, mode->vdisplay,
|
||||
drm_get_format_name(format, &tmp));
|
||||
if (!mcde->mdsi) {
|
||||
/* TODO: deal with this for non-DSI output */
|
||||
dev_err(drm->dev, "no DSI master attached!\n");
|
||||
return;
|
||||
}
|
||||
dev_info(mcde->dev, "output on DPI LCD from channel A\n");
|
||||
/* Display actual values */
|
||||
dev_info(mcde->dev, "HSW: %d, HFP: %d, HBP: %d, VSW: %d, VFP: %d, VBP: %d\n",
|
||||
hsw, hfp, hbp, vsw, vfp, vbp);
|
||||
|
||||
/*
|
||||
* The pixel fetcher is 128 64-bit words deep = 1024 bytes.
|
||||
* One overlay of 32bpp (4 cpp) assumed, fetch 160 pixels.
|
||||
* 160 * 4 = 640 bytes.
|
||||
*/
|
||||
*fifo_wtrmrk_lvl = 640;
|
||||
|
||||
/* Set up the main control, watermark level at 7 */
|
||||
val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
|
||||
/* 24 bits DPI: connect LSB Ch B to D[0:7] */
|
||||
val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
|
||||
/* TV out: connect LSB Ch B to D[8:15] */
|
||||
val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
|
||||
|
||||
/*
|
||||
* This sets up the internal silicon muxing of the DPI
|
||||
* lines. This is how the silicon connects out to the
|
||||
* external pins, then the pins need to be further
|
||||
* configured into "alternate functions" using pin control
|
||||
* to actually get the signals out.
|
||||
*
|
||||
* FIXME: this is hardcoded to the only setting found in
|
||||
* the wild. If we need to use different settings for
|
||||
* different DPI displays, make this parameterizable from
|
||||
* the device tree.
|
||||
*/
|
||||
/* 24 bits DPI: connect Ch A LSB to D[0:7] */
|
||||
val |= 0 << MCDE_CONF0_OUTMUX0_SHIFT;
|
||||
/* 24 bits DPI: connect Ch A MID to D[8:15] */
|
||||
val |= 1 << MCDE_CONF0_OUTMUX1_SHIFT;
|
||||
/* Don't care about this muxing */
|
||||
val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
|
||||
/* 24 bits DPI: connect MID Ch B to D[24:31] */
|
||||
val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
|
||||
/* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */
|
||||
val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
|
||||
/* Syncmux bits zero: DPI channel A and B on output pins A and B resp */
|
||||
/* Don't care about this muxing */
|
||||
val |= 0 << MCDE_CONF0_OUTMUX3_SHIFT;
|
||||
/* 24 bits DPI: connect Ch A MSB to D[32:39] */
|
||||
val |= 2 << MCDE_CONF0_OUTMUX4_SHIFT;
|
||||
/* Syncmux bits zero: DPI channel A */
|
||||
writel(val, mcde->regs + MCDE_CONF0);
|
||||
|
||||
/* Clear any pending interrupts */
|
||||
mcde_display_disable_irqs(mcde);
|
||||
writel(0, mcde->regs + MCDE_IMSCERR);
|
||||
writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
|
||||
/* This hammers us into LCD mode */
|
||||
writel(0, mcde->regs + MCDE_TVCRA);
|
||||
|
||||
dev_info(drm->dev, "output in %s mode, format %dbpp\n",
|
||||
/* Front porch and sync width */
|
||||
val = (vsw << MCDE_TVBL1_BEL1_SHIFT);
|
||||
val |= (vfp << MCDE_TVBL1_BSL1_SHIFT);
|
||||
writel(val, mcde->regs + MCDE_TVBL1A);
|
||||
/* The vendor driver sets the same value into TVBL2A */
|
||||
writel(val, mcde->regs + MCDE_TVBL2A);
|
||||
|
||||
/* Vertical back porch */
|
||||
val = (vbp << MCDE_TVDVO_DVO1_SHIFT);
|
||||
/* The vendor drivers sets the same value into TVDVOA */
|
||||
val |= (vbp << MCDE_TVDVO_DVO2_SHIFT);
|
||||
writel(val, mcde->regs + MCDE_TVDVOA);
|
||||
|
||||
/* Horizontal back porch, as 0 = 1 cycle we need to subtract 1 */
|
||||
writel((hbp - 1), mcde->regs + MCDE_TVTIM1A);
|
||||
|
||||
/* Horizongal sync width and horizonal front porch, 0 = 1 cycle */
|
||||
val = ((hsw - 1) << MCDE_TVLBALW_LBW_SHIFT);
|
||||
val |= ((hfp - 1) << MCDE_TVLBALW_ALW_SHIFT);
|
||||
writel(val, mcde->regs + MCDE_TVLBALWA);
|
||||
|
||||
/* Blank some TV registers we don't use */
|
||||
writel(0, mcde->regs + MCDE_TVISLA);
|
||||
writel(0, mcde->regs + MCDE_TVBLUA);
|
||||
|
||||
/* Set up sync inversion etc */
|
||||
val = 0;
|
||||
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
|
||||
val |= MCDE_LCDTIM1B_IHS;
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
val |= MCDE_LCDTIM1B_IVS;
|
||||
if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW)
|
||||
val |= MCDE_LCDTIM1B_IOE;
|
||||
if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
|
||||
val |= MCDE_LCDTIM1B_IPC;
|
||||
writel(val, mcde->regs + MCDE_LCDTIM1A);
|
||||
}
|
||||
|
||||
static void mcde_setup_dsi(struct mcde *mcde, const struct drm_display_mode *mode,
|
||||
int cpp, int *fifo_wtrmrk_lvl, int *dsi_formatter_frame,
|
||||
int *dsi_pkt_size)
|
||||
{
|
||||
u32 formatter_ppl = mode->hdisplay; /* pixels per line */
|
||||
u32 formatter_lpf = mode->vdisplay; /* lines per frame */
|
||||
int formatter_frame;
|
||||
int formatter_cpp;
|
||||
int fifo_wtrmrk;
|
||||
u32 pkt_div;
|
||||
int pkt_size;
|
||||
u32 val;
|
||||
|
||||
dev_info(mcde->dev, "output in %s mode, format %dbpp\n",
|
||||
(mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO) ?
|
||||
"VIDEO" : "CMD",
|
||||
mipi_dsi_pixel_format_to_bpp(mcde->mdsi->format));
|
||||
formatter_cpp =
|
||||
mipi_dsi_pixel_format_to_bpp(mcde->mdsi->format) / 8;
|
||||
dev_info(drm->dev, "overlay CPP %d bytes, DSI CPP %d bytes\n",
|
||||
cpp,
|
||||
formatter_cpp);
|
||||
dev_info(mcde->dev, "Overlay CPP: %d bytes, DSI formatter CPP %d bytes\n",
|
||||
cpp, formatter_cpp);
|
||||
|
||||
/* Set up the main control, watermark level at 7 */
|
||||
val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT;
|
||||
|
||||
/*
|
||||
* This is the internal silicon muxing of the DPI
|
||||
* (parallell display) lines. Since we are not using
|
||||
* this at all (we are using DSI) these are just
|
||||
* dummy values from the vendor tree.
|
||||
*/
|
||||
val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT;
|
||||
val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT;
|
||||
val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT;
|
||||
val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT;
|
||||
val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT;
|
||||
writel(val, mcde->regs + MCDE_CONF0);
|
||||
|
||||
/* Calculations from mcde_fmtr_dsi.c, fmtr_dsi_enable_video() */
|
||||
|
||||
|
@ -948,9 +1124,9 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
/* The FIFO is 640 entries deep on this v3 hardware */
|
||||
pkt_div = mcde_dsi_get_pkt_div(mode->hdisplay, 640);
|
||||
}
|
||||
dev_dbg(drm->dev, "FIFO watermark after flooring: %d bytes\n",
|
||||
dev_dbg(mcde->dev, "FIFO watermark after flooring: %d bytes\n",
|
||||
fifo_wtrmrk);
|
||||
dev_dbg(drm->dev, "Packet divisor: %d bytes\n", pkt_div);
|
||||
dev_dbg(mcde->dev, "Packet divisor: %d bytes\n", pkt_div);
|
||||
|
||||
/* NOTE: pkt_div is 1 for video mode */
|
||||
pkt_size = (formatter_ppl * formatter_cpp) / pkt_div;
|
||||
|
@ -958,16 +1134,64 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
if (!(mcde->mdsi->mode_flags & MIPI_DSI_MODE_VIDEO))
|
||||
pkt_size++;
|
||||
|
||||
dev_dbg(drm->dev, "DSI packet size: %d * %d bytes per line\n",
|
||||
dev_dbg(mcde->dev, "DSI packet size: %d * %d bytes per line\n",
|
||||
pkt_size, pkt_div);
|
||||
dev_dbg(drm->dev, "Overlay frame size: %u bytes\n",
|
||||
dev_dbg(mcde->dev, "Overlay frame size: %u bytes\n",
|
||||
mode->hdisplay * mode->vdisplay * cpp);
|
||||
/* NOTE: pkt_div is 1 for video mode */
|
||||
formatter_frame = pkt_size * pkt_div * formatter_lpf;
|
||||
dev_dbg(mcde->dev, "Formatter frame size: %u bytes\n", formatter_frame);
|
||||
|
||||
*fifo_wtrmrk_lvl = fifo_wtrmrk;
|
||||
*dsi_pkt_size = pkt_size;
|
||||
*dsi_formatter_frame = formatter_frame;
|
||||
}
|
||||
|
||||
static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *cstate,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct drm_crtc *crtc = &pipe->crtc;
|
||||
struct drm_plane *plane = &pipe->plane;
|
||||
struct drm_device *drm = crtc->dev;
|
||||
struct mcde *mcde = to_mcde(drm);
|
||||
const struct drm_display_mode *mode = &cstate->mode;
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
u32 format = fb->format->format;
|
||||
int dsi_pkt_size;
|
||||
int fifo_wtrmrk;
|
||||
int cpp = fb->format->cpp[0];
|
||||
struct drm_format_name_buf tmp;
|
||||
u32 dsi_formatter_frame;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* This powers up the entire MCDE block and the DSI hardware */
|
||||
ret = regulator_enable(mcde->epod);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "can't re-enable EPOD regulator\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_info(drm->dev, "enable MCDE, %d x %d format %s\n",
|
||||
mode->hdisplay, mode->vdisplay,
|
||||
drm_get_format_name(format, &tmp));
|
||||
|
||||
|
||||
/* Clear any pending interrupts */
|
||||
mcde_display_disable_irqs(mcde);
|
||||
writel(0, mcde->regs + MCDE_IMSCERR);
|
||||
writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
|
||||
|
||||
if (mcde->dpi_output)
|
||||
mcde_setup_dpi(mcde, mode, &fifo_wtrmrk);
|
||||
else
|
||||
mcde_setup_dsi(mcde, mode, cpp, &fifo_wtrmrk,
|
||||
&dsi_formatter_frame, &dsi_pkt_size);
|
||||
|
||||
mcde->stride = mode->hdisplay * cpp;
|
||||
dev_dbg(drm->dev, "Overlay line stride: %u bytes\n",
|
||||
mcde->stride);
|
||||
/* NOTE: pkt_div is 1 for video mode */
|
||||
formatter_frame = pkt_size * pkt_div * formatter_lpf;
|
||||
dev_dbg(drm->dev, "Formatter frame size: %u bytes\n", formatter_frame);
|
||||
|
||||
/* Drain the FIFO A + channel 0 pipe so we have a clean slate */
|
||||
mcde_drain_pipe(mcde, MCDE_FIFO_A, MCDE_CHANNEL_0);
|
||||
|
@ -995,6 +1219,27 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
*/
|
||||
mcde_configure_channel(mcde, MCDE_CHANNEL_0, MCDE_FIFO_A, mode);
|
||||
|
||||
if (mcde->dpi_output) {
|
||||
unsigned long lcd_freq;
|
||||
|
||||
/* Configure FIFO A to use DPI formatter 0 */
|
||||
mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DPI_FORMATTER_0,
|
||||
fifo_wtrmrk);
|
||||
|
||||
/* Set up and enable the LCD clock */
|
||||
lcd_freq = clk_round_rate(mcde->fifoa_clk, mode->clock * 1000);
|
||||
ret = clk_set_rate(mcde->fifoa_clk, lcd_freq);
|
||||
if (ret)
|
||||
dev_err(mcde->dev, "failed to set LCD clock rate %lu Hz\n",
|
||||
lcd_freq);
|
||||
ret = clk_prepare_enable(mcde->fifoa_clk);
|
||||
if (ret) {
|
||||
dev_err(mcde->dev, "failed to enable FIFO A DPI clock\n");
|
||||
return;
|
||||
}
|
||||
dev_info(mcde->dev, "LCD FIFO A clk rate %lu Hz\n",
|
||||
clk_get_rate(mcde->fifoa_clk));
|
||||
} else {
|
||||
/* Configure FIFO A to use DSI formatter 0 */
|
||||
mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0,
|
||||
fifo_wtrmrk);
|
||||
|
@ -1002,22 +1247,19 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
|
|||
/*
|
||||
* This brings up the DSI bridge which is tightly connected
|
||||
* to the MCDE DSI formatter.
|
||||
*
|
||||
* FIXME: if we want to use another formatter, such as DPI,
|
||||
* we need to be more elaborate here and select the appropriate
|
||||
* bridge.
|
||||
*/
|
||||
mcde_dsi_enable(mcde->bridge);
|
||||
|
||||
/* Configure the DSI formatter 0 for the DSI panel output */
|
||||
mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
|
||||
formatter_frame, pkt_size);
|
||||
dsi_formatter_frame, dsi_pkt_size);
|
||||
}
|
||||
|
||||
switch (mcde->flow_mode) {
|
||||
case MCDE_COMMAND_TE_FLOW:
|
||||
case MCDE_COMMAND_BTA_TE_FLOW:
|
||||
case MCDE_VIDEO_TE_FLOW:
|
||||
/* We are using TE in some comination */
|
||||
/* We are using TE in some combination */
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
val = MCDE_VSCRC_VSPOL;
|
||||
else
|
||||
|
@ -1069,8 +1311,12 @@ static void mcde_display_disable(struct drm_simple_display_pipe *pipe)
|
|||
/* Disable FIFO A flow */
|
||||
mcde_disable_fifo(mcde, MCDE_FIFO_A, true);
|
||||
|
||||
if (mcde->dpi_output) {
|
||||
clk_disable_unprepare(mcde->fifoa_clk);
|
||||
} else {
|
||||
/* This disables the DSI bridge */
|
||||
mcde_dsi_disable(mcde->bridge);
|
||||
}
|
||||
|
||||
event = crtc->state->event;
|
||||
if (event) {
|
||||
|
@ -1261,6 +1507,10 @@ int mcde_display_init(struct drm_device *drm)
|
|||
DRM_FORMAT_YUV422,
|
||||
};
|
||||
|
||||
ret = mcde_init_clock_divider(mcde);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_simple_display_pipe_init(drm, &mcde->pipe,
|
||||
&mcde_display_funcs,
|
||||
formats, ARRAY_SIZE(formats),
|
||||
|
|
|
@ -215,6 +215,80 @@
|
|||
#define MCDE_OVLXCOMP_Z_SHIFT 27
|
||||
#define MCDE_OVLXCOMP_Z_MASK 0x78000000
|
||||
|
||||
/* DPI/TV configuration registers, channel A and B */
|
||||
#define MCDE_TVCRA 0x00000838
|
||||
#define MCDE_TVCRB 0x00000A38
|
||||
#define MCDE_TVCR_MOD_TV BIT(0) /* 0 = LCD mode */
|
||||
#define MCDE_TVCR_INTEREN BIT(1)
|
||||
#define MCDE_TVCR_IFIELD BIT(2)
|
||||
#define MCDE_TVCR_TVMODE_SDTV_656P (0 << 3)
|
||||
#define MCDE_TVCR_TVMODE_SDTV_656P_LE (3 << 3)
|
||||
#define MCDE_TVCR_TVMODE_SDTV_656P_BE (4 << 3)
|
||||
#define MCDE_TVCR_SDTVMODE_Y0CBY1CR (0 << 6)
|
||||
#define MCDE_TVCR_SDTVMODE_CBY0CRY1 (1 << 6)
|
||||
#define MCDE_TVCR_AVRGEN BIT(8)
|
||||
#define MCDE_TVCR_CKINV BIT(9)
|
||||
|
||||
/* TV blanking control register 1, channel A and B */
|
||||
#define MCDE_TVBL1A 0x0000083C
|
||||
#define MCDE_TVBL1B 0x00000A3C
|
||||
#define MCDE_TVBL1_BEL1_SHIFT 0 /* VFP vertical front porch 11 bits */
|
||||
#define MCDE_TVBL1_BSL1_SHIFT 16 /* VSW vertical sync pulse width 11 bits */
|
||||
|
||||
/* Pixel processing TV start line, channel A and B */
|
||||
#define MCDE_TVISLA 0x00000840
|
||||
#define MCDE_TVISLB 0x00000A40
|
||||
#define MCDE_TVISL_FSL1_SHIFT 0 /* Field 1 identification start line 11 bits */
|
||||
#define MCDE_TVISL_FSL2_SHIFT 16 /* Field 2 identification start line 11 bits */
|
||||
|
||||
/* Pixel processing TV DVO offset */
|
||||
#define MCDE_TVDVOA 0x00000844
|
||||
#define MCDE_TVDVOB 0x00000A44
|
||||
#define MCDE_TVDVO_DVO1_SHIFT 0 /* VBP vertical back porch 0 = 0 */
|
||||
#define MCDE_TVDVO_DVO2_SHIFT 16
|
||||
|
||||
/*
|
||||
* Pixel processing TV Timing 1
|
||||
* HBP horizontal back porch 11 bits horizontal offset
|
||||
* 0 = 1 pixel HBP, 255 = 256 pixels, so actual value - 1
|
||||
*/
|
||||
#define MCDE_TVTIM1A 0x0000084C
|
||||
#define MCDE_TVTIM1B 0x00000A4C
|
||||
|
||||
/* Pixel processing TV LBALW */
|
||||
/* 0 = 1 clock cycle, 255 = 256 clock cycles */
|
||||
#define MCDE_TVLBALWA 0x00000850
|
||||
#define MCDE_TVLBALWB 0x00000A50
|
||||
#define MCDE_TVLBALW_LBW_SHIFT 0 /* HSW horizonal sync width, line blanking width 11 bits */
|
||||
#define MCDE_TVLBALW_ALW_SHIFT 16 /* HFP horizontal front porch, active line width 11 bits */
|
||||
|
||||
/* TV blanking control register 1, channel A and B */
|
||||
#define MCDE_TVBL2A 0x00000854
|
||||
#define MCDE_TVBL2B 0x00000A54
|
||||
#define MCDE_TVBL2_BEL2_SHIFT 0 /* Field 2 blanking end line 11 bits */
|
||||
#define MCDE_TVBL2_BSL2_SHIFT 16 /* Field 2 blanking start line 11 bits */
|
||||
|
||||
/* Pixel processing TV background */
|
||||
#define MCDE_TVBLUA 0x00000858
|
||||
#define MCDE_TVBLUB 0x00000A58
|
||||
#define MCDE_TVBLU_TVBLU_SHIFT 0 /* 8 bits luminance */
|
||||
#define MCDE_TVBLU_TVBCB_SHIFT 8 /* 8 bits Cb chrominance */
|
||||
#define MCDE_TVBLU_TVBCR_SHIFT 16 /* 8 bits Cr chrominance */
|
||||
|
||||
/* Pixel processing LCD timing 1 */
|
||||
#define MCDE_LCDTIM1A 0x00000860
|
||||
#define MCDE_LCDTIM1B 0x00000A60
|
||||
/* inverted vertical sync pulse for HRTFT 0 = active low, 1 active high */
|
||||
#define MCDE_LCDTIM1B_IVP BIT(19)
|
||||
/* inverted vertical sync, 0 = active high (the normal), 1 = active low */
|
||||
#define MCDE_LCDTIM1B_IVS BIT(20)
|
||||
/* inverted horizontal sync, 0 = active high (the normal), 1 = active low */
|
||||
#define MCDE_LCDTIM1B_IHS BIT(21)
|
||||
/* inverted panel clock 0 = rising edge data out, 1 = falling edge data out */
|
||||
#define MCDE_LCDTIM1B_IPC BIT(22)
|
||||
/* invert output enable 0 = active high, 1 = active low */
|
||||
#define MCDE_LCDTIM1B_IOE BIT(23)
|
||||
|
||||
#define MCDE_CRC 0x00000C00
|
||||
#define MCDE_CRC_C1EN BIT(2)
|
||||
#define MCDE_CRC_C2EN BIT(3)
|
||||
|
@ -360,6 +434,7 @@
|
|||
#define MCDE_CRB1 0x00000A04
|
||||
#define MCDE_CRX1_PCD_SHIFT 0
|
||||
#define MCDE_CRX1_PCD_MASK 0x000003FF
|
||||
#define MCDE_CRX1_PCD_BITS 10
|
||||
#define MCDE_CRX1_CLKSEL_SHIFT 10
|
||||
#define MCDE_CRX1_CLKSEL_MASK 0x00001C00
|
||||
#define MCDE_CRX1_CLKSEL_CLKPLL72 0
|
||||
|
@ -421,8 +496,20 @@
|
|||
#define MCDE_ROTACONF 0x0000087C
|
||||
#define MCDE_ROTBCONF 0x00000A7C
|
||||
|
||||
/* Synchronization event configuration */
|
||||
#define MCDE_SYNCHCONFA 0x00000880
|
||||
#define MCDE_SYNCHCONFB 0x00000A80
|
||||
#define MCDE_SYNCHCONF_HWREQVEVENT_SHIFT 0
|
||||
#define MCDE_SYNCHCONF_HWREQVEVENT_VSYNC (0 << 0)
|
||||
#define MCDE_SYNCHCONF_HWREQVEVENT_BACK_PORCH (1 << 0)
|
||||
#define MCDE_SYNCHCONF_HWREQVEVENT_ACTIVE_VIDEO (2 << 0)
|
||||
#define MCDE_SYNCHCONF_HWREQVEVENT_FRONT_PORCH (3 << 0)
|
||||
#define MCDE_SYNCHCONF_HWREQVCNT_SHIFT 2 /* 14 bits */
|
||||
#define MCDE_SYNCHCONF_SWINTVEVENT_VSYNC (0 << 16)
|
||||
#define MCDE_SYNCHCONF_SWINTVEVENT_BACK_PORCH (1 << 16)
|
||||
#define MCDE_SYNCHCONF_SWINTVEVENT_ACTIVE_VIDEO (2 << 16)
|
||||
#define MCDE_SYNCHCONF_SWINTVEVENT_FRONT_PORCH (3 << 16)
|
||||
#define MCDE_SYNCHCONF_SWINTVCNT_SHIFT 18 /* 14 bits */
|
||||
|
||||
/* Channel A+B control registers */
|
||||
#define MCDE_CTRLA 0x00000884
|
||||
|
@ -465,8 +552,8 @@
|
|||
#define MCDE_DSICONF0_PACKING_MASK 0x00700000
|
||||
#define MCDE_DSICONF0_PACKING_RGB565 0
|
||||
#define MCDE_DSICONF0_PACKING_RGB666 1
|
||||
#define MCDE_DSICONF0_PACKING_RGB666_PACKED 2
|
||||
#define MCDE_DSICONF0_PACKING_RGB888 3
|
||||
#define MCDE_DSICONF0_PACKING_RGB888 2
|
||||
#define MCDE_DSICONF0_PACKING_BGR888 3
|
||||
#define MCDE_DSICONF0_PACKING_HDTV 4
|
||||
|
||||
#define MCDE_DSIVID0FRAME 0x00000E04
|
||||
|
|
|
@ -62,6 +62,8 @@ enum mcde_flow_mode {
|
|||
MCDE_VIDEO_TE_FLOW,
|
||||
/* Video mode with the formatter itself as sync source */
|
||||
MCDE_VIDEO_FORMATTER_FLOW,
|
||||
/* DPI video with the formatter itsels as sync source */
|
||||
MCDE_DPI_FORMATTER_FLOW,
|
||||
};
|
||||
|
||||
struct mcde {
|
||||
|
@ -72,6 +74,7 @@ struct mcde {
|
|||
struct drm_connector *connector;
|
||||
struct drm_simple_display_pipe pipe;
|
||||
struct mipi_dsi_device *mdsi;
|
||||
bool dpi_output;
|
||||
s16 stride;
|
||||
enum mcde_flow_mode flow_mode;
|
||||
unsigned int flow_active;
|
||||
|
@ -82,6 +85,11 @@ struct mcde {
|
|||
struct clk *mcde_clk;
|
||||
struct clk *lcd_clk;
|
||||
struct clk *hdmi_clk;
|
||||
/* Handles to the clock dividers for FIFO A and B */
|
||||
struct clk *fifoa_clk;
|
||||
struct clk *fifob_clk;
|
||||
/* Locks the MCDE FIFO control register A and B */
|
||||
spinlock_t fifo_crx1_lock;
|
||||
|
||||
struct regulator *epod;
|
||||
struct regulator *vana;
|
||||
|
@ -105,4 +113,6 @@ void mcde_display_irq(struct mcde *mcde);
|
|||
void mcde_display_disable_irqs(struct mcde *mcde);
|
||||
int mcde_display_init(struct drm_device *drm);
|
||||
|
||||
int mcde_init_clock_divider(struct mcde *mcde);
|
||||
|
||||
#endif /* _MCDE_DRM_H_ */
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
* The hardware has four display pipes, and the layout is a little
|
||||
* bit like this::
|
||||
*
|
||||
* Memory -> Overlay -> Channel -> FIFO -> 5 formatters -> DSI/DPI
|
||||
* External 0..5 0..3 A,B, 3 x DSI bridge
|
||||
* Memory -> Overlay -> Channel -> FIFO -> 8 formatters -> DSI/DPI
|
||||
* External 0..5 0..3 A,B, 6 x DSI bridge
|
||||
* source 0..9 C0,C1 2 x DPI
|
||||
*
|
||||
* FIFOs A and B are for LCD and HDMI while FIFO CO/C1 are for
|
||||
* panels with embedded buffer.
|
||||
* 3 of the formatters are for DSI.
|
||||
* 6 of the formatters are for DSI, 3 pairs for VID/CMD respectively.
|
||||
* 2 of the formatters are for DPI.
|
||||
*
|
||||
* Behind the formatters are the DSI or DPI ports that route to
|
||||
|
@ -130,9 +130,37 @@ static int mcde_modeset_init(struct drm_device *drm)
|
|||
struct mcde *mcde = to_mcde(drm);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If no other bridge was found, check if we have a DPI panel or
|
||||
* any other bridge connected directly to the MCDE DPI output.
|
||||
* If a DSI bridge is found, DSI will take precedence.
|
||||
*
|
||||
* TODO: more elaborate bridge selection if we have more than one
|
||||
* thing attached to the system.
|
||||
*/
|
||||
if (!mcde->bridge) {
|
||||
dev_err(drm->dev, "no display output bridge yet\n");
|
||||
return -EPROBE_DEFER;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(drm->dev->of_node,
|
||||
0, 0, &panel, &bridge);
|
||||
if (ret) {
|
||||
dev_err(drm->dev,
|
||||
"Could not locate any output bridge or panel\n");
|
||||
return ret;
|
||||
}
|
||||
if (panel) {
|
||||
bridge = drm_panel_bridge_add_typed(panel,
|
||||
DRM_MODE_CONNECTOR_DPI);
|
||||
if (IS_ERR(bridge)) {
|
||||
dev_err(drm->dev,
|
||||
"Could not connect panel bridge\n");
|
||||
return PTR_ERR(bridge);
|
||||
}
|
||||
}
|
||||
mcde->dpi_output = true;
|
||||
mcde->bridge = bridge;
|
||||
mcde->flow_mode = MCDE_DPI_FORMATTER_FLOW;
|
||||
}
|
||||
|
||||
mode_config = &drm->mode_config;
|
||||
|
@ -156,13 +184,7 @@ static int mcde_modeset_init(struct drm_device *drm)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach the DSI bridge
|
||||
*
|
||||
* TODO: when adding support for the DPI bridge or several DSI bridges,
|
||||
* we selectively connect the bridge(s) here instead of this simple
|
||||
* attachment.
|
||||
*/
|
||||
/* Attach the bridge. */
|
||||
ret = drm_simple_display_pipe_attach_bridge(&mcde->pipe,
|
||||
mcde->bridge);
|
||||
if (ret) {
|
||||
|
|
|
@ -145,8 +145,6 @@ struct meson_dw_hdmi {
|
|||
struct reset_control *hdmitx_apb;
|
||||
struct reset_control *hdmitx_ctrl;
|
||||
struct reset_control *hdmitx_phy;
|
||||
struct clk *hdmi_pclk;
|
||||
struct clk *venci_clk;
|
||||
struct regulator *hdmi_supply;
|
||||
u32 irq_stat;
|
||||
struct dw_hdmi *hdmi;
|
||||
|
@ -946,6 +944,29 @@ static void meson_disable_regulator(void *data)
|
|||
regulator_disable(data);
|
||||
}
|
||||
|
||||
static void meson_disable_clk(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static int meson_enable_clk(struct device *dev, char *name)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = devm_clk_get(dev, name);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "Unable to get %s pclk\n", name);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (!ret)
|
||||
ret = devm_add_action_or_reset(dev, meson_disable_clk, clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
|
@ -1026,19 +1047,17 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
|
|||
if (IS_ERR(meson_dw_hdmi->hdmitx))
|
||||
return PTR_ERR(meson_dw_hdmi->hdmitx);
|
||||
|
||||
meson_dw_hdmi->hdmi_pclk = devm_clk_get(dev, "isfr");
|
||||
if (IS_ERR(meson_dw_hdmi->hdmi_pclk)) {
|
||||
dev_err(dev, "Unable to get HDMI pclk\n");
|
||||
return PTR_ERR(meson_dw_hdmi->hdmi_pclk);
|
||||
}
|
||||
clk_prepare_enable(meson_dw_hdmi->hdmi_pclk);
|
||||
ret = meson_enable_clk(dev, "isfr");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
meson_dw_hdmi->venci_clk = devm_clk_get(dev, "venci");
|
||||
if (IS_ERR(meson_dw_hdmi->venci_clk)) {
|
||||
dev_err(dev, "Unable to get venci clk\n");
|
||||
return PTR_ERR(meson_dw_hdmi->venci_clk);
|
||||
}
|
||||
clk_prepare_enable(meson_dw_hdmi->venci_clk);
|
||||
ret = meson_enable_clk(dev, "iahb");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = meson_enable_clk(dev, "venci");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dw_plat_data->regm = devm_regmap_init(dev, NULL, meson_dw_hdmi,
|
||||
&meson_dw_hdmi_regmap_config);
|
||||
|
@ -1071,6 +1090,8 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
|
|||
|
||||
encoder->possible_crtcs = BIT(0);
|
||||
|
||||
meson_dw_hdmi_init(meson_dw_hdmi);
|
||||
|
||||
DRM_DEBUG_DRIVER("encoder initialized\n");
|
||||
|
||||
/* Bridge / Connector */
|
||||
|
@ -1095,8 +1116,6 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
|
|||
if (IS_ERR(meson_dw_hdmi->hdmi))
|
||||
return PTR_ERR(meson_dw_hdmi->hdmi);
|
||||
|
||||
meson_dw_hdmi_init(meson_dw_hdmi);
|
||||
|
||||
next_bridge = of_drm_find_bridge(pdev->dev.of_node);
|
||||
if (next_bridge)
|
||||
drm_bridge_attach(encoder, next_bridge,
|
||||
|
|
|
@ -37,7 +37,6 @@ static const struct drm_driver mgag200_driver = {
|
|||
.major = DRIVER_MAJOR,
|
||||
.minor = DRIVER_MINOR,
|
||||
.patchlevel = DRIVER_PATCHLEVEL,
|
||||
.gem_create_object = drm_gem_shmem_create_object_cached,
|
||||
DRM_GEM_SHMEM_DRIVER_OPS,
|
||||
};
|
||||
|
||||
|
|
|
@ -211,10 +211,8 @@ int msm_gem_mmap_obj(struct drm_gem_object *obj,
|
|||
* address_space (so unmap_mapping_range does what we want,
|
||||
* in particular in the case of mmap'd dmabufs)
|
||||
*/
|
||||
fput(vma->vm_file);
|
||||
get_file(obj->filp);
|
||||
vma->vm_pgoff = 0;
|
||||
vma->vm_file = obj->filp;
|
||||
vma_set_file(vma, obj->filp);
|
||||
|
||||
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/hdmi.h>
|
||||
#include <linux/component.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
@ -1161,8 +1162,10 @@ nv50_msto_new(struct drm_device *dev, struct nv50_head *head, int id)
|
|||
|
||||
static struct drm_encoder *
|
||||
nv50_mstc_atomic_best_encoder(struct drm_connector *connector,
|
||||
struct drm_connector_state *connector_state)
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
|
||||
connector);
|
||||
struct nv50_mstc *mstc = nv50_mstc(connector);
|
||||
struct drm_crtc *crtc = connector_state->crtc;
|
||||
|
||||
|
|
|
@ -564,9 +564,8 @@ int omap_gem_mmap_obj(struct drm_gem_object *obj,
|
|||
* address_space (so unmap_mapping_range does what we want,
|
||||
* in particular in the case of mmap'd dmabufs)
|
||||
*/
|
||||
fput(vma->vm_file);
|
||||
vma->vm_pgoff = 0;
|
||||
vma->vm_file = get_file(obj->filp);
|
||||
vma_set_file(vma, obj->filp);
|
||||
|
||||
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
/* Manufacturer Command Set */
|
||||
#define MCS_ELVSS_ON 0xb1
|
||||
#define MCS_TEMP_SWIRE 0xb2
|
||||
#define MCS_MIECTL1 0xc0
|
||||
#define MCS_BCMODE 0xc1
|
||||
#define MCS_ERROR_CHECK 0xd5
|
||||
|
@ -35,64 +36,241 @@
|
|||
#define MCS_DISCTL 0xf2
|
||||
#define MCS_SRCCTL 0xf6
|
||||
#define MCS_IFCTL 0xf7
|
||||
#define MCS_PANELCTL 0xF8
|
||||
#define MCS_PANELCTL 0xf8
|
||||
#define MCS_PGAMMACTL 0xfa
|
||||
|
||||
#define S6E63M0_LCD_ID_VALUE_M2 0xA4
|
||||
#define S6E63M0_LCD_ID_VALUE_SM2 0xB4
|
||||
#define S6E63M0_LCD_ID_VALUE_SM2_1 0xB6
|
||||
|
||||
#define NUM_GAMMA_LEVELS 11
|
||||
#define NUM_GAMMA_LEVELS 28
|
||||
#define GAMMA_TABLE_COUNT 23
|
||||
|
||||
#define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
|
||||
|
||||
/* array of gamma tables for gamma value 2.2 */
|
||||
static u8 const s6e63m0_gamma_22[NUM_GAMMA_LEVELS][GAMMA_TABLE_COUNT] = {
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x78, 0xEC, 0x3D, 0xC8,
|
||||
0xC2, 0xB6, 0xC4, 0xC7, 0xB6, 0xD5, 0xD7,
|
||||
0xCC, 0x00, 0x39, 0x00, 0x36, 0x00, 0x51 },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x73, 0x4A, 0x3D, 0xC0,
|
||||
0xC2, 0xB1, 0xBB, 0xBE, 0xAC, 0xCE, 0xCF,
|
||||
0xC5, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x82 },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x70, 0x51, 0x3E, 0xBF,
|
||||
0xC1, 0xAF, 0xB9, 0xBC, 0xAB, 0xCC, 0xCC,
|
||||
0xC2, 0x00, 0x65, 0x00, 0x67, 0x00, 0x8D },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x6C, 0x54, 0x3A, 0xBC,
|
||||
0xBF, 0xAC, 0xB7, 0xBB, 0xA9, 0xC9, 0xC9,
|
||||
0xBE, 0x00, 0x71, 0x00, 0x73, 0x00, 0x9E },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x69, 0x54, 0x37, 0xBB,
|
||||
0xBE, 0xAC, 0xB4, 0xB7, 0xA6, 0xC7, 0xC8,
|
||||
0xBC, 0x00, 0x7B, 0x00, 0x7E, 0x00, 0xAB },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x66, 0x55, 0x34, 0xBA,
|
||||
0xBD, 0xAB, 0xB1, 0xB5, 0xA3, 0xC5, 0xC6,
|
||||
0xB9, 0x00, 0x85, 0x00, 0x88, 0x00, 0xBA },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x63, 0x53, 0x31, 0xB8,
|
||||
0xBC, 0xA9, 0xB0, 0xB5, 0xA2, 0xC4, 0xC4,
|
||||
0xB8, 0x00, 0x8B, 0x00, 0x8E, 0x00, 0xC2 },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x62, 0x54, 0x30, 0xB9,
|
||||
0xBB, 0xA9, 0xB0, 0xB3, 0xA1, 0xC1, 0xC3,
|
||||
0xB7, 0x00, 0x91, 0x00, 0x95, 0x00, 0xDA },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x66, 0x58, 0x34, 0xB6,
|
||||
0xBA, 0xA7, 0xAF, 0xB3, 0xA0, 0xC1, 0xC2,
|
||||
0xB7, 0x00, 0x97, 0x00, 0x9A, 0x00, 0xD1 },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x64, 0x56, 0x33, 0xB6,
|
||||
0xBA, 0xA8, 0xAC, 0xB1, 0x9D, 0xC1, 0xC1,
|
||||
0xB7, 0x00, 0x9C, 0x00, 0x9F, 0x00, 0xD6 },
|
||||
{ MCS_PGAMMACTL, 0x00,
|
||||
0x18, 0x08, 0x24, 0x5f, 0x50, 0x2d, 0xB6,
|
||||
0xB9, 0xA7, 0xAd, 0xB1, 0x9f, 0xbe, 0xC0,
|
||||
0xB5, 0x00, 0xa0, 0x00, 0xa4, 0x00, 0xdb },
|
||||
/* 30 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0xA1, 0x51, 0x7B, 0xCE,
|
||||
0xCB, 0xC2, 0xC7, 0xCB, 0xBC, 0xDA, 0xDD,
|
||||
0xD3, 0x00, 0x53, 0x00, 0x52, 0x00, 0x6F, },
|
||||
/* 40 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x97, 0x58, 0x71, 0xCC,
|
||||
0xCB, 0xC0, 0xC5, 0xC9, 0xBA, 0xD9, 0xDC,
|
||||
0xD1, 0x00, 0x5B, 0x00, 0x5A, 0x00, 0x7A, },
|
||||
/* 50 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x96, 0x58, 0x72, 0xCB,
|
||||
0xCA, 0xBF, 0xC6, 0xC9, 0xBA, 0xD6, 0xD9,
|
||||
0xCD, 0x00, 0x61, 0x00, 0x61, 0x00, 0x83, },
|
||||
/* 60 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x91, 0x5E, 0x6E, 0xC9,
|
||||
0xC9, 0xBD, 0xC4, 0xC9, 0xB8, 0xD3, 0xD7,
|
||||
0xCA, 0x00, 0x69, 0x00, 0x67, 0x00, 0x8D, },
|
||||
/* 70 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x8E, 0x62, 0x6B, 0xC7,
|
||||
0xC9, 0xBB, 0xC3, 0xC7, 0xB7, 0xD3, 0xD7,
|
||||
0xCA, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x94, },
|
||||
/* 80 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x89, 0x68, 0x65, 0xC9,
|
||||
0xC9, 0xBC, 0xC1, 0xC5, 0xB6, 0xD2, 0xD5,
|
||||
0xC9, 0x00, 0x73, 0x00, 0x72, 0x00, 0x9A, },
|
||||
/* 90 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x89, 0x69, 0x64, 0xC7,
|
||||
0xC8, 0xBB, 0xC0, 0xC5, 0xB4, 0xD2, 0xD5,
|
||||
0xC9, 0x00, 0x77, 0x00, 0x76, 0x00, 0xA0, },
|
||||
/* 100 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x86, 0x69, 0x60, 0xC6,
|
||||
0xC8, 0xBA, 0xBF, 0xC4, 0xB4, 0xD0, 0xD4,
|
||||
0xC6, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0xA7, },
|
||||
/* 110 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x86, 0x6A, 0x60, 0xC5,
|
||||
0xC7, 0xBA, 0xBD, 0xC3, 0xB2, 0xD0, 0xD4,
|
||||
0xC5, 0x00, 0x80, 0x00, 0x7E, 0x00, 0xAD, },
|
||||
/* 120 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x82, 0x6B, 0x5E, 0xC4,
|
||||
0xC8, 0xB9, 0xBD, 0xC2, 0xB1, 0xCE, 0xD2,
|
||||
0xC4, 0x00, 0x85, 0x00, 0x82, 0x00, 0xB3, },
|
||||
/* 130 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x8C, 0x6C, 0x60, 0xC3,
|
||||
0xC7, 0xB9, 0xBC, 0xC1, 0xAF, 0xCE, 0xD2,
|
||||
0xC3, 0x00, 0x88, 0x00, 0x86, 0x00, 0xB8, },
|
||||
/* 140 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x80, 0x6C, 0x5F, 0xC1,
|
||||
0xC6, 0xB7, 0xBC, 0xC1, 0xAE, 0xCD, 0xD0,
|
||||
0xC2, 0x00, 0x8C, 0x00, 0x8A, 0x00, 0xBE, },
|
||||
/* 150 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x80, 0x6E, 0x5F, 0xC1,
|
||||
0xC6, 0xB6, 0xBC, 0xC0, 0xAE, 0xCC, 0xD0,
|
||||
0xC2, 0x00, 0x8F, 0x00, 0x8D, 0x00, 0xC2, },
|
||||
/* 160 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x7F, 0x6E, 0x5F, 0xC0,
|
||||
0xC6, 0xB5, 0xBA, 0xBF, 0xAD, 0xCB, 0xCF,
|
||||
0xC0, 0x00, 0x94, 0x00, 0x91, 0x00, 0xC8, },
|
||||
/* 170 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x7C, 0x6D, 0x5C, 0xC0,
|
||||
0xC6, 0xB4, 0xBB, 0xBE, 0xAD, 0xCA, 0xCF,
|
||||
0xC0, 0x00, 0x96, 0x00, 0x94, 0x00, 0xCC, },
|
||||
/* 180 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x7B, 0x6D, 0x5B, 0xC0,
|
||||
0xC5, 0xB3, 0xBA, 0xBE, 0xAD, 0xCA, 0xCE,
|
||||
0xBF, 0x00, 0x99, 0x00, 0x97, 0x00, 0xD0, },
|
||||
/* 190 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x7A, 0x6D, 0x59, 0xC1,
|
||||
0xC5, 0xB4, 0xB8, 0xBD, 0xAC, 0xC9, 0xCE,
|
||||
0xBE, 0x00, 0x9D, 0x00, 0x9A, 0x00, 0xD5, },
|
||||
/* 200 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x79, 0x6D, 0x58, 0xC1,
|
||||
0xC4, 0xB4, 0xB6, 0xBD, 0xAA, 0xCA, 0xCD,
|
||||
0xBE, 0x00, 0x9F, 0x00, 0x9D, 0x00, 0xD9, },
|
||||
/* 210 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x79, 0x6D, 0x57, 0xC0,
|
||||
0xC4, 0xB4, 0xB7, 0xBD, 0xAA, 0xC8, 0xCC,
|
||||
0xBD, 0x00, 0xA2, 0x00, 0xA0, 0x00, 0xDD, },
|
||||
/* 220 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x78, 0x6F, 0x58, 0xBF,
|
||||
0xC4, 0xB3, 0xB5, 0xBB, 0xA9, 0xC8, 0xCC,
|
||||
0xBC, 0x00, 0xA6, 0x00, 0xA3, 0x00, 0xE2, },
|
||||
/* 230 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x75, 0x6F, 0x56, 0xBF,
|
||||
0xC3, 0xB2, 0xB6, 0xBB, 0xA8, 0xC7, 0xCB,
|
||||
0xBC, 0x00, 0xA8, 0x00, 0xA6, 0x00, 0xE6, },
|
||||
/* 240 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x76, 0x6F, 0x56, 0xC0,
|
||||
0xC3, 0xB2, 0xB5, 0xBA, 0xA8, 0xC6, 0xCB,
|
||||
0xBB, 0x00, 0xAA, 0x00, 0xA8, 0x00, 0xE9, },
|
||||
/* 250 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x74, 0x6D, 0x54, 0xBF,
|
||||
0xC3, 0xB2, 0xB4, 0xBA, 0xA7, 0xC6, 0xCA,
|
||||
0xBA, 0x00, 0xAD, 0x00, 0xAB, 0x00, 0xED, },
|
||||
/* 260 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x74, 0x6E, 0x54, 0xBD,
|
||||
0xC2, 0xB0, 0xB5, 0xBA, 0xA7, 0xC5, 0xC9,
|
||||
0xBA, 0x00, 0xB0, 0x00, 0xAE, 0x00, 0xF1, },
|
||||
/* 270 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x71, 0x6C, 0x50, 0xBD,
|
||||
0xC3, 0xB0, 0xB4, 0xB8, 0xA6, 0xC6, 0xC9,
|
||||
0xBB, 0x00, 0xB2, 0x00, 0xB1, 0x00, 0xF4, },
|
||||
/* 280 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x6E, 0x6C, 0x4D, 0xBE,
|
||||
0xC3, 0xB1, 0xB3, 0xB8, 0xA5, 0xC6, 0xC8,
|
||||
0xBB, 0x00, 0xB4, 0x00, 0xB3, 0x00, 0xF7, },
|
||||
/* 290 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x71, 0x70, 0x50, 0xBD,
|
||||
0xC1, 0xB0, 0xB2, 0xB8, 0xA4, 0xC6, 0xC7,
|
||||
0xBB, 0x00, 0xB6, 0x00, 0xB6, 0x00, 0xFA, },
|
||||
/* 300 cd */
|
||||
{ MCS_PGAMMACTL, 0x02,
|
||||
0x18, 0x08, 0x24, 0x70, 0x6E, 0x4E, 0xBC,
|
||||
0xC0, 0xAF, 0xB3, 0xB8, 0xA5, 0xC5, 0xC7,
|
||||
0xBB, 0x00, 0xB9, 0x00, 0xB8, 0x00, 0xFC, },
|
||||
};
|
||||
|
||||
#define NUM_ACL_LEVELS 7
|
||||
#define ACL_TABLE_COUNT 28
|
||||
|
||||
static u8 const s6e63m0_acl[NUM_ACL_LEVELS][ACL_TABLE_COUNT] = {
|
||||
/* NULL ACL */
|
||||
{ MCS_BCMODE,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00 },
|
||||
/* 40P ACL */
|
||||
{ MCS_BCMODE,
|
||||
0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
|
||||
0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x06, 0x0C, 0x11, 0x16, 0x1C, 0x21, 0x26,
|
||||
0x2B, 0x31, 0x36 },
|
||||
/* 43P ACL */
|
||||
{ MCS_BCMODE,
|
||||
0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
|
||||
0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x07, 0x0C, 0x12, 0x18, 0x1E, 0x23, 0x29,
|
||||
0x2F, 0x34, 0x3A },
|
||||
/* 45P ACL */
|
||||
{ MCS_BCMODE,
|
||||
0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
|
||||
0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x07, 0x0D, 0x13, 0x19, 0x1F, 0x25, 0x2B,
|
||||
0x31, 0x37, 0x3D },
|
||||
/* 47P ACL */
|
||||
{ MCS_BCMODE,
|
||||
0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
|
||||
0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x07, 0x0E, 0x14, 0x1B, 0x21, 0x27, 0x2E,
|
||||
0x34, 0x3B, 0x41 },
|
||||
/* 48P ACL */
|
||||
{ MCS_BCMODE,
|
||||
0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
|
||||
0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x08, 0x0E, 0x15, 0x1B, 0x22, 0x29, 0x2F,
|
||||
0x36, 0x3C, 0x43 },
|
||||
/* 50P ACL */
|
||||
{ MCS_BCMODE,
|
||||
0x4D, 0x96, 0x1D, 0x00, 0x00, 0x01, 0xDF, 0x00,
|
||||
0x00, 0x03, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x08, 0x0F, 0x16, 0x1D, 0x24, 0x2A, 0x31,
|
||||
0x38, 0x3F, 0x46 },
|
||||
};
|
||||
|
||||
/* This tells us which ACL level goes with which gamma */
|
||||
static u8 const s6e63m0_acl_per_gamma[NUM_GAMMA_LEVELS] = {
|
||||
/* 30 - 60 cd: ACL off/NULL */
|
||||
0, 0, 0, 0,
|
||||
/* 70 - 250 cd: 40P ACL */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* 260 - 300 cd: 50P ACL */
|
||||
6, 6, 6, 6, 6,
|
||||
};
|
||||
|
||||
/* The ELVSS backlight regulator has 5 levels */
|
||||
#define S6E63M0_ELVSS_LEVELS 5
|
||||
|
||||
static u8 const s6e63m0_elvss_offsets[S6E63M0_ELVSS_LEVELS] = {
|
||||
0x00, /* not set */
|
||||
0x0D, /* 30 cd - 100 cd */
|
||||
0x09, /* 110 cd - 160 cd */
|
||||
0x07, /* 170 cd - 200 cd */
|
||||
0x00, /* 210 cd - 300 cd */
|
||||
};
|
||||
|
||||
/* This tells us which ELVSS level goes with which gamma */
|
||||
static u8 const s6e63m0_elvss_per_gamma[NUM_GAMMA_LEVELS] = {
|
||||
/* 30 - 100 cd */
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* 110 - 160 cd */
|
||||
2, 2, 2, 2, 2, 2,
|
||||
/* 170 - 200 cd */
|
||||
3, 3, 3, 3,
|
||||
/* 210 - 300 cd */
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
};
|
||||
|
||||
struct s6e63m0 {
|
||||
|
@ -102,6 +280,7 @@ struct s6e63m0 {
|
|||
struct drm_panel panel;
|
||||
struct backlight_device *bl_dev;
|
||||
u8 lcd_type;
|
||||
u8 elvss_pulse;
|
||||
|
||||
struct regulator_bulk_data supplies[2];
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
@ -187,17 +366,25 @@ static int s6e63m0_check_lcd_type(struct s6e63m0 *ctx)
|
|||
|
||||
dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3);
|
||||
|
||||
/* We attempt to detect what panel is mounted on the controller */
|
||||
/*
|
||||
* We attempt to detect what panel is mounted on the controller.
|
||||
* The third ID byte represents the desired ELVSS pulse for
|
||||
* some displays.
|
||||
*/
|
||||
switch (id2) {
|
||||
case S6E63M0_LCD_ID_VALUE_M2:
|
||||
dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI M2\n");
|
||||
ctx->elvss_pulse = id3;
|
||||
break;
|
||||
case S6E63M0_LCD_ID_VALUE_SM2:
|
||||
case S6E63M0_LCD_ID_VALUE_SM2_1:
|
||||
dev_info(ctx->dev, "detected LCD panel AMS397GE MIPI SM2\n");
|
||||
ctx->elvss_pulse = id3;
|
||||
break;
|
||||
default:
|
||||
dev_info(ctx->dev, "unknown LCD panel type %02x\n", id2);
|
||||
/* Default ELVSS pulse level */
|
||||
ctx->elvss_pulse = 0x16;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -210,7 +397,7 @@ static void s6e63m0_init(struct s6e63m0 *ctx)
|
|||
{
|
||||
s6e63m0_dcs_write_seq_static(ctx, MCS_PANELCTL,
|
||||
0x01, 0x27, 0x27, 0x07, 0x07, 0x54, 0x9f,
|
||||
0x63, 0x86, 0x1a, 0x33, 0x0d, 0x00, 0x00);
|
||||
0x63, 0x8f, 0x1a, 0x33, 0x0d, 0x00, 0x00);
|
||||
|
||||
s6e63m0_dcs_write_seq_static(ctx, MCS_DISCTL,
|
||||
0x02, 0x03, 0x1c, 0x10, 0x10);
|
||||
|
@ -226,9 +413,8 @@ static void s6e63m0_init(struct s6e63m0 *ctx)
|
|||
0x01);
|
||||
|
||||
s6e63m0_dcs_write_seq_static(ctx, MCS_SRCCTL,
|
||||
0x00, 0x8c, 0x07);
|
||||
s6e63m0_dcs_write_seq_static(ctx, 0xb3,
|
||||
0xc);
|
||||
0x00, 0x8e, 0x07);
|
||||
s6e63m0_dcs_write_seq_static(ctx, 0xb3, 0x6c);
|
||||
|
||||
s6e63m0_dcs_write_seq_static(ctx, 0xb5,
|
||||
0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
|
||||
|
@ -247,9 +433,12 @@ static void s6e63m0_init(struct s6e63m0 *ctx)
|
|||
0x13, 0x1f, 0x1a, 0x2a, 0x24, 0x1f, 0x1b,
|
||||
0x1a, 0x17, 0x2b, 0x26, 0x22, 0x20, 0x3a,
|
||||
0x34, 0x30, 0x2c, 0x29, 0x26, 0x25, 0x23,
|
||||
0x21, 0x20, 0x1e, 0x1e, 0x00, 0x00, 0x11,
|
||||
0x22, 0x33, 0x44, 0x44, 0x44, 0x55, 0x55,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66);
|
||||
0x21, 0x20, 0x1e, 0x1e);
|
||||
|
||||
s6e63m0_dcs_write_seq_static(ctx, 0xb8,
|
||||
0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x44,
|
||||
0x44, 0x55, 0x55, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66);
|
||||
|
||||
s6e63m0_dcs_write_seq_static(ctx, 0xb9,
|
||||
0x2c, 0x12, 0x0c, 0x0a, 0x10, 0x0e, 0x17,
|
||||
|
@ -269,7 +458,7 @@ static void s6e63m0_init(struct s6e63m0 *ctx)
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06,
|
||||
0x09, 0x0d, 0x0f, 0x12, 0x15, 0x18);
|
||||
|
||||
s6e63m0_dcs_write_seq_static(ctx, 0xb2,
|
||||
s6e63m0_dcs_write_seq_static(ctx, MCS_TEMP_SWIRE,
|
||||
0x10, 0x10, 0x0b, 0x05);
|
||||
|
||||
s6e63m0_dcs_write_seq_static(ctx, MCS_MIECTL1,
|
||||
|
@ -447,15 +636,33 @@ static const struct drm_panel_funcs s6e63m0_drm_funcs = {
|
|||
static int s6e63m0_set_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct s6e63m0 *ctx = bl_get_data(bd);
|
||||
|
||||
int brightness = bd->props.brightness;
|
||||
u8 elvss_val;
|
||||
u8 elvss_cmd_set[5];
|
||||
int i;
|
||||
|
||||
/* disable and set new gamma */
|
||||
/* Adjust ELVSS to candela level */
|
||||
i = s6e63m0_elvss_per_gamma[brightness];
|
||||
elvss_val = ctx->elvss_pulse + s6e63m0_elvss_offsets[i];
|
||||
if (elvss_val > 0x1f)
|
||||
elvss_val = 0x1f;
|
||||
elvss_cmd_set[0] = MCS_TEMP_SWIRE;
|
||||
elvss_cmd_set[1] = elvss_val;
|
||||
elvss_cmd_set[2] = elvss_val;
|
||||
elvss_cmd_set[3] = elvss_val;
|
||||
elvss_cmd_set[4] = elvss_val;
|
||||
s6e63m0_dcs_write(ctx, elvss_cmd_set, 5);
|
||||
|
||||
/* Update the ACL per gamma value */
|
||||
i = s6e63m0_acl_per_gamma[brightness];
|
||||
s6e63m0_dcs_write(ctx, s6e63m0_acl[i],
|
||||
ARRAY_SIZE(s6e63m0_acl[i]));
|
||||
|
||||
/* Update gamma table */
|
||||
s6e63m0_dcs_write(ctx, s6e63m0_gamma_22[brightness],
|
||||
ARRAY_SIZE(s6e63m0_gamma_22[brightness]));
|
||||
s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x03);
|
||||
|
||||
/* update gamma table. */
|
||||
s6e63m0_dcs_write_seq_static(ctx, MCS_PGAMMACTL, 0x01);
|
||||
|
||||
return s6e63m0_clear_error(ctx);
|
||||
}
|
||||
|
|
|
@ -2267,6 +2267,31 @@ static const struct panel_desc innolux_n116bge = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct drm_display_mode innolux_n125hce_gn1_mode = {
|
||||
.clock = 162000,
|
||||
.hdisplay = 1920,
|
||||
.hsync_start = 1920 + 40,
|
||||
.hsync_end = 1920 + 40 + 40,
|
||||
.htotal = 1920 + 40 + 40 + 80,
|
||||
.vdisplay = 1080,
|
||||
.vsync_start = 1080 + 4,
|
||||
.vsync_end = 1080 + 4 + 4,
|
||||
.vtotal = 1080 + 4 + 4 + 24,
|
||||
};
|
||||
|
||||
static const struct panel_desc innolux_n125hce_gn1 = {
|
||||
.modes = &innolux_n125hce_gn1_mode,
|
||||
.num_modes = 1,
|
||||
.bpc = 8,
|
||||
.size = {
|
||||
.width = 276,
|
||||
.height = 155,
|
||||
},
|
||||
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.bus_flags = DRM_BUS_FLAG_DATA_MSB_TO_LSB,
|
||||
.connector_type = DRM_MODE_CONNECTOR_eDP,
|
||||
};
|
||||
|
||||
static const struct drm_display_mode innolux_n156bge_l21_mode = {
|
||||
.clock = 69300,
|
||||
.hdisplay = 1366,
|
||||
|
@ -4122,6 +4147,9 @@ static const struct of_device_id platform_of_match[] = {
|
|||
}, {
|
||||
.compatible = "innolux,n116bge",
|
||||
.data = &innolux_n116bge,
|
||||
}, {
|
||||
.compatible = "innolux,n125hce-gn1",
|
||||
.data = &innolux_n125hce_gn1,
|
||||
}, {
|
||||
.compatible = "innolux,n156bge-l21",
|
||||
.data = &innolux_n156bge_l21,
|
||||
|
|
|
@ -228,7 +228,7 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t
|
|||
INIT_LIST_HEAD(&obj->mappings.list);
|
||||
mutex_init(&obj->mappings.lock);
|
||||
obj->base.base.funcs = &panfrost_gem_funcs;
|
||||
obj->base.map_cached = pfdev->coherent;
|
||||
obj->base.map_wc = !pfdev->coherent;
|
||||
|
||||
return &obj->base.base;
|
||||
}
|
||||
|
|
|
@ -63,6 +63,9 @@ static atomic_long_t allocated_pages;
|
|||
static struct ttm_pool_type global_write_combined[MAX_ORDER];
|
||||
static struct ttm_pool_type global_uncached[MAX_ORDER];
|
||||
|
||||
static struct ttm_pool_type global_dma32_write_combined[MAX_ORDER];
|
||||
static struct ttm_pool_type global_dma32_uncached[MAX_ORDER];
|
||||
|
||||
static spinlock_t shrinker_lock;
|
||||
static struct list_head shrinker_list;
|
||||
static struct shrinker mm_shrinker;
|
||||
|
@ -290,8 +293,14 @@ static struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool,
|
|||
#ifdef CONFIG_X86
|
||||
switch (caching) {
|
||||
case ttm_write_combined:
|
||||
if (pool->use_dma32)
|
||||
return &global_dma32_write_combined[order];
|
||||
|
||||
return &global_write_combined[order];
|
||||
case ttm_uncached:
|
||||
if (pool->use_dma32)
|
||||
return &global_dma32_uncached[order];
|
||||
|
||||
return &global_uncached[order];
|
||||
default:
|
||||
break;
|
||||
|
@ -570,6 +579,11 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m)
|
|||
seq_puts(m, "uc\t:");
|
||||
ttm_pool_debugfs_orders(global_uncached, m);
|
||||
|
||||
seq_puts(m, "wc 32\t:");
|
||||
ttm_pool_debugfs_orders(global_dma32_write_combined, m);
|
||||
seq_puts(m, "uc 32\t:");
|
||||
ttm_pool_debugfs_orders(global_dma32_uncached, m);
|
||||
|
||||
for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
|
||||
seq_puts(m, "DMA ");
|
||||
switch (i) {
|
||||
|
@ -640,6 +654,11 @@ int ttm_pool_mgr_init(unsigned long num_pages)
|
|||
ttm_pool_type_init(&global_write_combined[i], NULL,
|
||||
ttm_write_combined, i);
|
||||
ttm_pool_type_init(&global_uncached[i], NULL, ttm_uncached, i);
|
||||
|
||||
ttm_pool_type_init(&global_dma32_write_combined[i], NULL,
|
||||
ttm_write_combined, i);
|
||||
ttm_pool_type_init(&global_dma32_uncached[i], NULL,
|
||||
ttm_uncached, i);
|
||||
}
|
||||
|
||||
mm_shrinker.count_objects = ttm_pool_shrinker_count;
|
||||
|
@ -660,6 +679,9 @@ void ttm_pool_mgr_fini(void)
|
|||
for (i = 0; i < MAX_ORDER; ++i) {
|
||||
ttm_pool_type_fini(&global_write_combined[i]);
|
||||
ttm_pool_type_fini(&global_uncached[i]);
|
||||
|
||||
ttm_pool_type_fini(&global_dma32_write_combined[i]);
|
||||
ttm_pool_type_fini(&global_dma32_uncached[i]);
|
||||
}
|
||||
|
||||
unregister_shrinker(&mm_shrinker);
|
||||
|
|
|
@ -38,8 +38,6 @@ static const struct drm_driver driver = {
|
|||
.driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
|
||||
|
||||
/* GEM hooks */
|
||||
.gem_create_object = drm_gem_shmem_create_object_cached,
|
||||
|
||||
.fops = &udl_driver_fops,
|
||||
DRM_GEM_SHMEM_DRIVER_OPS,
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size)
|
|||
obj = &bo->base.base;
|
||||
|
||||
obj->funcs = &v3d_gem_funcs;
|
||||
|
||||
bo->base.map_wc = true;
|
||||
INIT_LIST_HEAD(&bo->unref_head);
|
||||
|
||||
return &bo->base.base;
|
||||
|
|
|
@ -273,8 +273,10 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn,
|
|||
}
|
||||
|
||||
static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
|
||||
struct drm_connector_state *conn_state)
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
|
||||
conn);
|
||||
struct vc4_txp *txp = connector_to_vc4_txp(conn);
|
||||
struct drm_gem_cma_object *gem;
|
||||
struct drm_display_mode *mode;
|
||||
|
|
|
@ -403,8 +403,7 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
fput(vma->vm_file);
|
||||
vma->vm_file = get_file(obj->filp);
|
||||
vma_set_file(vma, obj->filp);
|
||||
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
|
||||
vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
|
||||
|
||||
|
|
|
@ -364,6 +364,7 @@ int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv)
|
|||
irqwait->request.sequence +=
|
||||
atomic_read(&cur_irq->irq_received);
|
||||
irqwait->request.type &= ~_DRM_VBLANK_RELATIVE;
|
||||
break;
|
||||
case VIA_IRQ_ABSOLUTE:
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -1001,8 +1001,8 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size,
|
|||
state = via_check_vheader6(&buf, buf_end);
|
||||
break;
|
||||
case state_command:
|
||||
if ((HALCYON_HEADER2 == (cmd = *buf)) &&
|
||||
supported_3d)
|
||||
cmd = *buf;
|
||||
if ((cmd == HALCYON_HEADER2) && supported_3d)
|
||||
state = state_header2;
|
||||
else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1)
|
||||
state = state_header1;
|
||||
|
@ -1064,7 +1064,8 @@ via_parse_command_stream(struct drm_device *dev, const uint32_t *buf,
|
|||
state = via_parse_vheader6(dev_priv, &buf, buf_end);
|
||||
break;
|
||||
case state_command:
|
||||
if (HALCYON_HEADER2 == (cmd = *buf))
|
||||
cmd = *buf;
|
||||
if (cmd == HALCYON_HEADER2)
|
||||
state = state_header2;
|
||||
else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1)
|
||||
state = state_header1;
|
||||
|
|
|
@ -67,8 +67,8 @@ virtio_gpu_debugfs_irq_info(struct seq_file *m, void *data)
|
|||
struct virtio_gpu_device *vgdev = node->minor->dev->dev_private;
|
||||
|
||||
seq_printf(m, "fence %llu %lld\n",
|
||||
(u64)atomic64_read(&vgdev->fence_drv.last_seq),
|
||||
vgdev->fence_drv.sync_seq);
|
||||
(u64)atomic64_read(&vgdev->fence_drv.last_fence_id),
|
||||
vgdev->fence_drv.current_fence_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,8 +127,8 @@ typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev,
|
|||
struct virtio_gpu_vbuffer *vbuf);
|
||||
|
||||
struct virtio_gpu_fence_driver {
|
||||
atomic64_t last_seq;
|
||||
uint64_t sync_seq;
|
||||
atomic64_t last_fence_id;
|
||||
uint64_t current_fence_id;
|
||||
uint64_t context;
|
||||
struct list_head fences;
|
||||
spinlock_t lock;
|
||||
|
@ -257,7 +257,7 @@ struct virtio_gpu_fpriv {
|
|||
struct mutex context_lock;
|
||||
};
|
||||
|
||||
/* virtio_ioctl.c */
|
||||
/* virtgpu_ioctl.c */
|
||||
#define DRM_VIRTIO_NUM_IOCTLS 11
|
||||
extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS];
|
||||
void virtio_gpu_create_context(struct drm_device *dev, struct drm_file *file);
|
||||
|
@ -420,7 +420,7 @@ void virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
|
|||
struct virtio_gpu_ctrl_hdr *cmd_hdr,
|
||||
struct virtio_gpu_fence *fence);
|
||||
void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
|
||||
u64 last_seq);
|
||||
u64 fence_id);
|
||||
|
||||
/* virtgpu_object.c */
|
||||
void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo);
|
||||
|
|
|
@ -48,7 +48,7 @@ static bool virtio_fence_signaled(struct dma_fence *f)
|
|||
/* leaked fence outside driver before completing
|
||||
* initialization with virtio_gpu_fence_emit */
|
||||
return false;
|
||||
if (atomic64_read(&fence->drv->last_seq) >= fence->f.seqno)
|
||||
if (atomic64_read(&fence->drv->last_fence_id) >= fence->f.seqno)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -62,7 +62,8 @@ static void virtio_timeline_value_str(struct dma_fence *f, char *str, int size)
|
|||
{
|
||||
struct virtio_gpu_fence *fence = to_virtio_fence(f);
|
||||
|
||||
snprintf(str, size, "%llu", (u64)atomic64_read(&fence->drv->last_seq));
|
||||
snprintf(str, size, "%llu",
|
||||
(u64)atomic64_read(&fence->drv->last_fence_id));
|
||||
}
|
||||
|
||||
static const struct dma_fence_ops virtio_fence_ops = {
|
||||
|
@ -100,7 +101,7 @@ void virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
|
|||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&drv->lock, irq_flags);
|
||||
fence->f.seqno = ++drv->sync_seq;
|
||||
fence->f.seqno = ++drv->current_fence_id;
|
||||
dma_fence_get(&fence->f);
|
||||
list_add_tail(&fence->node, &drv->fences);
|
||||
spin_unlock_irqrestore(&drv->lock, irq_flags);
|
||||
|
@ -112,16 +113,16 @@ void virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
|
|||
}
|
||||
|
||||
void virtio_gpu_fence_event_process(struct virtio_gpu_device *vgdev,
|
||||
u64 last_seq)
|
||||
u64 fence_id)
|
||||
{
|
||||
struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv;
|
||||
struct virtio_gpu_fence *fence, *tmp;
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_irqsave(&drv->lock, irq_flags);
|
||||
atomic64_set(&vgdev->fence_drv.last_seq, last_seq);
|
||||
atomic64_set(&vgdev->fence_drv.last_fence_id, fence_id);
|
||||
list_for_each_entry_safe(fence, tmp, &drv->fences, node) {
|
||||
if (last_seq < fence->f.seqno)
|
||||
if (fence_id < fence->f.seqno)
|
||||
continue;
|
||||
dma_fence_signal_locked(&fence->f);
|
||||
list_del(&fence->node);
|
||||
|
|
|
@ -591,8 +591,9 @@ static int verify_blob(struct virtio_gpu_device *vgdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_gpu_resource_create_blob(struct drm_device *dev,
|
||||
void *data, struct drm_file *file)
|
||||
static int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev,
|
||||
void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t handle = 0;
|
||||
|
@ -696,6 +697,6 @@ struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS] = {
|
|||
DRM_RENDER_ALLOW),
|
||||
|
||||
DRM_IOCTL_DEF_DRV(VIRTGPU_RESOURCE_CREATE_BLOB,
|
||||
virtio_gpu_resource_create_blob,
|
||||
virtio_gpu_resource_create_blob_ioctl,
|
||||
DRM_RENDER_ALLOW),
|
||||
};
|
||||
|
|
|
@ -144,7 +144,6 @@ struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
|
|||
|
||||
dshmem = &shmem->base.base;
|
||||
dshmem->base.funcs = &virtio_gpu_shmem_funcs;
|
||||
dshmem->map_cached = true;
|
||||
return &dshmem->base;
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,6 @@ static const struct drm_driver vkms_driver = {
|
|||
.driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
|
||||
.release = vkms_release,
|
||||
.fops = &vkms_driver_fops,
|
||||
.gem_create_object = drm_gem_shmem_create_object_cached,
|
||||
DRM_GEM_SHMEM_DRIVER_OPS,
|
||||
|
||||
.name = DRIVER_NAME,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <linux/dma-buf-map.h>
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_writeback.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
@ -105,8 +106,10 @@ static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
|
|||
}
|
||||
|
||||
static void vkms_wb_atomic_commit(struct drm_connector *conn,
|
||||
struct drm_connector_state *state)
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
|
||||
conn);
|
||||
struct vkms_device *vkmsdev = drm_device_to_vkms_device(conn->dev);
|
||||
struct vkms_output *output = &vkmsdev->output;
|
||||
struct drm_writeback_connector *wb_conn = &output->wb_connector;
|
||||
|
@ -122,7 +125,7 @@ static void vkms_wb_atomic_commit(struct drm_connector *conn,
|
|||
crtc_state->active_writeback = conn_state->writeback_job->priv;
|
||||
crtc_state->wb_pending = true;
|
||||
spin_unlock_irq(&output->composer_lock);
|
||||
drm_writeback_queue_job(wb_conn, state);
|
||||
drm_writeback_queue_job(wb_conn, connector_state);
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = {
|
||||
|
|
|
@ -450,9 +450,9 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)
|
|||
vma_set_anonymous(vma);
|
||||
}
|
||||
|
||||
if (vma->vm_file)
|
||||
fput(vma->vm_file);
|
||||
vma->vm_file = asma->file;
|
||||
vma_set_file(vma, asma->file);
|
||||
/* XXX: merge this with the get_file() above if possible */
|
||||
fput(asma->file);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ashmem_mutex);
|
||||
|
|
|
@ -682,6 +682,7 @@ static void lx_restore_display_ctlr(struct lxfb_par *par)
|
|||
case DC_DV_CTL:
|
||||
/* set all ram to dirty */
|
||||
write_dc(par, i, par->dc[i] | DC_DV_CTL_CLEAR_DV_RAM);
|
||||
break;
|
||||
|
||||
case DC_RSVD_1:
|
||||
case DC_RSVD_2:
|
||||
|
|
|
@ -239,6 +239,7 @@ static u32 to3264(u32 timing, int bpp, int is64)
|
|||
fallthrough;
|
||||
case 16:
|
||||
timing >>= 1;
|
||||
fallthrough;
|
||||
case 32:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -100,10 +100,10 @@ struct drm_fb_helper_funcs {
|
|||
* @funcs: driver callbacks for fb helper
|
||||
* @fbdev: emulated fbdev device info struct
|
||||
* @pseudo_palette: fake palette of 16 colors
|
||||
* @dirty_clip: clip rectangle used with deferred_io to accumulate damage to
|
||||
* @damage_clip: clip rectangle used with deferred_io to accumulate damage to
|
||||
* the screen buffer
|
||||
* @dirty_lock: spinlock protecting @dirty_clip
|
||||
* @dirty_work: worker used to flush the framebuffer
|
||||
* @damage_lock: spinlock protecting @damage_clip
|
||||
* @damage_work: worker used to flush the framebuffer
|
||||
* @resume_work: worker used during resume if the console lock is already taken
|
||||
*
|
||||
* This is the main structure used by the fbdev helpers. Drivers supporting
|
||||
|
@ -131,9 +131,9 @@ struct drm_fb_helper {
|
|||
const struct drm_fb_helper_funcs *funcs;
|
||||
struct fb_info *fbdev;
|
||||
u32 pseudo_palette[17];
|
||||
struct drm_clip_rect dirty_clip;
|
||||
spinlock_t dirty_lock;
|
||||
struct work_struct dirty_work;
|
||||
struct drm_clip_rect damage_clip;
|
||||
spinlock_t damage_lock;
|
||||
struct work_struct damage_work;
|
||||
struct work_struct resume_work;
|
||||
|
||||
/**
|
||||
|
|
|
@ -98,9 +98,9 @@ struct drm_gem_shmem_object {
|
|||
unsigned int vmap_use_count;
|
||||
|
||||
/**
|
||||
* @map_cached: map object cached (instead of using writecombine).
|
||||
* @map_wc: map object write-combined (instead of using shmem defaults).
|
||||
*/
|
||||
bool map_cached;
|
||||
bool map_wc;
|
||||
};
|
||||
|
||||
#define to_drm_gem_shmem_obj(obj) \
|
||||
|
@ -133,9 +133,6 @@ drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
|
|||
struct drm_device *dev, size_t size,
|
||||
uint32_t *handle);
|
||||
|
||||
struct drm_gem_object *
|
||||
drm_gem_shmem_create_object_cached(struct drm_device *dev, size_t size);
|
||||
|
||||
int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
|
||||
|
|
|
@ -195,6 +195,9 @@ enum drm_mode_status {
|
|||
* @crtc_vsync_end: hardware mode vertical sync end
|
||||
* @crtc_vtotal: hardware mode vertical total size
|
||||
*
|
||||
* This is the kernel API display mode information structure. For the
|
||||
* user-space version see struct drm_mode_modeinfo.
|
||||
*
|
||||
* The horizontal and vertical timings are defined per the following diagram.
|
||||
*
|
||||
* ::
|
||||
|
|
|
@ -1044,9 +1044,8 @@ struct drm_connector_helper_funcs {
|
|||
* NOTE:
|
||||
*
|
||||
* This function is called in the check phase of an atomic update. The
|
||||
* driver is not allowed to change anything outside of the free-standing
|
||||
* state objects passed-in or assembled in the overall &drm_atomic_state
|
||||
* update tracking structure.
|
||||
* driver is not allowed to change anything outside of the
|
||||
* &drm_atomic_state update tracking structure passed in.
|
||||
*
|
||||
* RETURNS:
|
||||
*
|
||||
|
@ -1056,7 +1055,7 @@ struct drm_connector_helper_funcs {
|
|||
* for this.
|
||||
*/
|
||||
struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector,
|
||||
struct drm_connector_state *connector_state);
|
||||
struct drm_atomic_state *state);
|
||||
|
||||
/**
|
||||
* @atomic_check:
|
||||
|
@ -1097,15 +1096,15 @@ struct drm_connector_helper_funcs {
|
|||
*
|
||||
* This hook is to be used by drivers implementing writeback connectors
|
||||
* that need a point when to commit the writeback job to the hardware.
|
||||
* The writeback_job to commit is available in
|
||||
* &drm_connector_state.writeback_job.
|
||||
* The writeback_job to commit is available in the new connector state,
|
||||
* in &drm_connector_state.writeback_job.
|
||||
*
|
||||
* This hook is optional.
|
||||
*
|
||||
* This callback is used by the atomic modeset helpers.
|
||||
*/
|
||||
void (*atomic_commit)(struct drm_connector *connector,
|
||||
struct drm_connector_state *state);
|
||||
struct drm_atomic_state *state);
|
||||
|
||||
/**
|
||||
* @prepare_writeback_job:
|
||||
|
|
|
@ -2719,6 +2719,8 @@ static inline void vma_set_page_prot(struct vm_area_struct *vma)
|
|||
}
|
||||
#endif
|
||||
|
||||
void vma_set_file(struct vm_area_struct *vma, struct file *file);
|
||||
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
unsigned long change_prot_numa(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end);
|
||||
|
|
|
@ -218,6 +218,27 @@ extern "C" {
|
|||
#define DRM_MODE_CONTENT_PROTECTION_DESIRED 1
|
||||
#define DRM_MODE_CONTENT_PROTECTION_ENABLED 2
|
||||
|
||||
/**
|
||||
* struct drm_mode_modeinfo - Display mode information.
|
||||
* @clock: pixel clock in kHz
|
||||
* @hdisplay: horizontal display size
|
||||
* @hsync_start: horizontal sync start
|
||||
* @hsync_end: horizontal sync end
|
||||
* @htotal: horizontal total size
|
||||
* @hskew: horizontal skew
|
||||
* @vdisplay: vertical display size
|
||||
* @vsync_start: vertical sync start
|
||||
* @vsync_end: vertical sync end
|
||||
* @vtotal: vertical total size
|
||||
* @vscan: vertical scan
|
||||
* @vrefresh: approximate vertical refresh rate in Hz
|
||||
* @flags: bitmask of misc. flags, see DRM_MODE_FLAG_* defines
|
||||
* @type: bitmask of type flags, see DRM_MODE_TYPE_* defines
|
||||
* @name: string describing the mode resolution
|
||||
*
|
||||
* This is the user-space API display mode information structure. For the
|
||||
* kernel version see struct drm_display_mode.
|
||||
*/
|
||||
struct drm_mode_modeinfo {
|
||||
__u32 clock;
|
||||
__u16 hdisplay;
|
||||
|
@ -368,27 +389,95 @@ enum drm_mode_subconnector {
|
|||
#define DRM_MODE_CONNECTOR_WRITEBACK 18
|
||||
#define DRM_MODE_CONNECTOR_SPI 19
|
||||
|
||||
/**
|
||||
* struct drm_mode_get_connector - Get connector metadata.
|
||||
*
|
||||
* User-space can perform a GETCONNECTOR ioctl to retrieve information about a
|
||||
* connector. User-space is expected to retrieve encoders, modes and properties
|
||||
* by performing this ioctl at least twice: the first time to retrieve the
|
||||
* number of elements, the second time to retrieve the elements themselves.
|
||||
*
|
||||
* To retrieve the number of elements, set @count_props and @count_encoders to
|
||||
* zero, set @count_modes to 1, and set @modes_ptr to a temporary struct
|
||||
* drm_mode_modeinfo element.
|
||||
*
|
||||
* To retrieve the elements, allocate arrays for @encoders_ptr, @modes_ptr,
|
||||
* @props_ptr and @prop_values_ptr, then set @count_modes, @count_props and
|
||||
* @count_encoders to their capacity.
|
||||
*
|
||||
* Performing the ioctl only twice may be racy: the number of elements may have
|
||||
* changed with a hotplug event in-between the two ioctls. User-space is
|
||||
* expected to retry the last ioctl until the number of elements stabilizes.
|
||||
* The kernel won't fill any array which doesn't have the expected length.
|
||||
*
|
||||
* **Force-probing a connector**
|
||||
*
|
||||
* If the @count_modes field is set to zero, the kernel will perform a forced
|
||||
* probe on the connector to refresh the connector status, modes and EDID.
|
||||
* A forced-probe can be slow and the ioctl will block. A force-probe can cause
|
||||
* flickering and temporary freezes, so it should not be performed
|
||||
* automatically.
|
||||
*
|
||||
* User-space shouldn't need to force-probe connectors in general: the kernel
|
||||
* will automatically take care of probing connectors that don't support
|
||||
* hot-plug detection when appropriate. However, user-space may force-probe
|
||||
* connectors on user request (e.g. clicking a "Scan connectors" button, or
|
||||
* opening a UI to manage screens).
|
||||
*/
|
||||
struct drm_mode_get_connector {
|
||||
|
||||
/** @encoders_ptr: Pointer to ``__u32`` array of object IDs. */
|
||||
__u64 encoders_ptr;
|
||||
/** @modes_ptr: Pointer to struct drm_mode_modeinfo array. */
|
||||
__u64 modes_ptr;
|
||||
/** @props_ptr: Pointer to ``__u32`` array of property IDs. */
|
||||
__u64 props_ptr;
|
||||
/** @prop_values_ptr: Pointer to ``__u64`` array of property values. */
|
||||
__u64 prop_values_ptr;
|
||||
|
||||
/** @count_modes: Number of modes. */
|
||||
__u32 count_modes;
|
||||
/** @count_props: Number of properties. */
|
||||
__u32 count_props;
|
||||
/** @count_encoders: Number of encoders. */
|
||||
__u32 count_encoders;
|
||||
|
||||
__u32 encoder_id; /**< Current Encoder */
|
||||
__u32 connector_id; /**< Id */
|
||||
/** @encoder_id: Object ID of the current encoder. */
|
||||
__u32 encoder_id;
|
||||
/** @connector_id: Object ID of the connector. */
|
||||
__u32 connector_id;
|
||||
/**
|
||||
* @connector_type: Type of the connector.
|
||||
*
|
||||
* See DRM_MODE_CONNECTOR_* defines.
|
||||
*/
|
||||
__u32 connector_type;
|
||||
/**
|
||||
* @connector_type_id: Type-specific connector number.
|
||||
*
|
||||
* This is not an object ID. This is a per-type connector number. Each
|
||||
* (type, type_id) combination is unique across all connectors of a DRM
|
||||
* device.
|
||||
*/
|
||||
__u32 connector_type_id;
|
||||
|
||||
/**
|
||||
* @connection: Status of the connector.
|
||||
*
|
||||
* See enum drm_connector_status.
|
||||
*/
|
||||
__u32 connection;
|
||||
__u32 mm_width; /**< width in millimeters */
|
||||
__u32 mm_height; /**< height in millimeters */
|
||||
/** @mm_width: Width of the connected sink in millimeters. */
|
||||
__u32 mm_width;
|
||||
/** @mm_height: Height of the connected sink in millimeters. */
|
||||
__u32 mm_height;
|
||||
/**
|
||||
* @subpixel: Subpixel order of the connected sink.
|
||||
*
|
||||
* See enum subpixel_order.
|
||||
*/
|
||||
__u32 subpixel;
|
||||
|
||||
/** @pad: Padding, must be zero. */
|
||||
__u32 pad;
|
||||
};
|
||||
|
||||
|
@ -905,24 +994,23 @@ struct drm_format_modifier {
|
|||
|
||||
/**
|
||||
* struct drm_mode_create_blob - Create New block property
|
||||
* @data: Pointer to data to copy.
|
||||
* @length: Length of data to copy.
|
||||
* @blob_id: new property ID.
|
||||
*
|
||||
* Create a new 'blob' data property, copying length bytes from data pointer,
|
||||
* and returning new blob ID.
|
||||
*/
|
||||
struct drm_mode_create_blob {
|
||||
/** Pointer to data to copy. */
|
||||
/** @data: Pointer to data to copy. */
|
||||
__u64 data;
|
||||
/** Length of data to copy. */
|
||||
/** @length: Length of data to copy. */
|
||||
__u32 length;
|
||||
/** Return: new property ID. */
|
||||
/** @blob_id: Return: new property ID. */
|
||||
__u32 blob_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_mode_destroy_blob - Destroy user blob
|
||||
* @blob_id: blob_id to destroy
|
||||
*
|
||||
* Destroy a user-created blob property.
|
||||
*
|
||||
* User-space can release blobs as soon as they do not need to refer to them by
|
||||
|
@ -937,36 +1025,32 @@ struct drm_mode_destroy_blob {
|
|||
|
||||
/**
|
||||
* struct drm_mode_create_lease - Create lease
|
||||
* @object_ids: Pointer to array of object ids.
|
||||
* @object_count: Number of object ids.
|
||||
* @flags: flags for new FD.
|
||||
* @lessee_id: unique identifier for lessee.
|
||||
* @fd: file descriptor to new drm_master file.
|
||||
*
|
||||
* Lease mode resources, creating another drm_master.
|
||||
*/
|
||||
struct drm_mode_create_lease {
|
||||
/** Pointer to array of object ids (__u32) */
|
||||
/** @object_ids: Pointer to array of object ids (__u32) */
|
||||
__u64 object_ids;
|
||||
/** Number of object ids */
|
||||
/** @object_count: Number of object ids */
|
||||
__u32 object_count;
|
||||
/** flags for new FD (O_CLOEXEC, etc) */
|
||||
/** @flags: flags for new FD (O_CLOEXEC, etc) */
|
||||
__u32 flags;
|
||||
|
||||
/** Return: unique identifier for lessee. */
|
||||
/** @lessee_id: Return: unique identifier for lessee. */
|
||||
__u32 lessee_id;
|
||||
/** Return: file descriptor to new drm_master file */
|
||||
/** @fd: Return: file descriptor to new drm_master file */
|
||||
__u32 fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_mode_list_lessees - List lessees
|
||||
* @count_lessees: Number of lessees.
|
||||
* @pad: pad.
|
||||
* @lessees_ptr: Pointer to lessess.
|
||||
* List lesses from a drm_master
|
||||
*
|
||||
* List lesses from a drm_master.
|
||||
*/
|
||||
struct drm_mode_list_lessees {
|
||||
/** Number of lessees.
|
||||
/**
|
||||
* @count_lessees: Number of lessees.
|
||||
*
|
||||
* On input, provides length of the array.
|
||||
* On output, provides total number. No
|
||||
* more than the input number will be written
|
||||
|
@ -974,23 +1058,26 @@ struct drm_mode_list_lessees {
|
|||
* the size and then the data.
|
||||
*/
|
||||
__u32 count_lessees;
|
||||
/** @pad: Padding. */
|
||||
__u32 pad;
|
||||
|
||||
/** Pointer to lessees.
|
||||
* pointer to __u64 array of lessee ids
|
||||
/**
|
||||
* @lessees_ptr: Pointer to lessees.
|
||||
*
|
||||
* Pointer to __u64 array of lessee ids
|
||||
*/
|
||||
__u64 lessees_ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_mode_get_lease - Get Lease
|
||||
* @count_objects: Number of leased objects.
|
||||
* @pad: pad.
|
||||
* @objects_ptr: Pointer to objects.
|
||||
* Get leased objects
|
||||
*
|
||||
* Get leased objects.
|
||||
*/
|
||||
struct drm_mode_get_lease {
|
||||
/** Number of leased objects.
|
||||
/**
|
||||
* @count_objects: Number of leased objects.
|
||||
*
|
||||
* On input, provides length of the array.
|
||||
* On output, provides total number. No
|
||||
* more than the input number will be written
|
||||
|
@ -998,22 +1085,22 @@ struct drm_mode_get_lease {
|
|||
* the size and then the data.
|
||||
*/
|
||||
__u32 count_objects;
|
||||
/** @pad: Padding. */
|
||||
__u32 pad;
|
||||
|
||||
/** Pointer to objects.
|
||||
* pointer to __u32 array of object ids
|
||||
/**
|
||||
* @objects_ptr: Pointer to objects.
|
||||
*
|
||||
* Pointer to __u32 array of object ids.
|
||||
*/
|
||||
__u64 objects_ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_mode_revoke_lease - Revoke lease
|
||||
* @lessee_id: Unique ID of lessee.
|
||||
* Revoke lease
|
||||
*/
|
||||
struct drm_mode_revoke_lease {
|
||||
/** Unique ID of lessee
|
||||
*/
|
||||
/** @lessee_id: Unique ID of lessee */
|
||||
__u32 lessee_id;
|
||||
};
|
||||
|
||||
|
|
|
@ -115,6 +115,10 @@ enum virtio_gpu_ctrl_type {
|
|||
|
||||
enum virtio_gpu_shm_id {
|
||||
VIRTIO_GPU_SHM_ID_UNDEFINED = 0,
|
||||
/*
|
||||
* VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB
|
||||
* VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB
|
||||
*/
|
||||
VIRTIO_GPU_SHM_ID_HOST_VISIBLE = 1
|
||||
};
|
||||
|
||||
|
|
|
@ -1897,8 +1897,8 @@ out:
|
|||
return addr;
|
||||
|
||||
unmap_and_free_vma:
|
||||
fput(vma->vm_file);
|
||||
vma->vm_file = NULL;
|
||||
fput(file);
|
||||
|
||||
/* Undo any partial mapping done by a device driver. */
|
||||
unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
|
||||
|
|
12
mm/util.c
12
mm/util.c
|
@ -311,6 +311,18 @@ int vma_is_stack_for_current(struct vm_area_struct *vma)
|
|||
return (vma->vm_start <= KSTK_ESP(t) && vma->vm_end >= KSTK_ESP(t));
|
||||
}
|
||||
|
||||
/*
|
||||
* Change backing file, only valid to use during initial VMA setup.
|
||||
*/
|
||||
void vma_set_file(struct vm_area_struct *vma, struct file *file)
|
||||
{
|
||||
/* Changing an anonymous vma with this is illegal */
|
||||
get_file(file);
|
||||
swap(vma->vm_file, file);
|
||||
fput(file);
|
||||
}
|
||||
EXPORT_SYMBOL(vma_set_file);
|
||||
|
||||
#ifndef STACK_RND_MASK
|
||||
#define STACK_RND_MASK (0x7ff >> (PAGE_SHIFT - 12)) /* 8MB of VA */
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue