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:
Daniel Vetter 2020-12-15 10:21:47 +01:00
commit 5fbd41d3bf
70 changed files with 2134 additions and 905 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)
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;
}

View File

@ -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,
};

View File

@ -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 */

View File

@ -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");

View File

@ -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

View File

@ -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),

View File

@ -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) {
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;
goto memcpy;
}
r = amdgpu_move_blit(bo, evict, new_mem, old_mem);
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;
}
/*

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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:

View File

@ -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 */
;
}

View File

@ -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;

View File

@ -230,14 +230,14 @@ struct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc)
*
* Setting MODE_ID to 0 will release reserved resources for the CRTC.
* SCALING_FILTER:
* Atomic property for setting the scaling filter for CRTC scaler
* 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
* The value of this property can be one of the following:
*
* Default:
* Driver's default scaling filter
* Nearest Neighbor:
* Nearest Neighbor scaling filter
*/
/**

View File

@ -371,9 +371,9 @@ 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,
struct drm_clip_rect *clip,
struct dma_buf_map *dst)
static void drm_fb_helper_damage_blit_real(struct drm_fb_helper *fb_helper,
struct drm_clip_rect *clip,
struct dma_buf_map *dst)
{
struct drm_framebuffer *fb = fb_helper->fb;
unsigned int cpp = fb->format->cpp[0];
@ -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) {
/* Call damage handlers only if necessary */
if (!(clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2))
return;
/* Generic fbdev uses a shadow buffer */
if (helper->buffer) {
ret = drm_client_buffer_vmap(helper->buffer, &map);
if (ret)
return;
drm_fb_helper_dirty_blit_real(helper, &clip_copy, &map);
}
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->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) {
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,
u32 width, u32 height)
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) {
fb_deferred_io_cleanup(fbi);
shadow = fbi->screen_buffer;
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);
vfree(shadow);
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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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:

View File

@ -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,

View File

@ -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 |

View File

@ -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,8 +277,11 @@ static void dcss_scaler_filter_design(int src_length, int dst_length,
else
fc_q = div_q(dst_length, src_length * PSC_NUM_PHASES);
/* compute gaussian filter coefficients */
dcss_scaler_gaussian_filter(fc_q, use_5_taps, phase0_identity, coef);
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);
}
static void dcss_scaler_write(struct dcss_scaler_ch *ch, u32 val, u32 ofs)
@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
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);
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);
/* Drain the FIFO A + channel 0 pipe so we have a clean slate */
mcde_drain_pipe(mcde, MCDE_FIFO_A, MCDE_CHANNEL_0);
@ -995,29 +1219,47 @@ static void mcde_display_enable(struct drm_simple_display_pipe *pipe,
*/
mcde_configure_channel(mcde, MCDE_CHANNEL_0, MCDE_FIFO_A, mode);
/* Configure FIFO A to use DSI formatter 0 */
mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0,
fifo_wtrmrk);
if (mcde->dpi_output) {
unsigned long lcd_freq;
/*
* 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 FIFO A to use DPI formatter 0 */
mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DPI_FORMATTER_0,
fifo_wtrmrk);
/* Configure the DSI formatter 0 for the DSI panel output */
mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
formatter_frame, pkt_size);
/* 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);
/*
* This brings up the DSI bridge which is tightly connected
* to the MCDE DSI formatter.
*/
mcde_dsi_enable(mcde->bridge);
/* Configure the DSI formatter 0 for the DSI panel output */
mcde_configure_dsi_formatter(mcde, MCDE_DSI_FORMATTER_0,
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);
/* This disables the DSI bridge */
mcde_dsi_disable(mcde->bridge);
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),

View File

@ -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

View File

@ -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_ */

View File

@ -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) {

View File

@ -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,

View File

@ -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,
};

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -23,76 +23,254 @@
#include "panel-samsung-s6e63m0.h"
/* Manufacturer Command Set */
#define MCS_ELVSS_ON 0xb1
#define MCS_MIECTL1 0xc0
#define MCS_BCMODE 0xc1
#define MCS_ELVSS_ON 0xb1
#define MCS_TEMP_SWIRE 0xb2
#define MCS_MIECTL1 0xc0
#define MCS_BCMODE 0xc1
#define MCS_ERROR_CHECK 0xd5
#define MCS_READ_ID1 0xda
#define MCS_READ_ID2 0xdb
#define MCS_READ_ID3 0xdc
#define MCS_LEVEL_2_KEY 0xf0
#define MCS_MTP_KEY 0xf1
#define MCS_DISCTL 0xf2
#define MCS_SRCCTL 0xf6
#define MCS_IFCTL 0xf7
#define MCS_PANELCTL 0xF8
#define MCS_PGAMMACTL 0xfa
#define MCS_DISCTL 0xf2
#define MCS_SRCCTL 0xf6
#define MCS_IFCTL 0xf7
#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 GAMMA_TABLE_COUNT 23
#define NUM_GAMMA_LEVELS 28
#define GAMMA_TABLE_COUNT 23
#define MAX_BRIGHTNESS (NUM_GAMMA_LEVELS - 1)
#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);
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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:

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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),
};

View File

@ -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;
}

View File

@ -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,

View File

@ -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 = {

View File

@ -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);

View File

@ -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:

View File

@ -239,6 +239,7 @@ static u32 to3264(u32 timing, int bpp, int is64)
fallthrough;
case 16:
timing >>= 1;
fallthrough;
case 32:
break;
}

View File

@ -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
* the screen buffer
* @dirty_lock: spinlock protecting @dirty_clip
* @dirty_work: worker used to flush the framebuffer
* @damage_clip: clip rectangle used with deferred_io to accumulate damage to
* the screen buffer
* @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;
/**

View File

@ -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);

View File

@ -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.
*
* ::

View File

@ -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:

View File

@ -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);

View File

@ -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;
};

View File

@ -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
};

View File

@ -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);

View File

@ -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