diff options
70 files changed, 2131 insertions, 902 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index f9750b0b6708..27fffafe5b5c 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -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 diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile index cb2497d157f6..90ed8c789e48 100644 --- a/drivers/char/agp/Makefile +++ b/drivers/char/agp/Makefile @@ -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 diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 4eb1c772ded7..bb09d64cd51e 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -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); diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 0eb80c1ecdab..e63684d4cd90 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -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); diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index bb5a42b10c29..6ddbeb5dfbf6 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -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); diff --git a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile index 6e54cdec3da0..974467791032 100644 --- a/drivers/dma-buf/heaps/Makefile +++ b/drivers/dma-buf/heaps/Makefile @@ -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 diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c index e55384dc115b..5e7c3436310c 100644 --- a/drivers/dma-buf/heaps/cma_heap.c +++ b/drivers/dma-buf/heaps/cma_heap.c @@ -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_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; +}; + +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 *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 *buffer = dmabuf->priv; + struct dma_heap_attachment *a; + int ret; - /* free page list */ - kfree(buffer->pages); - /* release memory */ - cma_release(cma_heap->cma, cma_pages, nr_pages); + 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; - if (align > CONFIG_CMA_ALIGNMENT) - align = CONFIG_CMA_ALIGNMENT; - - 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, cma_heap_free); - helper_buffer->heap = heap; - helper_buffer->size = len; + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->lock); + buffer->len = size; + + if (align > CONFIG_CMA_ALIGNMENT) + align = CONFIG_CMA_ALIGNMENT; - 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; } diff --git a/drivers/dma-buf/heaps/heap-helpers.c b/drivers/dma-buf/heaps/heap-helpers.c deleted file mode 100644 index fcf4ce3e2cbb..000000000000 --- a/drivers/dma-buf/heaps/heap-helpers.c +++ /dev/null @@ -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, -}; diff --git a/drivers/dma-buf/heaps/heap-helpers.h b/drivers/dma-buf/heaps/heap-helpers.h deleted file mode 100644 index 805d2df88024..000000000000 --- a/drivers/dma-buf/heaps/heap-helpers.h +++ /dev/null @@ -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 */ diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c index 0bf688e3c023..17e0e9a68baf 100644 --- a/drivers/dma-buf/heaps/system_heap.c +++ b/drivers/dma-buf/heaps/system_heap.c @@ -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> + +static 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; +}; + +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) +{ + struct sg_table *new_table; + int ret, i; + struct scatterlist *sg, *new_sg; + + 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); -#include "heap-helpers.h" + 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; +} -struct dma_heap *sys_heap; +static void system_heap_vunmap(struct dma_buf *dmabuf, struct dma_buf_map *map) +{ + struct system_heap_buffer *buffer = dmabuf->priv; -static void system_heap_free(struct heap_helper_buffer *buffer) + 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) { - pgoff_t pg; + 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); - for (pg = 0; pg < buffer->pagecount; pg++) - __free_page(buffer->pages[pg]); - kfree(buffer->pages); + __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; - - 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; - } + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->lock); + buffer->heap = heap; + buffer->len = len; - 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; + + 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; - helper_buffer->pages[pg] = alloc_page(GFP_KERNEL | __GFP_ZERO); - if (!helper_buffer->pages[pg]) - goto err1; + 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"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 6e2953233231..f9c81bc21ba4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -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 diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index ebdab31f9de9..31506a1678c3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -1533,8 +1533,6 @@ int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv) return 0; } -int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); - const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index b848f9e97613..4d8f19ab1014 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -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; } /* diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 6f975c16779d..8ab0b9060d2b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -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); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ddd0e3239150..ba1507036f26 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -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); } } } diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index ae2234aae93d..5c2141e9a9f4 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -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: diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 7a01d0918861..aeb1327e3077 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -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 */ ; } diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index fe573acf1067..ce45e380f4a2 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -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; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f927976eca50..74090fc3aa55 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -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 */ /** diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 25edf670867c..4b8119510687 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -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_client_buffer *buffer = fb_helper->buffer; + struct dma_buf_map map, dst; + int ret; + + /* + * 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, - dirty_work); - struct drm_clip_rect *clip = &helper->dirty_clip; + 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; - struct dma_buf_map map; int ret; - spin_lock_irqsave(&helper->dirty_lock, flags); + spin_lock_irqsave(&helper->damage_lock, flags); clip_copy = *clip; clip->x1 = clip->y1 = ~0; clip->x2 = clip->y2 = 0; - spin_unlock_irqrestore(&helper->dirty_lock, flags); + spin_unlock_irqrestore(&helper->damage_lock, flags); - /* call dirty callback only when it has been really touched */ - if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2) { - - /* Generic fbdev uses a shadow buffer */ - if (helper->buffer) { - ret = drm_client_buffer_vmap(helper->buffer, &map); - if (ret) - return; - drm_fb_helper_dirty_blit_real(helper, &clip_copy, &map); - } + /* Call damage handlers only if necessary */ + if (!(clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)) + return; - if (helper->fb->funcs->dirty) - helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, - &clip_copy, 1); + 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->buffer) - drm_client_buffer_vunmap(helper->buffer); + if (helper->fb->funcs->dirty) { + ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1); + if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret)) + goto err; } + + return; + +err: + /* + * Restore damage clip rectangle on errors. The next run + * of the damage worker will perform the update. + */ + spin_lock_irqsave(&helper->damage_lock, flags); + clip->x1 = min_t(u32, clip->x1, clip_copy.x1); + clip->y1 = min_t(u32, clip->y1, clip_copy.y1); + clip->x2 = max_t(u32, clip->x2, clip_copy.x2); + clip->y2 = max_t(u32, clip->y2, clip_copy.y2); + spin_unlock_irqrestore(&helper->damage_lock, flags); } /** @@ -440,10 +486,10 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, const struct drm_fb_helper_funcs *funcs) { INIT_LIST_HEAD(&helper->kernel_fb_list); - spin_lock_init(&helper->dirty_lock); + spin_lock_init(&helper->damage_lock); INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker); - INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work); - helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0; + INIT_WORK(&helper->damage_work, drm_fb_helper_damage_work); + helper->damage_clip.x1 = helper->damage_clip.y1 = ~0; mutex_init(&helper->lock); helper->funcs = funcs; helper->dev = dev; @@ -579,7 +625,7 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) return; cancel_work_sync(&fb_helper->resume_work); - cancel_work_sync(&fb_helper->dirty_work); + cancel_work_sync(&fb_helper->damage_work); info = fb_helper->fbdev; if (info) { @@ -614,30 +660,30 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper) fb->funcs->dirty; } -static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y, - 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; } diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 499189c48f0b..9825c378dfa6 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -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); @@ -477,33 +480,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 * @dev: DRM device @@ -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; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index bbd235473645..6d38c5c17f23 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -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; } diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 0c8684634fca..27f04aed8764 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -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; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c index 0dd477e56573..04e9c04545ad 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c @@ -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; } diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c index 3d69e51f3e4d..ec28a6cde49b 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c @@ -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: diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.h b/drivers/gpu/drm/imx/dcss/dcss-dev.h index c642ae17837f..1e582270c6ea 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-dev.h +++ b/drivers/gpu/drm/imx/dcss/dcss-dev.h @@ -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, diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c index e13652e3a115..03ba88f7f995 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-plane.c +++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c @@ -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 | diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c index cd21905de580..47852b9dd5ea 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-scaler.c +++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c @@ -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, diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index 832e5280a6ed..de62966243cd 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c @@ -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; diff --git a/drivers/gpu/drm/mcde/Kconfig b/drivers/gpu/drm/mcde/Kconfig index b3990126562c..71c689b573c9 100644 --- a/drivers/gpu/drm/mcde/Kconfig +++ b/drivers/gpu/drm/mcde/Kconfig @@ -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 diff --git a/drivers/gpu/drm/mcde/Makefile b/drivers/gpu/drm/mcde/Makefile index fe28f4e0fe46..15d9c89a3273 100644 --- a/drivers/gpu/drm/mcde/Makefile +++ b/drivers/gpu/drm/mcde/Makefile @@ -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 diff --git a/drivers/gpu/drm/mcde/mcde_clk_div.c b/drivers/gpu/drm/mcde/mcde_clk_div.c new file mode 100644 index 000000000000..038821d2ef80 --- /dev/null +++ b/drivers/gpu/drm/mcde/mcde_clk_div.c @@ -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; +} diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c index c271e5bf042e..7c2e0b865441 100644 --- a/drivers/gpu/drm/mcde/mcde_display.c +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -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() */ - - /* Use the MCDE clock for this FIFO */ - val = MCDE_CRX1_CLKSEL_MCDECLK << MCDE_CRX1_CLKSEL_SHIFT; + 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; + + /* 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); + + /* 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); +} - dev_info(drm->dev, "output in %s mode, format %dbpp\n", +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; + + /* Configure FIFO A to use DPI formatter 0 */ + mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DPI_FORMATTER_0, + fifo_wtrmrk); + + /* Set up and enable the LCD clock */ + lcd_freq = clk_round_rate(mcde->fifoa_clk, mode->clock * 1000); + ret = clk_set_rate(mcde->fifoa_clk, lcd_freq); + if (ret) + dev_err(mcde->dev, "failed to set LCD clock rate %lu Hz\n", + lcd_freq); + ret = clk_prepare_enable(mcde->fifoa_clk); + if (ret) { + dev_err(mcde->dev, "failed to enable FIFO A DPI clock\n"); + return; + } + dev_info(mcde->dev, "LCD FIFO A clk rate %lu Hz\n", + clk_get_rate(mcde->fifoa_clk)); + } else { + /* Configure FIFO A to use DSI formatter 0 */ + mcde_configure_fifo(mcde, MCDE_FIFO_A, MCDE_DSI_FORMATTER_0, + fifo_wtrmrk); - /* - * 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); + /* + * 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, - formatter_frame, pkt_size); + /* 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), diff --git a/drivers/gpu/drm/mcde/mcde_display_regs.h b/drivers/gpu/drm/mcde/mcde_display_regs.h index d3ac7ef5ff9a..2ad78c59d627 100644 --- a/drivers/gpu/drm/mcde/mcde_display_regs.h +++ b/drivers/gpu/drm/mcde/mcde_display_regs.h @@ -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 diff --git a/drivers/gpu/drm/mcde/mcde_drm.h b/drivers/gpu/drm/mcde/mcde_drm.h index 8253e2f9993e..ecb70b4b737c 100644 --- a/drivers/gpu/drm/mcde/mcde_drm.h +++ b/drivers/gpu/drm/mcde/mcde_drm.h @@ -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_ */ diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index 9d25181bd7e2..e60566a5739c 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -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) { diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index 7f8eea494147..aad75a22dc33 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -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, diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 0f07f259503d..a977c9f49719 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -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, }; diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 82cbaf337b50..9a7c49bc394f 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -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); } diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 36d6b6093d16..33fff388dd83 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -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; diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 68c271f4250b..30d299ca8795 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -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); } diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c index 210e70da3a15..6b4e97bfd46e 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c @@ -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; - - /* disable and set new gamma */ + u8 elvss_val; + u8 elvss_cmd_set[5]; + int i; + + /* 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); } diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 597f676a6591..41bbec72b2da 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -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, @@ -4123,6 +4148,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, }, { diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c index 57a31dd0ffed..3e0723bc36bd 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c @@ -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; } diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 1b96780b4989..5455b2044759 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -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); diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index b5a8dd9fdf02..9269092697d8 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -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, diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c index 8b52cb25877c..6a8731ab9d7d 100644 --- a/drivers/gpu/drm/v3d/v3d_bo.c +++ b/drivers/gpu/drm/v3d/v3d_bo.c @@ -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; diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 34612edcabbd..8aa5220885f4 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -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; diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c index 9a413091abb6..f8635ccaf9a1 100644 --- a/drivers/gpu/drm/vgem/vgem_drv.c +++ b/drivers/gpu/drm/vgem/vgem_drv.c @@ -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)); diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index 24cc445169e2..a3e0fb5b8671 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c @@ -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: diff --git a/drivers/gpu/drm/via/via_verifier.c b/drivers/gpu/drm/via/via_verifier.c index 8d8135f424ef..3d6e3a70f318 100644 --- a/drivers/gpu/drm/via/via_verifier.c +++ b/drivers/gpu/drm/via/via_verifier.c @@ -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; diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c index f336a8fa6666..5fefc88d47e4 100644 --- a/drivers/gpu/drm/virtio/virtgpu_debugfs.c +++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c @@ -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; } diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 3c0e17212c33..6a232553c99b 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -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); diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c index 5b2a4146c5bd..728ca36f6327 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fence.c +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c @@ -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); diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index 5417f365d1a3..23eb6d772e40 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -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), }; diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index d9ad27e00905..d69a5b6da553 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -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; } diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 1a1b5bc8e121..d4d39227f2ed 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -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, diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c index 67f80ab1e85f..78fdc1d59186 100644 --- a/drivers/gpu/drm/vkms/vkms_writeback.c +++ b/drivers/gpu/drm/vkms/vkms_writeback.c @@ -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 = { diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 10b4be1f3e78..4789d36ddfd3 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -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); diff --git a/drivers/video/fbdev/geode/lxfb_ops.c b/drivers/video/fbdev/geode/lxfb_ops.c index b3a041fce570..32baaf59fcf7 100644 --- a/drivers/video/fbdev/geode/lxfb_ops.c +++ b/drivers/video/fbdev/geode/lxfb_ops.c @@ -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: diff --git a/drivers/video/fbdev/pm2fb.c b/drivers/video/fbdev/pm2fb.c index 0642555289e0..27893fa139b0 100644 --- a/drivers/video/fbdev/pm2fb.c +++ b/drivers/video/fbdev/pm2fb.c @@ -239,6 +239,7 @@ static u32 to3264(u32 timing, int bpp, int is64) fallthrough; case 16: timing >>= 1; + fallthrough; case 32: break; } diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 306aa3a60be9..3b273f9ca39a 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -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; /** diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index 3449a0353fe0..434328d8a0d9 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -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); diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index cdf2a299ccd4..a0d79d1c51e2 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -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. * * :: diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index f2de050085be..16ff3fa148f5 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -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: diff --git a/include/linux/mm.h b/include/linux/mm.h index db6ae4d3fb4e..47bff16c182d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -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); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 5ad10ab2a577..b49fbf2bdc40 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -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; }; diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h index 0ec6b610402c..97523a95781d 100644 --- a/include/uapi/linux/virtio_gpu.h +++ b/include/uapi/linux/virtio_gpu.h @@ -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 }; diff --git a/mm/mmap.c b/mm/mmap.c index 5c8b4485860d..f25804be7d16 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -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); diff --git a/mm/util.c b/mm/util.c index 4ddb6e186dd5..8c9b7d1e7c49 100644 --- a/mm/util.c +++ b/mm/util.c @@ -311,6 +311,18 @@ int vma_is_stack_for_current(struct vm_area_struct *vma) return (vma->vm_start <= KSTK_ESP(t) && vma->vm_end >= KSTK_ESP(t)); } +/* + * Change backing file, only valid to use during initial VMA setup. + */ +void vma_set_file(struct vm_area_struct *vma, struct file *file) +{ + /* Changing an anonymous vma with this is illegal */ + get_file(file); + swap(vma->vm_file, file); + fput(file); +} +EXPORT_SYMBOL(vma_set_file); + #ifndef STACK_RND_MASK #define STACK_RND_MASK (0x7ff >> (PAGE_SHIFT - 12)) /* 8MB of VA */ #endif |