forked from Minki/linux
drm-misc-next for 5.2:
UAPI Changes: - Add Colorspace connector property (Uma) - fourcc: Several new YUV formats from ARM (Brian & Ayan) - fourcc: Fix merge conflicts between new formats above and Swati's that went in via topic/hdr-formats-2019-03-07 branch (Maarten) Cross-subsystem Changes: - Typed component support via topic/component-typed-2019-02-11 (Maxime/Daniel) Core Changes: - Improve component helper documentation (Daniel) - Avoid calling drm_dev_unregister() twice on unplugged devices (Noralf) - Add device managed (devm) drm_device init function (Noralf) - Graduate TINYDRM_MODE to DRM_SIMPLE_MODE in core (Noralf) - Move MIPI/DSI rate control params computation into core from i915 (David) - Add support for shmem backed gem objects (Noralf) Driver Changes: - various: Use of_node_name_eq for node name comparisons (Rob Herring) - sun4i: Add DSI burst mode support (Konstantin) - panel: Add Ronbo RB070D30 MIPI/DSI panel support (Konstantin) - virtio: A few prime improvements (Gerd) - tinydrm: Remove tinydrm_device (Noralf) - vc4: Add load tracker to driver to detect underflow in atomic check (Boris) - vboxvideo: Move it out of staging \o/ (Hans) - v3d: Add support for V3D v4.2 (Eric) Cc: Konstantin Sudakov <k.sudakov@integrasources.com> Cc: Rob Herring <robh@kernel.org> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Maxime Ripard <maxime.ripard@bootlin.com> Cc: Uma Shankar <uma.shankar@intel.com> Cc: Noralf Trønnes <noralf@tronnes.org> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: David Francis <David.Francis@amd.com> Cc: Boris Brezillon <boris.brezillon@bootlin.com> Cc: Eric Anholt <eric@anholt.net> Cc: Hans de Goede <hdegoede@redhat.com> Cc: Brian Starkey <brian.starkey@arm.com> Cc: Ayan Kumar Halder <ayan.halder@arm.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEfxcpfMSgdnQMs+QqlvcN/ahKBwoFAlyTxEYACgkQlvcN/ahK Bwpfhgf9HTwlxHKPwRL70o5Ilp7JVjeLjM5IgDgz+o7F+UZn2OdWocmSDAbJ+lwe V+LXImc5tykGNRzgn4lXljGv3jqxOgVOxEBo53hVjXeYE/jIdbGDF1cx+1tSke67 lbO61dD9RM5GG9eLuzZ9S72qv5mfBYKHJZuULqOei/Ohnubkg0kDQ3zQEFDah1mh kqHJkd+x1PwcwBnAjbWdIaCMiwrVmxj7yXLQS8bJzSFKc0/r7HlG8qNWmiBllH0D aRMO2phHkXCVZY+GWWCEOZwz7ve23sibYm9tzBS69nbWJL12CAomB/8LrRPM2K5v tVBNrX0eNHMKtOa0En0oF37BXUXizQ== =DVKQ -----END PGP SIGNATURE----- Merge tag 'drm-misc-next-2019-03-21' of git://anongit.freedesktop.org/drm/drm-misc into drm-next drm-misc-next for 5.2: UAPI Changes: - Add Colorspace connector property (Uma) - fourcc: Several new YUV formats from ARM (Brian & Ayan) - fourcc: Fix merge conflicts between new formats above and Swati's that went in via topic/hdr-formats-2019-03-07 branch (Maarten) Cross-subsystem Changes: - Typed component support via topic/component-typed-2019-02-11 (Maxime/Daniel) Core Changes: - Improve component helper documentation (Daniel) - Avoid calling drm_dev_unregister() twice on unplugged devices (Noralf) - Add device managed (devm) drm_device init function (Noralf) - Graduate TINYDRM_MODE to DRM_SIMPLE_MODE in core (Noralf) - Move MIPI/DSI rate control params computation into core from i915 (David) - Add support for shmem backed gem objects (Noralf) Driver Changes: - various: Use of_node_name_eq for node name comparisons (Rob Herring) - sun4i: Add DSI burst mode support (Konstantin) - panel: Add Ronbo RB070D30 MIPI/DSI panel support (Konstantin) - virtio: A few prime improvements (Gerd) - tinydrm: Remove tinydrm_device (Noralf) - vc4: Add load tracker to driver to detect underflow in atomic check (Boris) - vboxvideo: Move it out of staging \o/ (Hans) - v3d: Add support for V3D v4.2 (Eric) Cc: Konstantin Sudakov <k.sudakov@integrasources.com> Cc: Rob Herring <robh@kernel.org> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Maxime Ripard <maxime.ripard@bootlin.com> Cc: Uma Shankar <uma.shankar@intel.com> Cc: Noralf Trønnes <noralf@tronnes.org> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: David Francis <David.Francis@amd.com> Cc: Boris Brezillon <boris.brezillon@bootlin.com> Cc: Eric Anholt <eric@anholt.net> Cc: Hans de Goede <hdegoede@redhat.com> Cc: Brian Starkey <brian.starkey@arm.com> Cc: Ayan Kumar Halder <ayan.halder@arm.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> From: Sean Paul <sean@poorly.run> Link: https://patchwork.freedesktop.org/patch/msgid/20190321170805.GA50145@art_vandelay
This commit is contained in:
commit
0bec6219e5
@ -60,15 +60,14 @@ Required properties:
|
||||
- reg: base address and size of he following memory-mapped regions :
|
||||
- vpu
|
||||
- hhi
|
||||
- dmc
|
||||
- reg-names: should contain the names of the previous memory regions
|
||||
- interrupts: should contain the VENC Vsync interrupt number
|
||||
- amlogic,canvas: phandle to canvas provider node as described in the file
|
||||
../soc/amlogic/amlogic,canvas.txt
|
||||
|
||||
Optional properties:
|
||||
- power-domains: Optional phandle to associated power domain as described in
|
||||
the file ../power/power_domain.txt
|
||||
- amlogic,canvas: phandle to canvas provider node as described in the file
|
||||
../soc/amlogic/amlogic,canvas.txt
|
||||
|
||||
Required nodes:
|
||||
|
||||
|
@ -0,0 +1,51 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0+ OR X11)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/ronbo,rb070d30.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ronbo RB070D30 DSI Display Panel
|
||||
|
||||
maintainers:
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ronbo,rb070d30
|
||||
|
||||
reg:
|
||||
description: MIPI-DSI virtual channel
|
||||
|
||||
power-gpios:
|
||||
description: GPIO used for the power pin
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO used for the reset pin
|
||||
maxItems: 1
|
||||
|
||||
shlr-gpios:
|
||||
description: GPIO used for the shlr pin (horizontal flip)
|
||||
maxItems: 1
|
||||
|
||||
updn-gpios:
|
||||
description: GPIO used for the updn pin (vertical flip)
|
||||
maxItems: 1
|
||||
|
||||
vcc-lcd-supply:
|
||||
description: Power regulator
|
||||
|
||||
backlight:
|
||||
description: Backlight used by the panel
|
||||
$ref: "/schemas/types.yaml#/definitions/phandle"
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- power-gpios
|
||||
- reg
|
||||
- reset-gpios
|
||||
- shlr-gpios
|
||||
- updn-gpios
|
||||
- vcc-lcd-supply
|
||||
|
||||
additionalProperties: false
|
@ -6,15 +6,20 @@ For V3D 2.x, see brcm,bcm-vc4.txt.
|
||||
Required properties:
|
||||
- compatible: Should be "brcm,7268-v3d" or "brcm,7278-v3d"
|
||||
- reg: Physical base addresses and lengths of the register areas
|
||||
- reg-names: Names for the register areas. The "hub", "bridge", and "core0"
|
||||
- reg-names: Names for the register areas. The "hub" and "core0"
|
||||
register areas are always required. The "gca" register area
|
||||
is required if the GCA cache controller is present.
|
||||
is required if the GCA cache controller is present. The
|
||||
"bridge" register area is required if an external reset
|
||||
controller is not present.
|
||||
- interrupts: The interrupt numbers. The first interrupt is for the hub,
|
||||
while the following interrupts are for the cores.
|
||||
while the following interrupts are separate interrupt lines
|
||||
for the cores (if they don't share the hub's interrupt).
|
||||
See bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Optional properties:
|
||||
- clocks: The core clock the unit runs on
|
||||
- resets: The reset line for v3d, if not using a mapping of the bridge
|
||||
See bindings/reset/reset.txt
|
||||
|
||||
v3d {
|
||||
compatible = "brcm,7268-v3d";
|
||||
|
@ -346,6 +346,7 @@ rikomagic Rikomagic Tech Corp. Ltd
|
||||
riscv RISC-V Foundation
|
||||
rockchip Fuzhou Rockchip Electronics Co., Ltd
|
||||
rohm ROHM Semiconductor Co., Ltd
|
||||
ronbo Ronbo Electronics
|
||||
roofull Shenzhen Roofull Technology Co, Ltd
|
||||
samsung Samsung Semiconductor
|
||||
samtec Samtec/Softing company
|
||||
|
@ -1,3 +1,5 @@
|
||||
.. _component:
|
||||
|
||||
======================================
|
||||
Component Helper for Aggregate Drivers
|
||||
======================================
|
||||
|
@ -256,6 +256,9 @@ DMA
|
||||
dmam_pool_create()
|
||||
dmam_pool_destroy()
|
||||
|
||||
DRM
|
||||
devm_drm_dev_init()
|
||||
|
||||
GPIO
|
||||
devm_gpiod_get()
|
||||
devm_gpiod_get_index()
|
||||
|
@ -93,6 +93,11 @@ Device Instance and Driver Handling
|
||||
Driver Load
|
||||
-----------
|
||||
|
||||
Component Helper Usage
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_drv.c
|
||||
:doc: component helper usage recommendations
|
||||
|
||||
IRQ Helper Library
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
@ -369,3 +369,15 @@ Legacy CRTC/Modeset Helper Functions Reference
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
|
||||
:export:
|
||||
|
||||
SHMEM GEM Helper Reference
|
||||
==========================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gem_shmem_helper.c
|
||||
:doc: overview
|
||||
|
||||
.. kernel-doc:: include/drm/drm_gem_shmem_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gem_shmem_helper.c
|
||||
:export:
|
||||
|
@ -1,27 +1,12 @@
|
||||
==========================
|
||||
drm/tinydrm Driver library
|
||||
==========================
|
||||
============================
|
||||
drm/tinydrm Tiny DRM drivers
|
||||
============================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
|
||||
:doc: overview
|
||||
tinydrm is a collection of DRM drivers that are so small they can fit in a
|
||||
single source file.
|
||||
|
||||
Core functionality
|
||||
==================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
|
||||
:doc: core
|
||||
|
||||
.. kernel-doc:: include/drm/tinydrm/tinydrm.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
|
||||
:export:
|
||||
|
||||
Additional helpers
|
||||
==================
|
||||
Helpers
|
||||
=======
|
||||
|
||||
.. kernel-doc:: include/drm/tinydrm/tinydrm-helpers.h
|
||||
:internal:
|
||||
@ -29,6 +14,9 @@ Additional helpers
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
|
||||
:export:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
|
||||
:export:
|
||||
|
||||
MIPI DBI Compatible Controllers
|
||||
===============================
|
||||
|
||||
|
@ -215,12 +215,12 @@ Might be good to also have some igt testcases for this.
|
||||
|
||||
Contact: Daniel Vetter, Noralf Tronnes
|
||||
|
||||
Put a reservation_object into drm_gem_object
|
||||
Remove the ->gem_prime_res_obj callback
|
||||
--------------------------------------------
|
||||
|
||||
This would remove the need for the ->gem_prime_res_obj callback. It would also
|
||||
allow us to implement generic helpers for waiting for a bo, allowing for quite a
|
||||
bit of refactoring in the various wait ioctl implementations.
|
||||
The ->gem_prime_res_obj callback can be removed from drivers by using the
|
||||
reservation_object in the drm_gem_object. It may also be possible to use the
|
||||
generic drm_gem_reservation_object_wait helper for waiting for a bo.
|
||||
|
||||
Contact: Daniel Vetter
|
||||
|
||||
@ -469,10 +469,6 @@ those drivers as simple as possible, so lots of room for refactoring:
|
||||
one of the ideas for having a shared dsi/dbi helper, abstracting away the
|
||||
transport details more.
|
||||
|
||||
- Quick aside: The unregister devm stuff is kinda getting the lifetimes of
|
||||
a drm_device wrong. Doesn't matter, since everyone else gets it wrong
|
||||
too :-)
|
||||
|
||||
Contact: Noralf Trønnes, Daniel Vetter
|
||||
|
||||
AMD DC Display Driver
|
||||
|
@ -5045,6 +5045,13 @@ S: Odd Fixes
|
||||
F: drivers/gpu/drm/udl/
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
|
||||
DRM DRIVER FOR VIRTUALBOX VIRTUAL GPU
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
F: drivers/gpu/drm/vboxvideo/
|
||||
T: git git://anongit.freedesktop.org/drm/drm-misc
|
||||
|
||||
DRM DRIVER FOR VIRTUAL KERNEL MODESETTING (VKMS)
|
||||
M: Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
|
||||
R: Haneen Mohammed <hamohammed.sa@gmail.com>
|
||||
|
@ -73,6 +73,8 @@ int reservation_object_reserve_shared(struct reservation_object *obj,
|
||||
struct reservation_object_list *old, *new;
|
||||
unsigned int i, j, k, max;
|
||||
|
||||
reservation_object_assert_held(obj);
|
||||
|
||||
old = reservation_object_get_list(obj);
|
||||
|
||||
if (old && old->shared_max) {
|
||||
@ -151,6 +153,8 @@ void reservation_object_add_shared_fence(struct reservation_object *obj,
|
||||
|
||||
dma_fence_get(fence);
|
||||
|
||||
reservation_object_assert_held(obj);
|
||||
|
||||
fobj = reservation_object_get_list(obj);
|
||||
count = fobj->shared_count;
|
||||
|
||||
@ -196,6 +200,8 @@ void reservation_object_add_excl_fence(struct reservation_object *obj,
|
||||
struct reservation_object_list *old;
|
||||
u32 i = 0;
|
||||
|
||||
reservation_object_assert_held(obj);
|
||||
|
||||
old = reservation_object_get_list(obj);
|
||||
if (old)
|
||||
i = old->shared_count;
|
||||
@ -236,6 +242,8 @@ int reservation_object_copy_fences(struct reservation_object *dst,
|
||||
size_t size;
|
||||
unsigned i;
|
||||
|
||||
reservation_object_assert_held(dst);
|
||||
|
||||
rcu_read_lock();
|
||||
src_list = rcu_dereference(src->fence);
|
||||
|
||||
|
@ -173,6 +173,12 @@ config DRM_KMS_CMA_HELPER
|
||||
help
|
||||
Choose this if you need the KMS CMA helper functions
|
||||
|
||||
config DRM_GEM_SHMEM_HELPER
|
||||
bool
|
||||
depends on DRM
|
||||
help
|
||||
Choose this if you need the GEM shmem helper functions
|
||||
|
||||
config DRM_VM
|
||||
bool
|
||||
depends on DRM && MMU
|
||||
@ -329,6 +335,8 @@ source "drivers/gpu/drm/tve200/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/xen/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/vboxvideo/Kconfig"
|
||||
|
||||
# Keep legacy drivers last
|
||||
|
||||
menuconfig DRM_LEGACY
|
||||
|
@ -25,6 +25,7 @@ drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
|
||||
drm-$(CONFIG_DRM_VM) += drm_vm.o
|
||||
drm-$(CONFIG_COMPAT) += drm_ioc32.o
|
||||
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
|
||||
drm-$(CONFIG_DRM_GEM_SHMEM_HELPER) += drm_gem_shmem_helper.o
|
||||
drm-$(CONFIG_PCI) += ati_pcigart.o
|
||||
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
|
||||
drm-$(CONFIG_OF) += drm_of.o
|
||||
@ -109,3 +110,4 @@ obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
|
||||
obj-$(CONFIG_DRM_PL111) += pl111/
|
||||
obj-$(CONFIG_DRM_TVE200) += tve200/
|
||||
obj-$(CONFIG_DRM_XEN) += xen/
|
||||
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
|
||||
|
@ -974,6 +974,7 @@ amdgpu_pci_remove(struct pci_dev *pdev)
|
||||
|
||||
DRM_ERROR("Device removal is currently not supported outside of fbcon\n");
|
||||
drm_dev_unplug(dev);
|
||||
drm_dev_put(dev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
}
|
||||
|
@ -886,7 +886,7 @@ static int gmc_v6_0_sw_init(void *handle)
|
||||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
dev_warn(adev->dev, "amdgpu: No coherent DMA available.\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
adev->need_swiotlb = drm_need_swiotlb(dma_bits);
|
||||
|
||||
r = gmc_v6_0_init_microcode(adev);
|
||||
if (r) {
|
||||
|
@ -1030,7 +1030,7 @@ static int gmc_v7_0_sw_init(void *handle)
|
||||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
pr_warn("amdgpu: No coherent DMA available\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
adev->need_swiotlb = drm_need_swiotlb(dma_bits);
|
||||
|
||||
r = gmc_v7_0_init_microcode(adev);
|
||||
if (r) {
|
||||
|
@ -1155,7 +1155,7 @@ static int gmc_v8_0_sw_init(void *handle)
|
||||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
pr_warn("amdgpu: No coherent DMA available\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
adev->need_swiotlb = drm_need_swiotlb(dma_bits);
|
||||
|
||||
r = gmc_v8_0_init_microcode(adev);
|
||||
if (r) {
|
||||
|
@ -1011,7 +1011,7 @@ static int gmc_v9_0_sw_init(void *handle)
|
||||
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
|
||||
printk(KERN_WARNING "amdgpu: No coherent DMA available.\n");
|
||||
}
|
||||
adev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
adev->need_swiotlb = drm_need_swiotlb(dma_bits);
|
||||
|
||||
if (adev->gmc.xgmi.supported) {
|
||||
r = gfxhub_v1_1_get_xgmi_info(adev);
|
||||
|
@ -264,37 +264,17 @@ static bool
|
||||
malidp_verify_afbc_framebuffer_caps(struct drm_device *dev,
|
||||
const struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
const struct drm_format_info *info;
|
||||
|
||||
if ((mode_cmd->modifier[0] >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) {
|
||||
DRM_DEBUG_KMS("Unknown modifier (not Arm)\n");
|
||||
if (malidp_format_mod_supported(dev, mode_cmd->pixel_format,
|
||||
mode_cmd->modifier[0]) == false)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode_cmd->modifier[0] &
|
||||
~DRM_FORMAT_MOD_ARM_AFBC(AFBC_MOD_VALID_BITS)) {
|
||||
DRM_DEBUG_KMS("Unsupported modifiers\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
info = drm_get_format_info(dev, mode_cmd);
|
||||
if (!info) {
|
||||
DRM_DEBUG_KMS("Unable to get the format information\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info->num_planes != 1) {
|
||||
DRM_DEBUG_KMS("AFBC buffers expect one plane\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode_cmd->offsets[0] != 0) {
|
||||
DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
|
||||
case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
|
||||
switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) {
|
||||
case AFBC_SIZE_16X16:
|
||||
if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) {
|
||||
DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n");
|
||||
return false;
|
||||
@ -318,9 +298,10 @@ malidp_verify_afbc_framebuffer_size(struct drm_device *dev,
|
||||
struct drm_gem_object *objs = NULL;
|
||||
u32 afbc_superblock_size = 0, afbc_superblock_height = 0;
|
||||
u32 afbc_superblock_width = 0, afbc_size = 0;
|
||||
int bpp = 0;
|
||||
|
||||
switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) {
|
||||
case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16:
|
||||
switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) {
|
||||
case AFBC_SIZE_16X16:
|
||||
afbc_superblock_height = 16;
|
||||
afbc_superblock_width = 16;
|
||||
break;
|
||||
@ -334,15 +315,19 @@ malidp_verify_afbc_framebuffer_size(struct drm_device *dev,
|
||||
n_superblocks = (mode_cmd->width / afbc_superblock_width) *
|
||||
(mode_cmd->height / afbc_superblock_height);
|
||||
|
||||
afbc_superblock_size = info->cpp[0] * afbc_superblock_width *
|
||||
afbc_superblock_height;
|
||||
bpp = malidp_format_get_bpp(info->format);
|
||||
|
||||
afbc_superblock_size = (bpp * afbc_superblock_width * afbc_superblock_height)
|
||||
/ BITS_PER_BYTE;
|
||||
|
||||
afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT);
|
||||
afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT);
|
||||
|
||||
if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) {
|
||||
DRM_DEBUG_KMS("Invalid value of pitch (=%u) should be same as width (=%u) * cpp (=%u)\n",
|
||||
mode_cmd->pitches[0], mode_cmd->width, info->cpp[0]);
|
||||
if ((mode_cmd->width * bpp) != (mode_cmd->pitches[0] * BITS_PER_BYTE)) {
|
||||
DRM_DEBUG_KMS("Invalid value of (pitch * BITS_PER_BYTE) (=%u) "
|
||||
"should be same as width (=%u) * bpp (=%u)\n",
|
||||
(mode_cmd->pitches[0] * BITS_PER_BYTE),
|
||||
mode_cmd->width, bpp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -406,6 +391,7 @@ static int malidp_init(struct drm_device *drm)
|
||||
drm->mode_config.max_height = hwdev->max_line_size;
|
||||
drm->mode_config.funcs = &malidp_mode_config_funcs;
|
||||
drm->mode_config.helper_private = &malidp_mode_config_helpers;
|
||||
drm->mode_config.allow_fb_modifiers = true;
|
||||
|
||||
ret = malidp_crtc_init(drm);
|
||||
if (ret)
|
||||
|
@ -90,6 +90,12 @@ struct malidp_crtc_state {
|
||||
int malidp_de_planes_init(struct drm_device *drm);
|
||||
int malidp_crtc_init(struct drm_device *drm);
|
||||
|
||||
bool malidp_hw_format_is_linear_only(u32 format);
|
||||
bool malidp_hw_format_is_afbc_only(u32 format);
|
||||
|
||||
bool malidp_format_mod_supported(struct drm_device *drm,
|
||||
u32 format, u64 modifier);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void malidp_error(struct malidp_drm *malidp,
|
||||
struct malidp_error_stats *error_stats, u32 status,
|
||||
|
@ -49,11 +49,19 @@ static const struct malidp_format_id malidp500_de_formats[] = {
|
||||
{ DRM_FORMAT_YUYV, DE_VIDEO1, 13 },
|
||||
{ DRM_FORMAT_NV12, DE_VIDEO1 | SE_MEMWRITE, 14 },
|
||||
{ DRM_FORMAT_YUV420, DE_VIDEO1, 15 },
|
||||
{ DRM_FORMAT_XYUV8888, DE_VIDEO1, 16 },
|
||||
/* These are supported with AFBC only */
|
||||
{ DRM_FORMAT_YUV420_8BIT, DE_VIDEO1, 14 },
|
||||
{ DRM_FORMAT_VUY888, DE_VIDEO1, 16 },
|
||||
{ DRM_FORMAT_VUY101010, DE_VIDEO1, 17 },
|
||||
{ DRM_FORMAT_YUV420_10BIT, DE_VIDEO1, 18 }
|
||||
};
|
||||
|
||||
#define MALIDP_ID(__group, __format) \
|
||||
((((__group) & 0x7) << 3) | ((__format) & 0x7))
|
||||
|
||||
#define AFBC_YUV_422_FORMAT_ID MALIDP_ID(5, 1)
|
||||
|
||||
#define MALIDP_COMMON_FORMATS \
|
||||
/* fourcc, layers supporting the format, internal id */ \
|
||||
{ DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 0) }, \
|
||||
@ -74,11 +82,25 @@ static const struct malidp_format_id malidp500_de_formats[] = {
|
||||
{ DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1) }, \
|
||||
{ DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2) }, \
|
||||
{ DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3) }, \
|
||||
/* This is only supported with linear modifier */ \
|
||||
{ DRM_FORMAT_XYUV8888, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 0) },\
|
||||
/* This is only supported with AFBC modifier */ \
|
||||
{ DRM_FORMAT_VUY888, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 0) }, \
|
||||
{ DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \
|
||||
/* This is only supported with linear modifier */ \
|
||||
{ DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \
|
||||
{ DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(5, 6) }, \
|
||||
/* This is only supported with AFBC modifier */ \
|
||||
{ DRM_FORMAT_YUV420_8BIT, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \
|
||||
{ DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }, \
|
||||
{ DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)}
|
||||
/* This is only supported with linear modifier */ \
|
||||
{ DRM_FORMAT_XVYU2101010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 0)}, \
|
||||
/* This is only supported with AFBC modifier */ \
|
||||
{ DRM_FORMAT_VUY101010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 0)}, \
|
||||
{ DRM_FORMAT_X0L2, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 6)}, \
|
||||
/* This is only supported with AFBC modifier */ \
|
||||
{ DRM_FORMAT_YUV420_10BIT, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 7)}, \
|
||||
{ DRM_FORMAT_P010, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(6, 7)}
|
||||
|
||||
static const struct malidp_format_id malidp550_de_formats[] = {
|
||||
MALIDP_COMMON_FORMATS,
|
||||
@ -94,11 +116,14 @@ static const struct malidp_layer malidp500_layers[] = {
|
||||
* yuv2rgb matrix offset, mmu control register offset, rotation_features
|
||||
*/
|
||||
{ DE_VIDEO1, MALIDP500_DE_LV_BASE, MALIDP500_DE_LV_PTR_BASE,
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP500_LV_YUV2RGB, 0, ROTATE_ANY },
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP500_LV_YUV2RGB, 0, ROTATE_ANY,
|
||||
MALIDP500_DE_LV_AD_CTRL },
|
||||
{ DE_GRAPHICS1, MALIDP500_DE_LG1_BASE, MALIDP500_DE_LG1_PTR_BASE,
|
||||
MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY },
|
||||
MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY,
|
||||
MALIDP500_DE_LG1_AD_CTRL },
|
||||
{ DE_GRAPHICS2, MALIDP500_DE_LG2_BASE, MALIDP500_DE_LG2_PTR_BASE,
|
||||
MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY },
|
||||
MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY,
|
||||
MALIDP500_DE_LG2_AD_CTRL },
|
||||
};
|
||||
|
||||
static const struct malidp_layer malidp550_layers[] = {
|
||||
@ -106,13 +131,16 @@ static const struct malidp_layer malidp550_layers[] = {
|
||||
* yuv2rgb matrix offset, mmu control register offset, rotation_features
|
||||
*/
|
||||
{ DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE,
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY },
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY,
|
||||
MALIDP550_DE_LV1_AD_CTRL },
|
||||
{ DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE,
|
||||
MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY },
|
||||
MALIDP_DE_LG_STRIDE, 0, 0, ROTATE_ANY,
|
||||
MALIDP550_DE_LG_AD_CTRL },
|
||||
{ DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE,
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY },
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB, 0, ROTATE_ANY,
|
||||
MALIDP550_DE_LV2_AD_CTRL },
|
||||
{ DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE,
|
||||
MALIDP550_DE_LS_R1_STRIDE, 0, 0, ROTATE_NONE },
|
||||
MALIDP550_DE_LS_R1_STRIDE, 0, 0, ROTATE_NONE, 0 },
|
||||
};
|
||||
|
||||
static const struct malidp_layer malidp650_layers[] = {
|
||||
@ -122,16 +150,44 @@ static const struct malidp_layer malidp650_layers[] = {
|
||||
*/
|
||||
{ DE_VIDEO1, MALIDP550_DE_LV1_BASE, MALIDP550_DE_LV1_PTR_BASE,
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB,
|
||||
MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY },
|
||||
MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY,
|
||||
MALIDP550_DE_LV1_AD_CTRL },
|
||||
{ DE_GRAPHICS1, MALIDP550_DE_LG_BASE, MALIDP550_DE_LG_PTR_BASE,
|
||||
MALIDP_DE_LG_STRIDE, 0, MALIDP650_DE_LG_MMU_CTRL,
|
||||
ROTATE_COMPRESSED },
|
||||
ROTATE_COMPRESSED, MALIDP550_DE_LG_AD_CTRL },
|
||||
{ DE_VIDEO2, MALIDP550_DE_LV2_BASE, MALIDP550_DE_LV2_PTR_BASE,
|
||||
MALIDP_DE_LV_STRIDE0, MALIDP550_LV_YUV2RGB,
|
||||
MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY },
|
||||
MALIDP650_DE_LV_MMU_CTRL, ROTATE_ANY,
|
||||
MALIDP550_DE_LV2_AD_CTRL },
|
||||
{ DE_SMART, MALIDP550_DE_LS_BASE, MALIDP550_DE_LS_PTR_BASE,
|
||||
MALIDP550_DE_LS_R1_STRIDE, 0, MALIDP650_DE_LS_MMU_CTRL,
|
||||
ROTATE_NONE },
|
||||
ROTATE_NONE, 0 },
|
||||
};
|
||||
|
||||
const u64 malidp_format_modifiers[] = {
|
||||
/* All RGB formats (except XRGB, RGBX, XBGR, BGRX) */
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR | AFBC_SPARSE),
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR),
|
||||
|
||||
/* All RGB formats > 16bpp (except XRGB, RGBX, XBGR, BGRX) */
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_YTR | AFBC_SPARSE | AFBC_SPLIT),
|
||||
|
||||
/* All 8 or 10 bit YUV 444 formats. */
|
||||
/* In DP550, 10 bit YUV 420 format also supported */
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_SPARSE | AFBC_SPLIT),
|
||||
|
||||
/* YUV 420, 422 P1 8 bit and YUV 444 8 bit/10 bit formats */
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_SPARSE),
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16),
|
||||
|
||||
/* YUV 420, 422 P1 8, 10 bit formats */
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_CBR | AFBC_SPARSE),
|
||||
DRM_FORMAT_MOD_ARM_AFBC(AFBC_SIZE_16X16 | AFBC_CBR),
|
||||
|
||||
/* All formats */
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
|
||||
DRM_FORMAT_MOD_INVALID
|
||||
};
|
||||
|
||||
#define SE_N_SCALING_COEFFS 96
|
||||
@ -324,14 +380,39 @@ static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode *
|
||||
malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
|
||||
}
|
||||
|
||||
static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt)
|
||||
int malidp_format_get_bpp(u32 fmt)
|
||||
{
|
||||
int bpp = drm_format_plane_cpp(fmt, 0) * 8;
|
||||
|
||||
if (bpp == 0) {
|
||||
switch (fmt) {
|
||||
case DRM_FORMAT_VUY101010:
|
||||
bpp = 30;
|
||||
case DRM_FORMAT_YUV420_10BIT:
|
||||
bpp = 15;
|
||||
break;
|
||||
case DRM_FORMAT_YUV420_8BIT:
|
||||
bpp = 12;
|
||||
break;
|
||||
default:
|
||||
bpp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return bpp;
|
||||
}
|
||||
|
||||
static int malidp500_rotmem_required(struct malidp_hw_device *hwdev, u16 w,
|
||||
u16 h, u32 fmt, bool has_modifier)
|
||||
{
|
||||
/*
|
||||
* Each layer needs enough rotation memory to fit 8 lines
|
||||
* worth of pixel data. Required size is then:
|
||||
* size = rotated_width * (bpp / 8) * 8;
|
||||
*/
|
||||
return w * drm_format_plane_cpp(fmt, 0) * 8;
|
||||
int bpp = malidp_format_get_bpp(fmt);
|
||||
|
||||
return w * bpp;
|
||||
}
|
||||
|
||||
static void malidp500_se_write_pp_coefftab(struct malidp_hw_device *hwdev,
|
||||
@ -609,9 +690,9 @@ static void malidp550_modeset(struct malidp_hw_device *hwdev, struct videomode *
|
||||
malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_ILACED, MALIDP_DE_DISPLAY_FUNC);
|
||||
}
|
||||
|
||||
static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt)
|
||||
static int malidpx50_get_bytes_per_column(u32 fmt)
|
||||
{
|
||||
u32 bytes_per_col;
|
||||
u32 bytes_per_column;
|
||||
|
||||
switch (fmt) {
|
||||
/* 8 lines at 4 bytes per pixel */
|
||||
@ -637,19 +718,77 @@ static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w, u16
|
||||
case DRM_FORMAT_UYVY:
|
||||
case DRM_FORMAT_YUYV:
|
||||
case DRM_FORMAT_X0L0:
|
||||
case DRM_FORMAT_X0L2:
|
||||
bytes_per_col = 32;
|
||||
bytes_per_column = 32;
|
||||
break;
|
||||
/* 16 lines at 1.5 bytes per pixel */
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_YUV420:
|
||||
bytes_per_col = 24;
|
||||
/* 8 lines at 3 bytes per pixel */
|
||||
case DRM_FORMAT_VUY888:
|
||||
/* 16 lines at 12 bits per pixel */
|
||||
case DRM_FORMAT_YUV420_8BIT:
|
||||
/* 8 lines at 3 bytes per pixel */
|
||||
case DRM_FORMAT_P010:
|
||||
bytes_per_column = 24;
|
||||
break;
|
||||
/* 8 lines at 30 bits per pixel */
|
||||
case DRM_FORMAT_VUY101010:
|
||||
/* 16 lines at 15 bits per pixel */
|
||||
case DRM_FORMAT_YUV420_10BIT:
|
||||
bytes_per_column = 30;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return w * bytes_per_col;
|
||||
return bytes_per_column;
|
||||
}
|
||||
|
||||
static int malidp550_rotmem_required(struct malidp_hw_device *hwdev, u16 w,
|
||||
u16 h, u32 fmt, bool has_modifier)
|
||||
{
|
||||
int bytes_per_column = 0;
|
||||
|
||||
switch (fmt) {
|
||||
/* 8 lines at 15 bits per pixel */
|
||||
case DRM_FORMAT_YUV420_10BIT:
|
||||
bytes_per_column = 15;
|
||||
break;
|
||||
/* Uncompressed YUV 420 10 bit single plane cannot be rotated */
|
||||
case DRM_FORMAT_X0L2:
|
||||
if (has_modifier)
|
||||
bytes_per_column = 8;
|
||||
else
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
bytes_per_column = malidpx50_get_bytes_per_column(fmt);
|
||||
}
|
||||
|
||||
if (bytes_per_column == -EINVAL)
|
||||
return bytes_per_column;
|
||||
|
||||
return w * bytes_per_column;
|
||||
}
|
||||
|
||||
static int malidp650_rotmem_required(struct malidp_hw_device *hwdev, u16 w,
|
||||
u16 h, u32 fmt, bool has_modifier)
|
||||
{
|
||||
int bytes_per_column = 0;
|
||||
|
||||
switch (fmt) {
|
||||
/* 16 lines at 2 bytes per pixel */
|
||||
case DRM_FORMAT_X0L2:
|
||||
bytes_per_column = 32;
|
||||
break;
|
||||
default:
|
||||
bytes_per_column = malidpx50_get_bytes_per_column(fmt);
|
||||
}
|
||||
|
||||
if (bytes_per_column == -EINVAL)
|
||||
return bytes_per_column;
|
||||
|
||||
return w * bytes_per_column;
|
||||
}
|
||||
|
||||
static int malidp550_se_set_scaling_coeffs(struct malidp_hw_device *hwdev,
|
||||
@ -838,7 +977,10 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
|
||||
.se_base = MALIDP550_SE_BASE,
|
||||
.dc_base = MALIDP550_DC_BASE,
|
||||
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
||||
.features = MALIDP_REGMAP_HAS_CLEARIRQ,
|
||||
.features = MALIDP_REGMAP_HAS_CLEARIRQ |
|
||||
MALIDP_DEVICE_AFBC_SUPPORT_SPLIT |
|
||||
MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT |
|
||||
MALIDP_DEVICE_AFBC_YUYV_USE_422_P2,
|
||||
.n_layers = ARRAY_SIZE(malidp550_layers),
|
||||
.layers = malidp550_layers,
|
||||
.de_irq_map = {
|
||||
@ -884,7 +1026,9 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
|
||||
.se_base = MALIDP550_SE_BASE,
|
||||
.dc_base = MALIDP550_DC_BASE,
|
||||
.out_depth_base = MALIDP550_DE_OUTPUT_DEPTH,
|
||||
.features = MALIDP_REGMAP_HAS_CLEARIRQ,
|
||||
.features = MALIDP_REGMAP_HAS_CLEARIRQ |
|
||||
MALIDP_DEVICE_AFBC_SUPPORT_SPLIT |
|
||||
MALIDP_DEVICE_AFBC_YUYV_USE_422_P2,
|
||||
.n_layers = ARRAY_SIZE(malidp650_layers),
|
||||
.layers = malidp650_layers,
|
||||
.de_irq_map = {
|
||||
@ -923,7 +1067,7 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
|
||||
.in_config_mode = malidp550_in_config_mode,
|
||||
.set_config_valid = malidp550_set_config_valid,
|
||||
.modeset = malidp550_modeset,
|
||||
.rotmem_required = malidp550_rotmem_required,
|
||||
.rotmem_required = malidp650_rotmem_required,
|
||||
.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
|
||||
.se_calc_mclk = malidp550_se_calc_mclk,
|
||||
.enable_memwrite = malidp550_enable_memwrite,
|
||||
@ -933,19 +1077,72 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
|
||||
};
|
||||
|
||||
u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
|
||||
u8 layer_id, u32 format)
|
||||
u8 layer_id, u32 format, bool has_modifier)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < map->n_pixel_formats; i++) {
|
||||
if (((map->pixel_formats[i].layer & layer_id) == layer_id) &&
|
||||
(map->pixel_formats[i].format == format))
|
||||
return map->pixel_formats[i].id;
|
||||
(map->pixel_formats[i].format == format)) {
|
||||
/*
|
||||
* In some DP550 and DP650, DRM_FORMAT_YUYV + AFBC modifier
|
||||
* is supported by a different h/w format id than
|
||||
* DRM_FORMAT_YUYV (only).
|
||||
*/
|
||||
if (format == DRM_FORMAT_YUYV &&
|
||||
(has_modifier) &&
|
||||
(map->features & MALIDP_DEVICE_AFBC_YUYV_USE_422_P2))
|
||||
return AFBC_YUV_422_FORMAT_ID;
|
||||
else
|
||||
return map->pixel_formats[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
return MALIDP_INVALID_FORMAT_ID;
|
||||
}
|
||||
|
||||
bool malidp_hw_format_is_linear_only(u32 format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_ARGB2101010:
|
||||
case DRM_FORMAT_RGBA1010102:
|
||||
case DRM_FORMAT_BGRA1010102:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_RGBX8888:
|
||||
case DRM_FORMAT_BGRX8888:
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_RGB565:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
case DRM_FORMAT_BGRA5551:
|
||||
case DRM_FORMAT_UYVY:
|
||||
case DRM_FORMAT_XYUV8888:
|
||||
case DRM_FORMAT_XVYU2101010:
|
||||
case DRM_FORMAT_X0L2:
|
||||
case DRM_FORMAT_X0L0:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool malidp_hw_format_is_afbc_only(u32 format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_VUY888:
|
||||
case DRM_FORMAT_VUY101010:
|
||||
case DRM_FORMAT_YUV420_8BIT:
|
||||
case DRM_FORMAT_YUV420_10BIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void malidp_hw_clear_irq(struct malidp_hw_device *hwdev, u8 block, u32 irq)
|
||||
{
|
||||
u32 base = malidp_get_block_base(hwdev, block);
|
||||
|
@ -70,6 +70,8 @@ struct malidp_layer {
|
||||
s16 yuv2rgb_offset; /* offset to the YUV->RGB matrix entries */
|
||||
u16 mmu_ctrl_offset; /* offset to the MMU control register */
|
||||
enum rotation_features rot; /* type of rotation supported */
|
||||
/* address offset for the AFBC decoder registers */
|
||||
u16 afbc_decoder_offset;
|
||||
};
|
||||
|
||||
enum malidp_scaling_coeff_set {
|
||||
@ -93,7 +95,10 @@ struct malidp_se_config {
|
||||
};
|
||||
|
||||
/* regmap features */
|
||||
#define MALIDP_REGMAP_HAS_CLEARIRQ (1 << 0)
|
||||
#define MALIDP_REGMAP_HAS_CLEARIRQ BIT(0)
|
||||
#define MALIDP_DEVICE_AFBC_SUPPORT_SPLIT BIT(1)
|
||||
#define MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT BIT(2)
|
||||
#define MALIDP_DEVICE_AFBC_YUYV_USE_422_P2 BIT(3)
|
||||
|
||||
struct malidp_hw_regmap {
|
||||
/* address offset of the DE register bank */
|
||||
@ -179,7 +184,8 @@ struct malidp_hw {
|
||||
* Calculate the required rotation memory given the active area
|
||||
* and the buffer format.
|
||||
*/
|
||||
int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h, u32 fmt);
|
||||
int (*rotmem_required)(struct malidp_hw_device *hwdev, u16 w, u16 h,
|
||||
u32 fmt, bool has_modifier);
|
||||
|
||||
int (*se_set_scaling_coeffs)(struct malidp_hw_device *hwdev,
|
||||
struct malidp_se_config *se_config,
|
||||
@ -319,7 +325,9 @@ int malidp_se_irq_init(struct drm_device *drm, int irq);
|
||||
void malidp_se_irq_fini(struct malidp_hw_device *hwdev);
|
||||
|
||||
u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map,
|
||||
u8 layer_id, u32 format);
|
||||
u8 layer_id, u32 format, bool has_modifier);
|
||||
|
||||
int malidp_format_get_bpp(u32 fmt);
|
||||
|
||||
static inline u8 malidp_hw_get_pitch_align(struct malidp_hw_device *hwdev, bool rotated)
|
||||
{
|
||||
@ -388,9 +396,18 @@ static inline void malidp_se_set_enh_coeffs(struct malidp_hw_device *hwdev)
|
||||
|
||||
#define MALIDP_GAMMA_LUT_SIZE 4096
|
||||
|
||||
#define AFBC_MOD_VALID_BITS (AFBC_FORMAT_MOD_BLOCK_SIZE_MASK | \
|
||||
AFBC_FORMAT_MOD_YTR | AFBC_FORMAT_MOD_SPLIT | \
|
||||
AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_CBR | \
|
||||
AFBC_FORMAT_MOD_TILED | AFBC_FORMAT_MOD_SC)
|
||||
#define AFBC_SIZE_MASK AFBC_FORMAT_MOD_BLOCK_SIZE_MASK
|
||||
#define AFBC_SIZE_16X16 AFBC_FORMAT_MOD_BLOCK_SIZE_16x16
|
||||
#define AFBC_YTR AFBC_FORMAT_MOD_YTR
|
||||
#define AFBC_SPARSE AFBC_FORMAT_MOD_SPARSE
|
||||
#define AFBC_CBR AFBC_FORMAT_MOD_CBR
|
||||
#define AFBC_SPLIT AFBC_FORMAT_MOD_SPLIT
|
||||
#define AFBC_TILED AFBC_FORMAT_MOD_TILED
|
||||
#define AFBC_SC AFBC_FORMAT_MOD_SC
|
||||
|
||||
#define AFBC_MOD_VALID_BITS (AFBC_SIZE_MASK | AFBC_YTR | AFBC_SPLIT | \
|
||||
AFBC_SPARSE | AFBC_CBR | AFBC_TILED | AFBC_SC)
|
||||
|
||||
extern const u64 malidp_format_modifiers[];
|
||||
|
||||
#endif /* __MALIDP_HW_H__ */
|
||||
|
@ -141,9 +141,14 @@ malidp_mw_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fb->modifier) {
|
||||
DRM_DEBUG_KMS("Writeback framebuffer does not support modifiers\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mw_state->format =
|
||||
malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE,
|
||||
fb->format->format);
|
||||
fb->format->format, !!fb->modifier);
|
||||
if (mw_state->format == MALIDP_INVALID_FORMAT_ID) {
|
||||
struct drm_format_name_buf format_name;
|
||||
|
||||
|
@ -52,6 +52,8 @@
|
||||
#define MALIDP550_LS_ENABLE 0x01c
|
||||
#define MALIDP550_LS_R1_IN_SIZE 0x020
|
||||
|
||||
#define MODIFIERS_COUNT_MAX 15
|
||||
|
||||
/*
|
||||
* This 4-entry look-up-table is used to determine the full 8-bit alpha value
|
||||
* for formats with 1- or 2-bit alpha channels.
|
||||
@ -145,6 +147,119 @@ static void malidp_plane_atomic_print_state(struct drm_printer *p,
|
||||
drm_printf(p, "\tmmu_prefetch_pgsize=%d\n", ms->mmu_prefetch_pgsize);
|
||||
}
|
||||
|
||||
bool malidp_format_mod_supported(struct drm_device *drm,
|
||||
u32 format, u64 modifier)
|
||||
{
|
||||
const struct drm_format_info *info;
|
||||
const u64 *modifiers;
|
||||
struct malidp_drm *malidp = drm->dev_private;
|
||||
const struct malidp_hw_regmap *map = &malidp->dev->hw->map;
|
||||
|
||||
if (WARN_ON(modifier == DRM_FORMAT_MOD_INVALID))
|
||||
return false;
|
||||
|
||||
/* Some pixel formats are supported without any modifier */
|
||||
if (modifier == DRM_FORMAT_MOD_LINEAR) {
|
||||
/*
|
||||
* However these pixel formats need to be supported with
|
||||
* modifiers only
|
||||
*/
|
||||
return !malidp_hw_format_is_afbc_only(format);
|
||||
}
|
||||
|
||||
if ((modifier >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) {
|
||||
DRM_ERROR("Unknown modifier (not Arm)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modifier &
|
||||
~DRM_FORMAT_MOD_ARM_AFBC(AFBC_MOD_VALID_BITS)) {
|
||||
DRM_DEBUG_KMS("Unsupported modifiers\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
modifiers = malidp_format_modifiers;
|
||||
|
||||
/* SPLIT buffers must use SPARSE layout */
|
||||
if (WARN_ON_ONCE((modifier & AFBC_SPLIT) && !(modifier & AFBC_SPARSE)))
|
||||
return false;
|
||||
|
||||
/* CBR only applies to YUV formats, where YTR should be always 0 */
|
||||
if (WARN_ON_ONCE((modifier & AFBC_CBR) && (modifier & AFBC_YTR)))
|
||||
return false;
|
||||
|
||||
while (*modifiers != DRM_FORMAT_MOD_INVALID) {
|
||||
if (*modifiers == modifier)
|
||||
break;
|
||||
|
||||
modifiers++;
|
||||
}
|
||||
|
||||
/* return false, if the modifier was not found */
|
||||
if (*modifiers == DRM_FORMAT_MOD_INVALID) {
|
||||
DRM_DEBUG_KMS("Unsupported modifier\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
info = drm_format_info(format);
|
||||
|
||||
if (info->num_planes != 1) {
|
||||
DRM_DEBUG_KMS("AFBC buffers expect one plane\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (malidp_hw_format_is_linear_only(format) == true) {
|
||||
DRM_DEBUG_KMS("Given format (0x%x) is supported is linear mode only\n",
|
||||
format);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* RGB formats need to provide YTR modifier and YUV formats should not
|
||||
* provide YTR modifier.
|
||||
*/
|
||||
if (!(info->is_yuv) != !!(modifier & AFBC_FORMAT_MOD_YTR)) {
|
||||
DRM_DEBUG_KMS("AFBC_FORMAT_MOD_YTR is %s for %s formats\n",
|
||||
info->is_yuv ? "disallowed" : "mandatory",
|
||||
info->is_yuv ? "YUV" : "RGB");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (modifier & AFBC_SPLIT) {
|
||||
if (!info->is_yuv) {
|
||||
if (drm_format_plane_cpp(format, 0) <= 2) {
|
||||
DRM_DEBUG_KMS("RGB formats <= 16bpp are not supported with SPLIT\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((drm_format_horz_chroma_subsampling(format) != 1) ||
|
||||
(drm_format_vert_chroma_subsampling(format) != 1)) {
|
||||
if (!(format == DRM_FORMAT_YUV420_10BIT &&
|
||||
(map->features & MALIDP_DEVICE_AFBC_YUV_420_10_SUPPORT_SPLIT))) {
|
||||
DRM_DEBUG_KMS("Formats which are sub-sampled should never be split\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modifier & AFBC_CBR) {
|
||||
if ((drm_format_horz_chroma_subsampling(format) == 1) ||
|
||||
(drm_format_vert_chroma_subsampling(format) == 1)) {
|
||||
DRM_DEBUG_KMS("Formats which are not sub-sampled should not have CBR set\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool malidp_format_mod_supported_per_plane(struct drm_plane *plane,
|
||||
u32 format, u64 modifier)
|
||||
{
|
||||
return malidp_format_mod_supported(plane->dev, format, modifier);
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs malidp_de_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
@ -153,6 +268,7 @@ static const struct drm_plane_funcs malidp_de_plane_funcs = {
|
||||
.atomic_duplicate_state = malidp_duplicate_plane_state,
|
||||
.atomic_destroy_state = malidp_destroy_plane_state,
|
||||
.atomic_print_state = malidp_plane_atomic_print_state,
|
||||
.format_mod_supported = malidp_format_mod_supported_per_plane,
|
||||
};
|
||||
|
||||
static int malidp_se_check_scaling(struct malidp_plane *mp,
|
||||
@ -406,8 +522,8 @@ static int malidp_de_plane_check(struct drm_plane *plane,
|
||||
fb = state->fb;
|
||||
|
||||
ms->format = malidp_hw_get_format_id(&mp->hwdev->hw->map,
|
||||
mp->layer->id,
|
||||
fb->format->format);
|
||||
mp->layer->id, fb->format->format,
|
||||
!!fb->modifier);
|
||||
if (ms->format == MALIDP_INVALID_FORMAT_ID)
|
||||
return -EINVAL;
|
||||
|
||||
@ -415,8 +531,8 @@ static int malidp_de_plane_check(struct drm_plane *plane,
|
||||
for (i = 0; i < ms->n_planes; i++) {
|
||||
u8 alignment = malidp_hw_get_pitch_align(mp->hwdev, rotated);
|
||||
|
||||
if ((fb->pitches[i] * drm_format_info_block_height(fb->format, i))
|
||||
& (alignment - 1)) {
|
||||
if (((fb->pitches[i] * drm_format_info_block_height(fb->format, i))
|
||||
& (alignment - 1)) && !(fb->modifier)) {
|
||||
DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
|
||||
fb->pitches[i], i);
|
||||
return -EINVAL;
|
||||
@ -469,13 +585,20 @@ static int malidp_de_plane_check(struct drm_plane *plane,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* SMART layer does not support AFBC */
|
||||
if (mp->layer->id == DE_SMART && fb->modifier) {
|
||||
DRM_ERROR("AFBC framebuffer not supported in SMART layer");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ms->rotmem_size = 0;
|
||||
if (state->rotation & MALIDP_ROTATED_MASK) {
|
||||
int val;
|
||||
|
||||
val = mp->hwdev->hw->rotmem_required(mp->hwdev, state->crtc_w,
|
||||
state->crtc_h,
|
||||
fb->format->format);
|
||||
fb->format->format,
|
||||
!!(fb->modifier));
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
@ -592,6 +715,83 @@ static void malidp_de_set_mmu_control(struct malidp_plane *mp,
|
||||
mp->layer->base + mp->layer->mmu_ctrl_offset);
|
||||
}
|
||||
|
||||
static void malidp_set_plane_base_addr(struct drm_framebuffer *fb,
|
||||
struct malidp_plane *mp,
|
||||
int plane_index)
|
||||
{
|
||||
dma_addr_t paddr;
|
||||
u16 ptr;
|
||||
struct drm_plane *plane = &mp->base;
|
||||
bool afbc = fb->modifier ? true : false;
|
||||
|
||||
ptr = mp->layer->ptr + (plane_index << 4);
|
||||
|
||||
/*
|
||||
* drm_fb_cma_get_gem_addr() alters the physical base address of the
|
||||
* framebuffer as per the plane's src_x, src_y co-ordinates (ie to
|
||||
* take care of source cropping).
|
||||
* For AFBC, this is not needed as the cropping is handled by _AD_CROP_H
|
||||
* and _AD_CROP_V registers.
|
||||
*/
|
||||
if (!afbc) {
|
||||
paddr = drm_fb_cma_get_gem_addr(fb, plane->state,
|
||||
plane_index);
|
||||
} else {
|
||||
struct drm_gem_cma_object *obj;
|
||||
|
||||
obj = drm_fb_cma_get_gem_obj(fb, plane_index);
|
||||
|
||||
if (WARN_ON(!obj))
|
||||
return;
|
||||
paddr = obj->paddr;
|
||||
}
|
||||
|
||||
malidp_hw_write(mp->hwdev, lower_32_bits(paddr), ptr);
|
||||
malidp_hw_write(mp->hwdev, upper_32_bits(paddr), ptr + 4);
|
||||
}
|
||||
|
||||
static void malidp_de_set_plane_afbc(struct drm_plane *plane)
|
||||
{
|
||||
struct malidp_plane *mp;
|
||||
u32 src_w, src_h, val = 0, src_x, src_y;
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
|
||||
mp = to_malidp_plane(plane);
|
||||
|
||||
/* no afbc_decoder_offset means AFBC is not supported on this plane */
|
||||
if (!mp->layer->afbc_decoder_offset)
|
||||
return;
|
||||
|
||||
if (!fb->modifier) {
|
||||
malidp_hw_write(mp->hwdev, 0, mp->layer->afbc_decoder_offset);
|
||||
return;
|
||||
}
|
||||
|
||||
/* convert src values from Q16 fixed point to integer */
|
||||
src_w = plane->state->src_w >> 16;
|
||||
src_h = plane->state->src_h >> 16;
|
||||
src_x = plane->state->src_x >> 16;
|
||||
src_y = plane->state->src_y >> 16;
|
||||
|
||||
val = ((fb->width - (src_x + src_w)) << MALIDP_AD_CROP_RIGHT_OFFSET) |
|
||||
src_x;
|
||||
malidp_hw_write(mp->hwdev, val,
|
||||
mp->layer->afbc_decoder_offset + MALIDP_AD_CROP_H);
|
||||
|
||||
val = ((fb->height - (src_y + src_h)) << MALIDP_AD_CROP_BOTTOM_OFFSET) |
|
||||
src_y;
|
||||
malidp_hw_write(mp->hwdev, val,
|
||||
mp->layer->afbc_decoder_offset + MALIDP_AD_CROP_V);
|
||||
|
||||
val = MALIDP_AD_EN;
|
||||
if (fb->modifier & AFBC_FORMAT_MOD_SPLIT)
|
||||
val |= MALIDP_AD_BS;
|
||||
if (fb->modifier & AFBC_FORMAT_MOD_YTR)
|
||||
val |= MALIDP_AD_YTR;
|
||||
|
||||
malidp_hw_write(mp->hwdev, val, mp->layer->afbc_decoder_offset);
|
||||
}
|
||||
|
||||
static void malidp_de_plane_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
@ -602,12 +802,23 @@ static void malidp_de_plane_update(struct drm_plane *plane,
|
||||
u8 plane_alpha = state->alpha >> 8;
|
||||
u32 src_w, src_h, dest_w, dest_h, val;
|
||||
int i;
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
|
||||
mp = to_malidp_plane(plane);
|
||||
|
||||
/* convert src values from Q16 fixed point to integer */
|
||||
src_w = state->src_w >> 16;
|
||||
src_h = state->src_h >> 16;
|
||||
/*
|
||||
* For AFBC framebuffer, use the framebuffer width and height for
|
||||
* configuring layer input size register.
|
||||
*/
|
||||
if (fb->modifier) {
|
||||
src_w = fb->width;
|
||||
src_h = fb->height;
|
||||
} else {
|
||||
/* convert src values from Q16 fixed point to integer */
|
||||
src_w = state->src_w >> 16;
|
||||
src_h = state->src_h >> 16;
|
||||
}
|
||||
|
||||
dest_w = state->crtc_w;
|
||||
dest_h = state->crtc_h;
|
||||
|
||||
@ -615,15 +826,8 @@ static void malidp_de_plane_update(struct drm_plane *plane,
|
||||
val = (val & ~LAYER_FORMAT_MASK) | ms->format;
|
||||
malidp_hw_write(mp->hwdev, val, mp->layer->base);
|
||||
|
||||
for (i = 0; i < ms->n_planes; i++) {
|
||||
/* calculate the offset for the layer's plane registers */
|
||||
u16 ptr = mp->layer->ptr + (i << 4);
|
||||
dma_addr_t fb_addr = drm_fb_cma_get_gem_addr(state->fb,
|
||||
state, i);
|
||||
|
||||
malidp_hw_write(mp->hwdev, lower_32_bits(fb_addr), ptr);
|
||||
malidp_hw_write(mp->hwdev, upper_32_bits(fb_addr), ptr + 4);
|
||||
}
|
||||
for (i = 0; i < ms->n_planes; i++)
|
||||
malidp_set_plane_base_addr(fb, mp, i);
|
||||
|
||||
malidp_de_set_mmu_control(mp, ms);
|
||||
|
||||
@ -657,6 +861,8 @@ static void malidp_de_plane_update(struct drm_plane *plane,
|
||||
mp->layer->base + MALIDP550_LS_R1_IN_SIZE);
|
||||
}
|
||||
|
||||
malidp_de_set_plane_afbc(plane);
|
||||
|
||||
/* first clear the rotation bits */
|
||||
val = malidp_hw_read(mp->hwdev, mp->layer->base + MALIDP_LAYER_CONTROL);
|
||||
val &= ~LAYER_ROT_MASK;
|
||||
@ -733,7 +939,26 @@ int malidp_de_planes_init(struct drm_device *drm)
|
||||
BIT(DRM_MODE_BLEND_PREMULTI) |
|
||||
BIT(DRM_MODE_BLEND_COVERAGE);
|
||||
u32 *formats;
|
||||
int ret, i, j, n;
|
||||
int ret, i = 0, j = 0, n;
|
||||
u64 supported_modifiers[MODIFIERS_COUNT_MAX];
|
||||
const u64 *modifiers;
|
||||
|
||||
modifiers = malidp_format_modifiers;
|
||||
|
||||
if (!(map->features & MALIDP_DEVICE_AFBC_SUPPORT_SPLIT)) {
|
||||
/*
|
||||
* Since our hardware does not support SPLIT, so build the list
|
||||
* of supported modifiers excluding SPLIT ones.
|
||||
*/
|
||||
while (*modifiers != DRM_FORMAT_MOD_INVALID) {
|
||||
if (!(*modifiers & AFBC_SPLIT))
|
||||
supported_modifiers[j++] = *modifiers;
|
||||
|
||||
modifiers++;
|
||||
}
|
||||
supported_modifiers[j++] = DRM_FORMAT_MOD_INVALID;
|
||||
modifiers = supported_modifiers;
|
||||
}
|
||||
|
||||
formats = kcalloc(map->n_pixel_formats, sizeof(*formats), GFP_KERNEL);
|
||||
if (!formats) {
|
||||
@ -758,9 +983,15 @@ int malidp_de_planes_init(struct drm_device *drm)
|
||||
|
||||
plane_type = (i == 0) ? DRM_PLANE_TYPE_PRIMARY :
|
||||
DRM_PLANE_TYPE_OVERLAY;
|
||||
|
||||
/*
|
||||
* All the layers except smart layer supports AFBC modifiers.
|
||||
*/
|
||||
ret = drm_universal_plane_init(drm, &plane->base, crtcs,
|
||||
&malidp_de_plane_funcs, formats,
|
||||
n, NULL, plane_type, NULL);
|
||||
&malidp_de_plane_funcs, formats, n,
|
||||
(id == DE_SMART) ? NULL : modifiers, plane_type,
|
||||
NULL);
|
||||
|
||||
if (ret < 0)
|
||||
goto cleanup;
|
||||
|
||||
|
@ -198,10 +198,13 @@
|
||||
#define MALIDP500_LV_YUV2RGB ((s16)(-0xB8))
|
||||
#define MALIDP500_DE_LV_BASE 0x00100
|
||||
#define MALIDP500_DE_LV_PTR_BASE 0x00124
|
||||
#define MALIDP500_DE_LV_AD_CTRL 0x00400
|
||||
#define MALIDP500_DE_LG1_BASE 0x00200
|
||||
#define MALIDP500_DE_LG1_PTR_BASE 0x0021c
|
||||
#define MALIDP500_DE_LG1_AD_CTRL 0x0040c
|
||||
#define MALIDP500_DE_LG2_BASE 0x00300
|
||||
#define MALIDP500_DE_LG2_PTR_BASE 0x0031c
|
||||
#define MALIDP500_DE_LG2_AD_CTRL 0x00418
|
||||
#define MALIDP500_SE_BASE 0x00c00
|
||||
#define MALIDP500_SE_CONTROL 0x00c0c
|
||||
#define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c
|
||||
@ -228,10 +231,13 @@
|
||||
#define MALIDP550_LV_YUV2RGB 0x00084
|
||||
#define MALIDP550_DE_LV1_BASE 0x00100
|
||||
#define MALIDP550_DE_LV1_PTR_BASE 0x00124
|
||||
#define MALIDP550_DE_LV1_AD_CTRL 0x001B8
|
||||
#define MALIDP550_DE_LV2_BASE 0x00200
|
||||
#define MALIDP550_DE_LV2_PTR_BASE 0x00224
|
||||
#define MALIDP550_DE_LV2_AD_CTRL 0x002B8
|
||||
#define MALIDP550_DE_LG_BASE 0x00300
|
||||
#define MALIDP550_DE_LG_PTR_BASE 0x0031c
|
||||
#define MALIDP550_DE_LG_AD_CTRL 0x00330
|
||||
#define MALIDP550_DE_LS_BASE 0x00400
|
||||
#define MALIDP550_DE_LS_PTR_BASE 0x0042c
|
||||
#define MALIDP550_DE_PERF_BASE 0x00500
|
||||
@ -258,6 +264,20 @@
|
||||
#define MALIDP_MMU_CTRL_PX_PS(x) (1 << (8 + (x)))
|
||||
#define MALIDP_MMU_CTRL_PP_NUM_REQ(x) (((x) & 0x7f) << 12)
|
||||
|
||||
/* AFBC register offsets relative to MALIDPXXX_DE_LX_AD_CTRL */
|
||||
/* The following register offsets are common for DP500, DP550 and DP650 */
|
||||
#define MALIDP_AD_CROP_H 0x4
|
||||
#define MALIDP_AD_CROP_V 0x8
|
||||
#define MALIDP_AD_END_PTR_LOW 0xc
|
||||
#define MALIDP_AD_END_PTR_HIGH 0x10
|
||||
|
||||
/* AFBC decoder Registers */
|
||||
#define MALIDP_AD_EN BIT(0)
|
||||
#define MALIDP_AD_YTR BIT(4)
|
||||
#define MALIDP_AD_BS BIT(8)
|
||||
#define MALIDP_AD_CROP_RIGHT_OFFSET 16
|
||||
#define MALIDP_AD_CROP_BOTTOM_OFFSET 16
|
||||
|
||||
/*
|
||||
* Starting with DP550 the register map blocks has been standardised to the
|
||||
* following layout:
|
||||
|
@ -214,20 +214,9 @@ static enum drm_mode_status bochs_connector_mode_valid(struct drm_connector *con
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static struct drm_encoder *
|
||||
bochs_connector_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
/* pick the encoder ids */
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, NULL, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs bochs_connector_connector_helper_funcs = {
|
||||
.get_modes = bochs_connector_get_modes,
|
||||
.mode_valid = bochs_connector_mode_valid,
|
||||
.best_encoder = bochs_connector_best_encoder,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs bochs_connector_connector_funcs = {
|
||||
|
@ -1752,7 +1752,7 @@ int drm_atomic_helper_commit(struct drm_device *dev,
|
||||
*
|
||||
* NOTE: Commit work has multiple phases, first hardware commit, then
|
||||
* cleanup. We want them to overlap, hence need system_unbound_wq to
|
||||
* make sure work items don't artifically stall on each another.
|
||||
* make sure work items don't artificially stall on each another.
|
||||
*/
|
||||
|
||||
drm_atomic_state_get(state);
|
||||
@ -1786,7 +1786,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
|
||||
*
|
||||
* Asynchronous workers need to have sufficient parallelism to be able to run
|
||||
* different atomic commits on different CRTCs in parallel. The simplest way to
|
||||
* achive this is by running them on the &system_unbound_wq work queue. Note
|
||||
* achieve this is by running them on the &system_unbound_wq work queue. Note
|
||||
* that drivers are not required to split up atomic commits and run an
|
||||
* individual commit in parallel - userspace is supposed to do that if it cares.
|
||||
* But it might be beneficial to do that for modesets, since those necessarily
|
||||
|
@ -733,6 +733,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
|
||||
return -EINVAL;
|
||||
}
|
||||
state->content_protection = val;
|
||||
} else if (property == connector->colorspace_property) {
|
||||
state->colorspace = val;
|
||||
} else if (property == config->writeback_fb_id_property) {
|
||||
struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val);
|
||||
int ret = drm_atomic_set_writeback_fb_for_connector(state, fb);
|
||||
@ -801,6 +803,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
|
||||
*val = state->picture_aspect_ratio;
|
||||
} else if (property == config->content_type_property) {
|
||||
*val = state->content_type;
|
||||
} else if (property == connector->colorspace_property) {
|
||||
*val = state->colorspace;
|
||||
} else if (property == connector->scaling_mode_property) {
|
||||
*val = state->scaling_mode;
|
||||
} else if (property == connector->content_protection_property) {
|
||||
|
@ -245,6 +245,7 @@ int drm_connector_init(struct drm_device *dev,
|
||||
INIT_LIST_HEAD(&connector->modes);
|
||||
mutex_init(&connector->mutex);
|
||||
connector->edid_blob_ptr = NULL;
|
||||
connector->tile_blob_ptr = NULL;
|
||||
connector->status = connector_status_unknown;
|
||||
connector->display_info.panel_orientation =
|
||||
DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
|
||||
@ -272,6 +273,9 @@ int drm_connector_init(struct drm_device *dev,
|
||||
drm_object_attach_property(&connector->base,
|
||||
config->non_desktop_property,
|
||||
0);
|
||||
drm_object_attach_property(&connector->base,
|
||||
config->tile_property,
|
||||
0);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
|
||||
drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
|
||||
@ -826,6 +830,33 @@ static struct drm_prop_enum_list drm_cp_enum_list[] = {
|
||||
};
|
||||
DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
|
||||
|
||||
static const struct drm_prop_enum_list hdmi_colorspaces[] = {
|
||||
/* For Default case, driver will set the colorspace */
|
||||
{ DRM_MODE_COLORIMETRY_DEFAULT, "Default" },
|
||||
/* Standard Definition Colorimetry based on CEA 861 */
|
||||
{ DRM_MODE_COLORIMETRY_SMPTE_170M_YCC, "SMPTE_170M_YCC" },
|
||||
{ DRM_MODE_COLORIMETRY_BT709_YCC, "BT709_YCC" },
|
||||
/* Standard Definition Colorimetry based on IEC 61966-2-4 */
|
||||
{ DRM_MODE_COLORIMETRY_XVYCC_601, "XVYCC_601" },
|
||||
/* High Definition Colorimetry based on IEC 61966-2-4 */
|
||||
{ DRM_MODE_COLORIMETRY_XVYCC_709, "XVYCC_709" },
|
||||
/* Colorimetry based on IEC 61966-2-1/Amendment 1 */
|
||||
{ DRM_MODE_COLORIMETRY_SYCC_601, "SYCC_601" },
|
||||
/* Colorimetry based on IEC 61966-2-5 [33] */
|
||||
{ DRM_MODE_COLORIMETRY_OPYCC_601, "opYCC_601" },
|
||||
/* Colorimetry based on IEC 61966-2-5 */
|
||||
{ DRM_MODE_COLORIMETRY_OPRGB, "opRGB" },
|
||||
/* Colorimetry based on ITU-R BT.2020 */
|
||||
{ DRM_MODE_COLORIMETRY_BT2020_CYCC, "BT2020_CYCC" },
|
||||
/* Colorimetry based on ITU-R BT.2020 */
|
||||
{ DRM_MODE_COLORIMETRY_BT2020_RGB, "BT2020_RGB" },
|
||||
/* Colorimetry based on ITU-R BT.2020 */
|
||||
{ DRM_MODE_COLORIMETRY_BT2020_YCC, "BT2020_YCC" },
|
||||
/* Added as part of Additional Colorimetry Extension in 861.G */
|
||||
{ DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65, "DCI-P3_RGB_D65" },
|
||||
{ DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER, "DCI-P3_RGB_Theater" },
|
||||
};
|
||||
|
||||
/**
|
||||
* DOC: standard connector properties
|
||||
*
|
||||
@ -1547,6 +1578,57 @@ int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
|
||||
|
||||
/**
|
||||
* DOC: standard connector properties
|
||||
*
|
||||
* Colorspace:
|
||||
* drm_mode_create_colorspace_property - create colorspace property
|
||||
* This property helps select a suitable colorspace based on the sink
|
||||
* capability. Modern sink devices support wider gamut like BT2020.
|
||||
* This helps switch to BT2020 mode if the BT2020 encoded video stream
|
||||
* is being played by the user, same for any other colorspace. Thereby
|
||||
* giving a good visual experience to users.
|
||||
*
|
||||
* The expectation from userspace is that it should parse the EDID
|
||||
* and get supported colorspaces. Use this property and switch to the
|
||||
* one supported. Sink supported colorspaces should be retrieved by
|
||||
* userspace from EDID and driver will not explicitly expose them.
|
||||
*
|
||||
* Basically the expectation from userspace is:
|
||||
* - Set up CRTC DEGAMMA/CTM/GAMMA to convert to some sink
|
||||
* colorspace
|
||||
* - Set this new property to let the sink know what it
|
||||
* converted the CRTC output to.
|
||||
* - This property is just to inform sink what colorspace
|
||||
* source is trying to drive.
|
||||
*
|
||||
* Called by a driver the first time it's needed, must be attached to desired
|
||||
* connectors.
|
||||
*/
|
||||
int drm_mode_create_colorspace_property(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_property *prop;
|
||||
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
|
||||
connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) {
|
||||
prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
|
||||
"Colorspace",
|
||||
hdmi_colorspaces,
|
||||
ARRAY_SIZE(hdmi_colorspaces));
|
||||
if (!prop)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
DRM_DEBUG_KMS("Colorspace property not supported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
connector->colorspace_property = prop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_create_colorspace_property);
|
||||
|
||||
/**
|
||||
* drm_mode_create_content_type_property - create content type property
|
||||
* @dev: DRM device
|
||||
@ -1634,6 +1716,8 @@ EXPORT_SYMBOL(drm_connector_set_path_property);
|
||||
* This looks up the tile information for a connector, and creates a
|
||||
* property for userspace to parse if it exists. The property is of
|
||||
* the form of 8 integers using ':' as a separator.
|
||||
* This is used for dual port tiled displays with DisplayPort SST
|
||||
* or DisplayPort MST connectors.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, errno on failure.
|
||||
@ -1677,6 +1761,9 @@ EXPORT_SYMBOL(drm_connector_set_tile_property);
|
||||
*
|
||||
* This function creates a new blob modeset object and assigns its id to the
|
||||
* connector's edid property.
|
||||
* Since we also parse tile information from EDID's displayID block, we also
|
||||
* set the connector's tile property here. See drm_connector_set_tile_property()
|
||||
* for more details.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative errno on failure.
|
||||
@ -1718,7 +1805,9 @@ int drm_connector_update_edid_property(struct drm_connector *connector,
|
||||
edid,
|
||||
&connector->base,
|
||||
dev->mode_config.edid_property);
|
||||
return ret;
|
||||
if (ret)
|
||||
return ret;
|
||||
return drm_connector_set_tile_property(connector);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_connector_update_edid_property);
|
||||
|
||||
|
@ -3022,7 +3022,6 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
|
||||
edid = drm_edid_duplicate(port->cached_edid);
|
||||
else {
|
||||
edid = drm_get_edid(connector, &port->aux.ddc);
|
||||
drm_connector_set_tile_property(connector);
|
||||
}
|
||||
port->has_audio = drm_detect_monitor_audio(edid);
|
||||
drm_dp_mst_topology_put_port(port);
|
||||
|
@ -286,6 +286,138 @@ void drm_minor_release(struct drm_minor *minor)
|
||||
* Note that the lifetime rules for &drm_device instance has still a lot of
|
||||
* historical baggage. Hence use the reference counting provided by
|
||||
* drm_dev_get() and drm_dev_put() only carefully.
|
||||
*
|
||||
* Display driver example
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* The following example shows a typical structure of a DRM display driver.
|
||||
* The example focus on the probe() function and the other functions that is
|
||||
* almost always present and serves as a demonstration of devm_drm_dev_init()
|
||||
* usage with its accompanying drm_driver->release callback.
|
||||
*
|
||||
* .. code-block:: c
|
||||
*
|
||||
* struct driver_device {
|
||||
* struct drm_device drm;
|
||||
* void *userspace_facing;
|
||||
* struct clk *pclk;
|
||||
* };
|
||||
*
|
||||
* static void driver_drm_release(struct drm_device *drm)
|
||||
* {
|
||||
* struct driver_device *priv = container_of(...);
|
||||
*
|
||||
* drm_mode_config_cleanup(drm);
|
||||
* drm_dev_fini(drm);
|
||||
* kfree(priv->userspace_facing);
|
||||
* kfree(priv);
|
||||
* }
|
||||
*
|
||||
* static struct drm_driver driver_drm_driver = {
|
||||
* [...]
|
||||
* .release = driver_drm_release,
|
||||
* };
|
||||
*
|
||||
* static int driver_probe(struct platform_device *pdev)
|
||||
* {
|
||||
* struct driver_device *priv;
|
||||
* struct drm_device *drm;
|
||||
* int ret;
|
||||
*
|
||||
* [
|
||||
* devm_kzalloc() can't be used here because the drm_device
|
||||
* lifetime can exceed the device lifetime if driver unbind
|
||||
* happens when userspace still has open file descriptors.
|
||||
* ]
|
||||
* priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
* if (!priv)
|
||||
* return -ENOMEM;
|
||||
*
|
||||
* drm = &priv->drm;
|
||||
*
|
||||
* ret = devm_drm_dev_init(&pdev->dev, drm, &driver_drm_driver);
|
||||
* if (ret) {
|
||||
* kfree(drm);
|
||||
* return ret;
|
||||
* }
|
||||
*
|
||||
* drm_mode_config_init(drm);
|
||||
*
|
||||
* priv->userspace_facing = kzalloc(..., GFP_KERNEL);
|
||||
* if (!priv->userspace_facing)
|
||||
* return -ENOMEM;
|
||||
*
|
||||
* priv->pclk = devm_clk_get(dev, "PCLK");
|
||||
* if (IS_ERR(priv->pclk))
|
||||
* return PTR_ERR(priv->pclk);
|
||||
*
|
||||
* [ Further setup, display pipeline etc ]
|
||||
*
|
||||
* platform_set_drvdata(pdev, drm);
|
||||
*
|
||||
* drm_mode_config_reset(drm);
|
||||
*
|
||||
* ret = drm_dev_register(drm);
|
||||
* if (ret)
|
||||
* return ret;
|
||||
*
|
||||
* drm_fbdev_generic_setup(drm, 32);
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* [ This function is called before the devm_ resources are released ]
|
||||
* static int driver_remove(struct platform_device *pdev)
|
||||
* {
|
||||
* struct drm_device *drm = platform_get_drvdata(pdev);
|
||||
*
|
||||
* drm_dev_unregister(drm);
|
||||
* drm_atomic_helper_shutdown(drm)
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* [ This function is called on kernel restart and shutdown ]
|
||||
* static void driver_shutdown(struct platform_device *pdev)
|
||||
* {
|
||||
* drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
|
||||
* }
|
||||
*
|
||||
* static int __maybe_unused driver_pm_suspend(struct device *dev)
|
||||
* {
|
||||
* return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
|
||||
* }
|
||||
*
|
||||
* static int __maybe_unused driver_pm_resume(struct device *dev)
|
||||
* {
|
||||
* drm_mode_config_helper_resume(dev_get_drvdata(dev));
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* static const struct dev_pm_ops driver_pm_ops = {
|
||||
* SET_SYSTEM_SLEEP_PM_OPS(driver_pm_suspend, driver_pm_resume)
|
||||
* };
|
||||
*
|
||||
* static struct platform_driver driver_driver = {
|
||||
* .driver = {
|
||||
* [...]
|
||||
* .pm = &driver_pm_ops,
|
||||
* },
|
||||
* .probe = driver_probe,
|
||||
* .remove = driver_remove,
|
||||
* .shutdown = driver_shutdown,
|
||||
* };
|
||||
* module_platform_driver(driver_driver);
|
||||
*
|
||||
* Drivers that want to support device unplugging (USB, DT overlay unload) should
|
||||
* use drm_dev_unplug() instead of drm_dev_unregister(). The driver must protect
|
||||
* regions that is accessing device resources to prevent use after they're
|
||||
* released. This is done using drm_dev_enter() and drm_dev_exit(). There is one
|
||||
* shortcoming however, drm_dev_unplug() marks the drm_device as unplugged before
|
||||
* drm_atomic_helper_shutdown() is called. This means that if the disable code
|
||||
* paths are protected, they will not run on regular driver module unload,
|
||||
* possibily leaving the hardware enabled.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -376,11 +508,6 @@ void drm_dev_unplug(struct drm_device *dev)
|
||||
synchronize_srcu(&drm_unplug_srcu);
|
||||
|
||||
drm_dev_unregister(dev);
|
||||
|
||||
mutex_lock(&drm_global_mutex);
|
||||
if (dev->open_count == 0)
|
||||
drm_dev_put(dev);
|
||||
mutex_unlock(&drm_global_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dev_unplug);
|
||||
|
||||
@ -456,6 +583,31 @@ static void drm_fs_inode_free(struct inode *inode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: component helper usage recommendations
|
||||
*
|
||||
* DRM drivers that drive hardware where a logical device consists of a pile of
|
||||
* independent hardware blocks are recommended to use the :ref:`component helper
|
||||
* library<component>`. For consistency and better options for code reuse the
|
||||
* following guidelines apply:
|
||||
*
|
||||
* - The entire device initialization procedure should be run from the
|
||||
* &component_master_ops.master_bind callback, starting with drm_dev_init(),
|
||||
* then binding all components with component_bind_all() and finishing with
|
||||
* drm_dev_register().
|
||||
*
|
||||
* - The opaque pointer passed to all components through component_bind_all()
|
||||
* should point at &struct drm_device of the device instance, not some driver
|
||||
* specific private structure.
|
||||
*
|
||||
* - The component helper fills the niche where further standardization of
|
||||
* interfaces is not practical. When there already is, or will be, a
|
||||
* standardized interface like &drm_bridge or &drm_panel, providing its own
|
||||
* functions to find such components at driver load time, like
|
||||
* drm_of_find_panel_or_bridge(), then the component helper should not be
|
||||
* used.
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_dev_init - Initialise new DRM device
|
||||
* @dev: DRM device
|
||||
@ -501,7 +653,7 @@ int drm_dev_init(struct drm_device *dev,
|
||||
BUG_ON(!parent);
|
||||
|
||||
kref_init(&dev->ref);
|
||||
dev->dev = parent;
|
||||
dev->dev = get_device(parent);
|
||||
dev->driver = driver;
|
||||
|
||||
/* no per-device feature limits by default */
|
||||
@ -571,6 +723,7 @@ err_minors:
|
||||
drm_minor_free(dev, DRM_MINOR_RENDER);
|
||||
drm_fs_inode_free(dev->anon_inode);
|
||||
err_free:
|
||||
put_device(dev->dev);
|
||||
mutex_destroy(&dev->master_mutex);
|
||||
mutex_destroy(&dev->ctxlist_mutex);
|
||||
mutex_destroy(&dev->clientlist_mutex);
|
||||
@ -580,6 +733,45 @@ err_free:
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dev_init);
|
||||
|
||||
static void devm_drm_dev_init_release(void *data)
|
||||
{
|
||||
drm_dev_put(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_drm_dev_init - Resource managed drm_dev_init()
|
||||
* @parent: Parent device object
|
||||
* @dev: DRM device
|
||||
* @driver: DRM driver
|
||||
*
|
||||
* Managed drm_dev_init(). The DRM device initialized with this function is
|
||||
* automatically put on driver detach using drm_dev_put(). You must supply a
|
||||
* &drm_driver.release callback to control the finalization explicitly.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, or error code on failure.
|
||||
*/
|
||||
int devm_drm_dev_init(struct device *parent,
|
||||
struct drm_device *dev,
|
||||
struct drm_driver *driver)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!parent || !driver->release))
|
||||
return -EINVAL;
|
||||
|
||||
ret = drm_dev_init(dev, driver, parent);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
|
||||
if (ret)
|
||||
devm_drm_dev_init_release(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_drm_dev_init);
|
||||
|
||||
/**
|
||||
* drm_dev_fini - Finalize a dead DRM device
|
||||
* @dev: DRM device
|
||||
@ -606,6 +798,8 @@ void drm_dev_fini(struct drm_device *dev)
|
||||
drm_minor_free(dev, DRM_MINOR_PRIMARY);
|
||||
drm_minor_free(dev, DRM_MINOR_RENDER);
|
||||
|
||||
put_device(dev->dev);
|
||||
|
||||
mutex_destroy(&dev->master_mutex);
|
||||
mutex_destroy(&dev->ctxlist_mutex);
|
||||
mutex_destroy(&dev->clientlist_mutex);
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_dsc.h>
|
||||
|
||||
@ -31,75 +32,74 @@
|
||||
/**
|
||||
* drm_dsc_dp_pps_header_init() - Initializes the PPS Header
|
||||
* for DisplayPort as per the DP 1.4 spec.
|
||||
* @pps_sdp: Secondary data packet for DSC Picture Parameter Set
|
||||
* as defined in &struct drm_dsc_pps_infoframe
|
||||
* @pps_header: Secondary data packet header for DSC Picture
|
||||
* Parameter Set as defined in &struct dp_sdp_header
|
||||
*
|
||||
* DP 1.4 spec defines the secondary data packet for sending the
|
||||
* picture parameter infoframes from the source to the sink.
|
||||
* This function populates the pps header defined in
|
||||
* &struct drm_dsc_pps_infoframe as per the header bytes defined
|
||||
* in &struct dp_sdp_header.
|
||||
* This function populates the SDP header defined in
|
||||
* &struct dp_sdp_header.
|
||||
*/
|
||||
void drm_dsc_dp_pps_header_init(struct drm_dsc_pps_infoframe *pps_sdp)
|
||||
void drm_dsc_dp_pps_header_init(struct dp_sdp_header *pps_header)
|
||||
{
|
||||
memset(&pps_sdp->pps_header, 0, sizeof(pps_sdp->pps_header));
|
||||
memset(pps_header, 0, sizeof(*pps_header));
|
||||
|
||||
pps_sdp->pps_header.HB1 = DP_SDP_PPS;
|
||||
pps_sdp->pps_header.HB2 = DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1;
|
||||
pps_header->HB1 = DP_SDP_PPS;
|
||||
pps_header->HB2 = DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dsc_dp_pps_header_init);
|
||||
|
||||
/**
|
||||
* drm_dsc_pps_infoframe_pack() - Populates the DSC PPS infoframe
|
||||
* drm_dsc_pps_payload_pack() - Populates the DSC PPS
|
||||
*
|
||||
* @pps_sdp:
|
||||
* Secondary data packet for DSC Picture Parameter Set. This is defined
|
||||
* by &struct drm_dsc_pps_infoframe
|
||||
* @pps_payload:
|
||||
* Bitwise struct for DSC Picture Parameter Set. This is defined
|
||||
* by &struct drm_dsc_picture_parameter_set
|
||||
* @dsc_cfg:
|
||||
* DSC Configuration data filled by driver as defined by
|
||||
* &struct drm_dsc_config
|
||||
*
|
||||
* DSC source device sends a secondary data packet filled with all the
|
||||
* picture parameter set (PPS) information required by the sink to decode
|
||||
* the compressed frame. Driver populates the dsC PPS infoframe using the DSC
|
||||
* configuration parameters in the order expected by the DSC Display Sink
|
||||
* device. For the DSC, the sink device expects the PPS payload in the big
|
||||
* endian format for the fields that span more than 1 byte.
|
||||
* DSC source device sends a picture parameter set (PPS) containing the
|
||||
* information required by the sink to decode the compressed frame. Driver
|
||||
* populates the DSC PPS struct using the DSC configuration parameters in
|
||||
* the order expected by the DSC Display Sink device. For the DSC, the sink
|
||||
* device expects the PPS payload in big endian format for fields
|
||||
* that span more than 1 byte.
|
||||
*/
|
||||
void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp,
|
||||
void drm_dsc_pps_payload_pack(struct drm_dsc_picture_parameter_set *pps_payload,
|
||||
const struct drm_dsc_config *dsc_cfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Protect against someone accidently changing struct size */
|
||||
BUILD_BUG_ON(sizeof(pps_sdp->pps_payload) !=
|
||||
BUILD_BUG_ON(sizeof(*pps_payload) !=
|
||||
DP_SDP_PPS_HEADER_PAYLOAD_BYTES_MINUS_1 + 1);
|
||||
|
||||
memset(&pps_sdp->pps_payload, 0, sizeof(pps_sdp->pps_payload));
|
||||
memset(pps_payload, 0, sizeof(*pps_payload));
|
||||
|
||||
/* PPS 0 */
|
||||
pps_sdp->pps_payload.dsc_version =
|
||||
pps_payload->dsc_version =
|
||||
dsc_cfg->dsc_version_minor |
|
||||
dsc_cfg->dsc_version_major << DSC_PPS_VERSION_MAJOR_SHIFT;
|
||||
|
||||
/* PPS 1, 2 is 0 */
|
||||
|
||||
/* PPS 3 */
|
||||
pps_sdp->pps_payload.pps_3 =
|
||||
pps_payload->pps_3 =
|
||||
dsc_cfg->line_buf_depth |
|
||||
dsc_cfg->bits_per_component << DSC_PPS_BPC_SHIFT;
|
||||
|
||||
/* PPS 4 */
|
||||
pps_sdp->pps_payload.pps_4 =
|
||||
pps_payload->pps_4 =
|
||||
((dsc_cfg->bits_per_pixel & DSC_PPS_BPP_HIGH_MASK) >>
|
||||
DSC_PPS_MSB_SHIFT) |
|
||||
dsc_cfg->vbr_enable << DSC_PPS_VBR_EN_SHIFT |
|
||||
dsc_cfg->enable422 << DSC_PPS_SIMPLE422_SHIFT |
|
||||
dsc_cfg->simple_422 << DSC_PPS_SIMPLE422_SHIFT |
|
||||
dsc_cfg->convert_rgb << DSC_PPS_CONVERT_RGB_SHIFT |
|
||||
dsc_cfg->block_pred_enable << DSC_PPS_BLOCK_PRED_EN_SHIFT;
|
||||
|
||||
/* PPS 5 */
|
||||
pps_sdp->pps_payload.bits_per_pixel_low =
|
||||
pps_payload->bits_per_pixel_low =
|
||||
(dsc_cfg->bits_per_pixel & DSC_PPS_LSB_MASK);
|
||||
|
||||
/*
|
||||
@ -110,103 +110,103 @@ void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp,
|
||||
*/
|
||||
|
||||
/* PPS 6, 7 */
|
||||
pps_sdp->pps_payload.pic_height = cpu_to_be16(dsc_cfg->pic_height);
|
||||
pps_payload->pic_height = cpu_to_be16(dsc_cfg->pic_height);
|
||||
|
||||
/* PPS 8, 9 */
|
||||
pps_sdp->pps_payload.pic_width = cpu_to_be16(dsc_cfg->pic_width);
|
||||
pps_payload->pic_width = cpu_to_be16(dsc_cfg->pic_width);
|
||||
|
||||
/* PPS 10, 11 */
|
||||
pps_sdp->pps_payload.slice_height = cpu_to_be16(dsc_cfg->slice_height);
|
||||
pps_payload->slice_height = cpu_to_be16(dsc_cfg->slice_height);
|
||||
|
||||
/* PPS 12, 13 */
|
||||
pps_sdp->pps_payload.slice_width = cpu_to_be16(dsc_cfg->slice_width);
|
||||
pps_payload->slice_width = cpu_to_be16(dsc_cfg->slice_width);
|
||||
|
||||
/* PPS 14, 15 */
|
||||
pps_sdp->pps_payload.chunk_size = cpu_to_be16(dsc_cfg->slice_chunk_size);
|
||||
pps_payload->chunk_size = cpu_to_be16(dsc_cfg->slice_chunk_size);
|
||||
|
||||
/* PPS 16 */
|
||||
pps_sdp->pps_payload.initial_xmit_delay_high =
|
||||
pps_payload->initial_xmit_delay_high =
|
||||
((dsc_cfg->initial_xmit_delay &
|
||||
DSC_PPS_INIT_XMIT_DELAY_HIGH_MASK) >>
|
||||
DSC_PPS_MSB_SHIFT);
|
||||
|
||||
/* PPS 17 */
|
||||
pps_sdp->pps_payload.initial_xmit_delay_low =
|
||||
pps_payload->initial_xmit_delay_low =
|
||||
(dsc_cfg->initial_xmit_delay & DSC_PPS_LSB_MASK);
|
||||
|
||||
/* PPS 18, 19 */
|
||||
pps_sdp->pps_payload.initial_dec_delay =
|
||||
pps_payload->initial_dec_delay =
|
||||
cpu_to_be16(dsc_cfg->initial_dec_delay);
|
||||
|
||||
/* PPS 20 is 0 */
|
||||
|
||||
/* PPS 21 */
|
||||
pps_sdp->pps_payload.initial_scale_value =
|
||||
pps_payload->initial_scale_value =
|
||||
dsc_cfg->initial_scale_value;
|
||||
|
||||
/* PPS 22, 23 */
|
||||
pps_sdp->pps_payload.scale_increment_interval =
|
||||
pps_payload->scale_increment_interval =
|
||||
cpu_to_be16(dsc_cfg->scale_increment_interval);
|
||||
|
||||
/* PPS 24 */
|
||||
pps_sdp->pps_payload.scale_decrement_interval_high =
|
||||
pps_payload->scale_decrement_interval_high =
|
||||
((dsc_cfg->scale_decrement_interval &
|
||||
DSC_PPS_SCALE_DEC_INT_HIGH_MASK) >>
|
||||
DSC_PPS_MSB_SHIFT);
|
||||
|
||||
/* PPS 25 */
|
||||
pps_sdp->pps_payload.scale_decrement_interval_low =
|
||||
pps_payload->scale_decrement_interval_low =
|
||||
(dsc_cfg->scale_decrement_interval & DSC_PPS_LSB_MASK);
|
||||
|
||||
/* PPS 26[7:0], PPS 27[7:5] RESERVED */
|
||||
|
||||
/* PPS 27 */
|
||||
pps_sdp->pps_payload.first_line_bpg_offset =
|
||||
pps_payload->first_line_bpg_offset =
|
||||
dsc_cfg->first_line_bpg_offset;
|
||||
|
||||
/* PPS 28, 29 */
|
||||
pps_sdp->pps_payload.nfl_bpg_offset =
|
||||
pps_payload->nfl_bpg_offset =
|
||||
cpu_to_be16(dsc_cfg->nfl_bpg_offset);
|
||||
|
||||
/* PPS 30, 31 */
|
||||
pps_sdp->pps_payload.slice_bpg_offset =
|
||||
pps_payload->slice_bpg_offset =
|
||||
cpu_to_be16(dsc_cfg->slice_bpg_offset);
|
||||
|
||||
/* PPS 32, 33 */
|
||||
pps_sdp->pps_payload.initial_offset =
|
||||
pps_payload->initial_offset =
|
||||
cpu_to_be16(dsc_cfg->initial_offset);
|
||||
|
||||
/* PPS 34, 35 */
|
||||
pps_sdp->pps_payload.final_offset = cpu_to_be16(dsc_cfg->final_offset);
|
||||
pps_payload->final_offset = cpu_to_be16(dsc_cfg->final_offset);
|
||||
|
||||
/* PPS 36 */
|
||||
pps_sdp->pps_payload.flatness_min_qp = dsc_cfg->flatness_min_qp;
|
||||
pps_payload->flatness_min_qp = dsc_cfg->flatness_min_qp;
|
||||
|
||||
/* PPS 37 */
|
||||
pps_sdp->pps_payload.flatness_max_qp = dsc_cfg->flatness_max_qp;
|
||||
pps_payload->flatness_max_qp = dsc_cfg->flatness_max_qp;
|
||||
|
||||
/* PPS 38, 39 */
|
||||
pps_sdp->pps_payload.rc_model_size =
|
||||
pps_payload->rc_model_size =
|
||||
cpu_to_be16(DSC_RC_MODEL_SIZE_CONST);
|
||||
|
||||
/* PPS 40 */
|
||||
pps_sdp->pps_payload.rc_edge_factor = DSC_RC_EDGE_FACTOR_CONST;
|
||||
pps_payload->rc_edge_factor = DSC_RC_EDGE_FACTOR_CONST;
|
||||
|
||||
/* PPS 41 */
|
||||
pps_sdp->pps_payload.rc_quant_incr_limit0 =
|
||||
pps_payload->rc_quant_incr_limit0 =
|
||||
dsc_cfg->rc_quant_incr_limit0;
|
||||
|
||||
/* PPS 42 */
|
||||
pps_sdp->pps_payload.rc_quant_incr_limit1 =
|
||||
pps_payload->rc_quant_incr_limit1 =
|
||||
dsc_cfg->rc_quant_incr_limit1;
|
||||
|
||||
/* PPS 43 */
|
||||
pps_sdp->pps_payload.rc_tgt_offset = DSC_RC_TGT_OFFSET_LO_CONST |
|
||||
pps_payload->rc_tgt_offset = DSC_RC_TGT_OFFSET_LO_CONST |
|
||||
DSC_RC_TGT_OFFSET_HI_CONST << DSC_PPS_RC_TGT_OFFSET_HI_SHIFT;
|
||||
|
||||
/* PPS 44 - 57 */
|
||||
for (i = 0; i < DSC_NUM_BUF_RANGES - 1; i++)
|
||||
pps_sdp->pps_payload.rc_buf_thresh[i] =
|
||||
pps_payload->rc_buf_thresh[i] =
|
||||
dsc_cfg->rc_buf_thresh[i];
|
||||
|
||||
/* PPS 58 - 87 */
|
||||
@ -215,32 +215,181 @@ void drm_dsc_pps_infoframe_pack(struct drm_dsc_pps_infoframe *pps_sdp,
|
||||
* are as follows: Min_qp[15:11], max_qp[10:6], offset[5:0]
|
||||
*/
|
||||
for (i = 0; i < DSC_NUM_BUF_RANGES; i++) {
|
||||
pps_sdp->pps_payload.rc_range_parameters[i] =
|
||||
pps_payload->rc_range_parameters[i] =
|
||||
((dsc_cfg->rc_range_params[i].range_min_qp <<
|
||||
DSC_PPS_RC_RANGE_MINQP_SHIFT) |
|
||||
(dsc_cfg->rc_range_params[i].range_max_qp <<
|
||||
DSC_PPS_RC_RANGE_MAXQP_SHIFT) |
|
||||
(dsc_cfg->rc_range_params[i].range_bpg_offset));
|
||||
pps_sdp->pps_payload.rc_range_parameters[i] =
|
||||
cpu_to_be16(pps_sdp->pps_payload.rc_range_parameters[i]);
|
||||
pps_payload->rc_range_parameters[i] =
|
||||
cpu_to_be16(pps_payload->rc_range_parameters[i]);
|
||||
}
|
||||
|
||||
/* PPS 88 */
|
||||
pps_sdp->pps_payload.native_422_420 = dsc_cfg->native_422 |
|
||||
pps_payload->native_422_420 = dsc_cfg->native_422 |
|
||||
dsc_cfg->native_420 << DSC_PPS_NATIVE_420_SHIFT;
|
||||
|
||||
/* PPS 89 */
|
||||
pps_sdp->pps_payload.second_line_bpg_offset =
|
||||
pps_payload->second_line_bpg_offset =
|
||||
dsc_cfg->second_line_bpg_offset;
|
||||
|
||||
/* PPS 90, 91 */
|
||||
pps_sdp->pps_payload.nsl_bpg_offset =
|
||||
pps_payload->nsl_bpg_offset =
|
||||
cpu_to_be16(dsc_cfg->nsl_bpg_offset);
|
||||
|
||||
/* PPS 92, 93 */
|
||||
pps_sdp->pps_payload.second_line_offset_adj =
|
||||
pps_payload->second_line_offset_adj =
|
||||
cpu_to_be16(dsc_cfg->second_line_offset_adj);
|
||||
|
||||
/* PPS 94 - 127 are O */
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dsc_pps_infoframe_pack);
|
||||
EXPORT_SYMBOL(drm_dsc_pps_payload_pack);
|
||||
|
||||
/**
|
||||
* drm_dsc_compute_rc_parameters() - Write rate control
|
||||
* parameters to the dsc configuration defined in
|
||||
* &struct drm_dsc_config in accordance with the DSC 1.2
|
||||
* specification. Some configuration fields must be present
|
||||
* beforehand.
|
||||
*
|
||||
* @vdsc_cfg:
|
||||
* DSC Configuration data partially filled by driver
|
||||
*/
|
||||
int drm_dsc_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg)
|
||||
{
|
||||
unsigned long groups_per_line = 0;
|
||||
unsigned long groups_total = 0;
|
||||
unsigned long num_extra_mux_bits = 0;
|
||||
unsigned long slice_bits = 0;
|
||||
unsigned long hrd_delay = 0;
|
||||
unsigned long final_scale = 0;
|
||||
unsigned long rbs_min = 0;
|
||||
|
||||
if (vdsc_cfg->native_420 || vdsc_cfg->native_422) {
|
||||
/* Number of groups used to code each line of a slice */
|
||||
groups_per_line = DIV_ROUND_UP(vdsc_cfg->slice_width / 2,
|
||||
DSC_RC_PIXELS_PER_GROUP);
|
||||
|
||||
/* chunksize in Bytes */
|
||||
vdsc_cfg->slice_chunk_size = DIV_ROUND_UP(vdsc_cfg->slice_width / 2 *
|
||||
vdsc_cfg->bits_per_pixel,
|
||||
(8 * 16));
|
||||
} else {
|
||||
/* Number of groups used to code each line of a slice */
|
||||
groups_per_line = DIV_ROUND_UP(vdsc_cfg->slice_width,
|
||||
DSC_RC_PIXELS_PER_GROUP);
|
||||
|
||||
/* chunksize in Bytes */
|
||||
vdsc_cfg->slice_chunk_size = DIV_ROUND_UP(vdsc_cfg->slice_width *
|
||||
vdsc_cfg->bits_per_pixel,
|
||||
(8 * 16));
|
||||
}
|
||||
|
||||
if (vdsc_cfg->convert_rgb)
|
||||
num_extra_mux_bits = 3 * (vdsc_cfg->mux_word_size +
|
||||
(4 * vdsc_cfg->bits_per_component + 4)
|
||||
- 2);
|
||||
else if (vdsc_cfg->native_422)
|
||||
num_extra_mux_bits = 4 * vdsc_cfg->mux_word_size +
|
||||
(4 * vdsc_cfg->bits_per_component + 4) +
|
||||
3 * (4 * vdsc_cfg->bits_per_component) - 2;
|
||||
else
|
||||
num_extra_mux_bits = 3 * vdsc_cfg->mux_word_size +
|
||||
(4 * vdsc_cfg->bits_per_component + 4) +
|
||||
2 * (4 * vdsc_cfg->bits_per_component) - 2;
|
||||
/* Number of bits in one Slice */
|
||||
slice_bits = 8 * vdsc_cfg->slice_chunk_size * vdsc_cfg->slice_height;
|
||||
|
||||
while ((num_extra_mux_bits > 0) &&
|
||||
((slice_bits - num_extra_mux_bits) % vdsc_cfg->mux_word_size))
|
||||
num_extra_mux_bits--;
|
||||
|
||||
if (groups_per_line < vdsc_cfg->initial_scale_value - 8)
|
||||
vdsc_cfg->initial_scale_value = groups_per_line + 8;
|
||||
|
||||
/* scale_decrement_interval calculation according to DSC spec 1.11 */
|
||||
if (vdsc_cfg->initial_scale_value > 8)
|
||||
vdsc_cfg->scale_decrement_interval = groups_per_line /
|
||||
(vdsc_cfg->initial_scale_value - 8);
|
||||
else
|
||||
vdsc_cfg->scale_decrement_interval = DSC_SCALE_DECREMENT_INTERVAL_MAX;
|
||||
|
||||
vdsc_cfg->final_offset = vdsc_cfg->rc_model_size -
|
||||
(vdsc_cfg->initial_xmit_delay *
|
||||
vdsc_cfg->bits_per_pixel + 8) / 16 + num_extra_mux_bits;
|
||||
|
||||
if (vdsc_cfg->final_offset >= vdsc_cfg->rc_model_size) {
|
||||
DRM_DEBUG_KMS("FinalOfs < RcModelSze for this InitialXmitDelay\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
final_scale = (vdsc_cfg->rc_model_size * 8) /
|
||||
(vdsc_cfg->rc_model_size - vdsc_cfg->final_offset);
|
||||
if (vdsc_cfg->slice_height > 1)
|
||||
/*
|
||||
* NflBpgOffset is 16 bit value with 11 fractional bits
|
||||
* hence we multiply by 2^11 for preserving the
|
||||
* fractional part
|
||||
*/
|
||||
vdsc_cfg->nfl_bpg_offset = DIV_ROUND_UP((vdsc_cfg->first_line_bpg_offset << 11),
|
||||
(vdsc_cfg->slice_height - 1));
|
||||
else
|
||||
vdsc_cfg->nfl_bpg_offset = 0;
|
||||
|
||||
/* 2^16 - 1 */
|
||||
if (vdsc_cfg->nfl_bpg_offset > 65535) {
|
||||
DRM_DEBUG_KMS("NflBpgOffset is too large for this slice height\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Number of groups used to code the entire slice */
|
||||
groups_total = groups_per_line * vdsc_cfg->slice_height;
|
||||
|
||||
/* slice_bpg_offset is 16 bit value with 11 fractional bits */
|
||||
vdsc_cfg->slice_bpg_offset = DIV_ROUND_UP(((vdsc_cfg->rc_model_size -
|
||||
vdsc_cfg->initial_offset +
|
||||
num_extra_mux_bits) << 11),
|
||||
groups_total);
|
||||
|
||||
if (final_scale > 9) {
|
||||
/*
|
||||
* ScaleIncrementInterval =
|
||||
* finaloffset/((NflBpgOffset + SliceBpgOffset)*8(finalscale - 1.125))
|
||||
* as (NflBpgOffset + SliceBpgOffset) has 11 bit fractional value,
|
||||
* we need divide by 2^11 from pstDscCfg values
|
||||
*/
|
||||
vdsc_cfg->scale_increment_interval =
|
||||
(vdsc_cfg->final_offset * (1 << 11)) /
|
||||
((vdsc_cfg->nfl_bpg_offset +
|
||||
vdsc_cfg->slice_bpg_offset) *
|
||||
(final_scale - 9));
|
||||
} else {
|
||||
/*
|
||||
* If finalScaleValue is less than or equal to 9, a value of 0 should
|
||||
* be used to disable the scale increment at the end of the slice
|
||||
*/
|
||||
vdsc_cfg->scale_increment_interval = 0;
|
||||
}
|
||||
|
||||
if (vdsc_cfg->scale_increment_interval > 65535) {
|
||||
DRM_DEBUG_KMS("ScaleIncrementInterval is large for slice height\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* DSC spec mentions that bits_per_pixel specifies the target
|
||||
* bits/pixel (bpp) rate that is used by the encoder,
|
||||
* in steps of 1/16 of a bit per pixel
|
||||
*/
|
||||
rbs_min = vdsc_cfg->rc_model_size - vdsc_cfg->initial_offset +
|
||||
DIV_ROUND_UP(vdsc_cfg->initial_xmit_delay *
|
||||
vdsc_cfg->bits_per_pixel, 16) +
|
||||
groups_per_line * vdsc_cfg->first_line_bpg_offset;
|
||||
|
||||
hrd_delay = DIV_ROUND_UP((rbs_min * 16), vdsc_cfg->bits_per_pixel);
|
||||
vdsc_cfg->rc_bits = (hrd_delay * vdsc_cfg->bits_per_pixel) / 16;
|
||||
vdsc_cfg->initial_dec_delay = hrd_delay - vdsc_cfg->initial_xmit_delay;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dsc_compute_rc_parameters);
|
||||
|
@ -193,6 +193,12 @@ static const struct edid_quirk {
|
||||
|
||||
/* Sony PlayStation VR Headset */
|
||||
{ "SNY", 0x0704, EDID_QUIRK_NON_DESKTOP },
|
||||
|
||||
/* Sensics VR Headsets */
|
||||
{ "SEN", 0x1019, EDID_QUIRK_NON_DESKTOP },
|
||||
|
||||
/* OSVR HDK and HDK2 VR Headsets */
|
||||
{ "SVR", 0x1019, EDID_QUIRK_NON_DESKTOP },
|
||||
};
|
||||
|
||||
/*
|
||||
@ -4924,6 +4930,76 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,
|
||||
}
|
||||
EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);
|
||||
|
||||
/* HDMI Colorspace Spec Definitions */
|
||||
#define FULL_COLORIMETRY_MASK 0x1FF
|
||||
#define NORMAL_COLORIMETRY_MASK 0x3
|
||||
#define EXTENDED_COLORIMETRY_MASK 0x7
|
||||
#define EXTENDED_ACE_COLORIMETRY_MASK 0xF
|
||||
|
||||
#define C(x) ((x) << 0)
|
||||
#define EC(x) ((x) << 2)
|
||||
#define ACE(x) ((x) << 5)
|
||||
|
||||
#define HDMI_COLORIMETRY_NO_DATA 0x0
|
||||
#define HDMI_COLORIMETRY_SMPTE_170M_YCC (C(1) | EC(0) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_BT709_YCC (C(2) | EC(0) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_XVYCC_601 (C(3) | EC(0) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_XVYCC_709 (C(3) | EC(1) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_SYCC_601 (C(3) | EC(2) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_OPYCC_601 (C(3) | EC(3) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_OPRGB (C(3) | EC(4) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_BT2020_CYCC (C(3) | EC(5) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_BT2020_RGB (C(3) | EC(6) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_BT2020_YCC (C(3) | EC(6) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_DCI_P3_RGB_D65 (C(3) | EC(7) | ACE(0))
|
||||
#define HDMI_COLORIMETRY_DCI_P3_RGB_THEATER (C(3) | EC(7) | ACE(1))
|
||||
|
||||
static const u32 hdmi_colorimetry_val[] = {
|
||||
[DRM_MODE_COLORIMETRY_NO_DATA] = HDMI_COLORIMETRY_NO_DATA,
|
||||
[DRM_MODE_COLORIMETRY_SMPTE_170M_YCC] = HDMI_COLORIMETRY_SMPTE_170M_YCC,
|
||||
[DRM_MODE_COLORIMETRY_BT709_YCC] = HDMI_COLORIMETRY_BT709_YCC,
|
||||
[DRM_MODE_COLORIMETRY_XVYCC_601] = HDMI_COLORIMETRY_XVYCC_601,
|
||||
[DRM_MODE_COLORIMETRY_XVYCC_709] = HDMI_COLORIMETRY_XVYCC_709,
|
||||
[DRM_MODE_COLORIMETRY_SYCC_601] = HDMI_COLORIMETRY_SYCC_601,
|
||||
[DRM_MODE_COLORIMETRY_OPYCC_601] = HDMI_COLORIMETRY_OPYCC_601,
|
||||
[DRM_MODE_COLORIMETRY_OPRGB] = HDMI_COLORIMETRY_OPRGB,
|
||||
[DRM_MODE_COLORIMETRY_BT2020_CYCC] = HDMI_COLORIMETRY_BT2020_CYCC,
|
||||
[DRM_MODE_COLORIMETRY_BT2020_RGB] = HDMI_COLORIMETRY_BT2020_RGB,
|
||||
[DRM_MODE_COLORIMETRY_BT2020_YCC] = HDMI_COLORIMETRY_BT2020_YCC,
|
||||
};
|
||||
|
||||
#undef C
|
||||
#undef EC
|
||||
#undef ACE
|
||||
|
||||
/**
|
||||
* drm_hdmi_avi_infoframe_colorspace() - fill the HDMI AVI infoframe
|
||||
* colorspace information
|
||||
* @frame: HDMI AVI infoframe
|
||||
* @conn_state: connector state
|
||||
*/
|
||||
void
|
||||
drm_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
|
||||
const struct drm_connector_state *conn_state)
|
||||
{
|
||||
u32 colorimetry_val;
|
||||
u32 colorimetry_index = conn_state->colorspace & FULL_COLORIMETRY_MASK;
|
||||
|
||||
if (colorimetry_index >= ARRAY_SIZE(hdmi_colorimetry_val))
|
||||
colorimetry_val = HDMI_COLORIMETRY_NO_DATA;
|
||||
else
|
||||
colorimetry_val = hdmi_colorimetry_val[colorimetry_index];
|
||||
|
||||
frame->colorimetry = colorimetry_val & NORMAL_COLORIMETRY_MASK;
|
||||
/*
|
||||
* ToDo: Extend it for ACE formats as well. Modify the infoframe
|
||||
* structure and extend it in drivers/video/hdmi
|
||||
*/
|
||||
frame->extended_colorimetry = (colorimetry_val >> 2) &
|
||||
EXTENDED_COLORIMETRY_MASK;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorspace);
|
||||
|
||||
/**
|
||||
* drm_hdmi_avi_infoframe_quant_range() - fill the HDMI AVI infoframe
|
||||
* quantization range information
|
||||
|
@ -3024,7 +3024,8 @@ static int drm_fbdev_fb_open(struct fb_info *info, int user)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
|
||||
if (!try_module_get(fb_helper->dev->driver->fops->owner))
|
||||
/* No need to take a ref for fbcon because it unbinds on unregister */
|
||||
if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
@ -3034,7 +3035,8 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user)
|
||||
{
|
||||
struct drm_fb_helper *fb_helper = info->par;
|
||||
|
||||
module_put(fb_helper->dev->driver->fops->owner);
|
||||
if (user)
|
||||
module_put(fb_helper->dev->driver->fops->owner);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -489,11 +489,9 @@ int drm_release(struct inode *inode, struct file *filp)
|
||||
|
||||
drm_close_helper(filp);
|
||||
|
||||
if (!--dev->open_count) {
|
||||
if (!--dev->open_count)
|
||||
drm_lastclose(dev);
|
||||
if (drm_dev_is_unplugged(dev))
|
||||
drm_put_dev(dev);
|
||||
}
|
||||
|
||||
mutex_unlock(&drm_global_mutex);
|
||||
|
||||
drm_minor_release(minor);
|
||||
@ -579,6 +577,7 @@ put_back_event:
|
||||
file_priv->event_space -= length;
|
||||
list_add(&e->link, &file_priv->event_list);
|
||||
spin_unlock_irq(&dev->event_lock);
|
||||
wake_up_interruptible(&file_priv->event_wait);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -198,6 +198,10 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
||||
{ .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGBA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_XRGB16161616F, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_XBGR16161616F, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1 },
|
||||
{ .format = DRM_FORMAT_ARGB16161616F, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_ABGR16161616F, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_RGB888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_BGR888_A8, .depth = 32, .num_planes = 2, .cpp = { 3, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
{ .format = DRM_FORMAT_XRGB8888_A8, .depth = 32, .num_planes = 2, .cpp = { 4, 1, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true },
|
||||
@ -225,7 +229,17 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
||||
{ .format = DRM_FORMAT_UYVY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_XYUV8888, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_VUY888, .depth = 0, .num_planes = 1, .cpp = { 3, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_Y210, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_Y212, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_Y216, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_Y410, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_Y412, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_Y416, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_XVYU2101010, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_XVYU12_16161616, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_XVYU16161616, .depth = 0, .num_planes = 1, .cpp = { 8, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_Y0L0, .depth = 0, .num_planes = 1,
|
||||
.char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 },
|
||||
.hsub = 2, .vsub = 2, .has_alpha = true, .is_yuv = true },
|
||||
@ -247,6 +261,19 @@ const struct drm_format_info *__drm_format_info(u32 format)
|
||||
{ .format = DRM_FORMAT_P016, .depth = 0, .num_planes = 2,
|
||||
.char_per_block = { 2, 4, 0 }, .block_w = { 1, 0, 0 }, .block_h = { 1, 0, 0 },
|
||||
.hsub = 2, .vsub = 2, .is_yuv = true},
|
||||
{ .format = DRM_FORMAT_P210, .depth = 0,
|
||||
.num_planes = 2, .char_per_block = { 2, 4, 0 },
|
||||
.block_w = { 1, 0, 0 }, .block_h = { 1, 0, 0 }, .hsub = 2,
|
||||
.vsub = 1, .is_yuv = true },
|
||||
{ .format = DRM_FORMAT_VUY101010, .depth = 0,
|
||||
.num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 1, .vsub = 1,
|
||||
.is_yuv = true },
|
||||
{ .format = DRM_FORMAT_YUV420_8BIT, .depth = 0,
|
||||
.num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
|
||||
.is_yuv = true },
|
||||
{ .format = DRM_FORMAT_YUV420_10BIT, .depth = 0,
|
||||
.num_planes = 1, .cpp = { 0, 0, 0 }, .hsub = 2, .vsub = 2,
|
||||
.is_yuv = true },
|
||||
};
|
||||
|
||||
unsigned int i;
|
||||
|
@ -171,6 +171,10 @@ void drm_gem_private_object_init(struct drm_device *dev,
|
||||
kref_init(&obj->refcount);
|
||||
obj->handle_count = 0;
|
||||
obj->size = size;
|
||||
reservation_object_init(&obj->_resv);
|
||||
if (!obj->resv)
|
||||
obj->resv = &obj->_resv;
|
||||
|
||||
drm_vma_node_reset(&obj->vma_node);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_private_object_init);
|
||||
@ -687,6 +691,44 @@ drm_gem_object_lookup(struct drm_file *filp, u32 handle)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_object_lookup);
|
||||
|
||||
/**
|
||||
* drm_gem_reservation_object_wait - Wait on GEM object's reservation's objects
|
||||
* shared and/or exclusive fences.
|
||||
* @filep: DRM file private date
|
||||
* @handle: userspace handle
|
||||
* @wait_all: if true, wait on all fences, else wait on just exclusive fence
|
||||
* @timeout: timeout value in jiffies or zero to return immediately
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* Returns -ERESTARTSYS if interrupted, 0 if the wait timed out, or
|
||||
* greater than 0 on success.
|
||||
*/
|
||||
long drm_gem_reservation_object_wait(struct drm_file *filep, u32 handle,
|
||||
bool wait_all, unsigned long timeout)
|
||||
{
|
||||
long ret;
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
obj = drm_gem_object_lookup(filep, handle);
|
||||
if (!obj) {
|
||||
DRM_DEBUG("Failed to look up GEM BO %d\n", handle);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = reservation_object_wait_timeout_rcu(obj->resv, wait_all,
|
||||
true, timeout);
|
||||
if (ret == 0)
|
||||
ret = -ETIME;
|
||||
else if (ret > 0)
|
||||
ret = 0;
|
||||
|
||||
drm_gem_object_put_unlocked(obj);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_reservation_object_wait);
|
||||
|
||||
/**
|
||||
* drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl
|
||||
* @dev: drm_device
|
||||
@ -851,6 +893,7 @@ drm_gem_object_release(struct drm_gem_object *obj)
|
||||
if (obj->filp)
|
||||
fput(obj->filp);
|
||||
|
||||
reservation_object_fini(&obj->_resv);
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_object_release);
|
||||
@ -1190,3 +1233,81 @@ void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr)
|
||||
obj->dev->driver->gem_prime_vunmap(obj, vaddr);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_vunmap);
|
||||
|
||||
/**
|
||||
* drm_gem_lock_reservations - Sets up the ww context and acquires
|
||||
* the lock on an array of GEM objects.
|
||||
*
|
||||
* Once you've locked your reservations, you'll want to set up space
|
||||
* for your shared fences (if applicable), submit your job, then
|
||||
* drm_gem_unlock_reservations().
|
||||
*
|
||||
* @objs: drm_gem_objects to lock
|
||||
* @count: Number of objects in @objs
|
||||
* @acquire_ctx: struct ww_acquire_ctx that will be initialized as
|
||||
* part of tracking this set of locked reservations.
|
||||
*/
|
||||
int
|
||||
drm_gem_lock_reservations(struct drm_gem_object **objs, int count,
|
||||
struct ww_acquire_ctx *acquire_ctx)
|
||||
{
|
||||
int contended = -1;
|
||||
int i, ret;
|
||||
|
||||
ww_acquire_init(acquire_ctx, &reservation_ww_class);
|
||||
|
||||
retry:
|
||||
if (contended != -1) {
|
||||
struct drm_gem_object *obj = objs[contended];
|
||||
|
||||
ret = ww_mutex_lock_slow_interruptible(&obj->resv->lock,
|
||||
acquire_ctx);
|
||||
if (ret) {
|
||||
ww_acquire_done(acquire_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i == contended)
|
||||
continue;
|
||||
|
||||
ret = ww_mutex_lock_interruptible(&objs[i]->resv->lock,
|
||||
acquire_ctx);
|
||||
if (ret) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; j++)
|
||||
ww_mutex_unlock(&objs[j]->resv->lock);
|
||||
|
||||
if (contended != -1 && contended >= i)
|
||||
ww_mutex_unlock(&objs[contended]->resv->lock);
|
||||
|
||||
if (ret == -EDEADLK) {
|
||||
contended = i;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ww_acquire_done(acquire_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ww_acquire_done(acquire_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_lock_reservations);
|
||||
|
||||
void
|
||||
drm_gem_unlock_reservations(struct drm_gem_object **objs, int count,
|
||||
struct ww_acquire_ctx *acquire_ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
ww_mutex_unlock(&objs[i]->resv->lock);
|
||||
|
||||
ww_acquire_fini(acquire_ctx);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_unlock_reservations);
|
||||
|
625
drivers/gpu/drm/drm_gem_shmem_helper.c
Normal file
625
drivers/gpu/drm/drm_gem_shmem_helper.c
Normal file
@ -0,0 +1,625 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2018 Noralf Trønnes
|
||||
*/
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_prime.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
/**
|
||||
* DOC: overview
|
||||
*
|
||||
* This library provides helpers for GEM objects backed by shmem buffers
|
||||
* allocated using anonymous pageable memory.
|
||||
*/
|
||||
|
||||
static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
|
||||
.free = drm_gem_shmem_free_object,
|
||||
.print_info = drm_gem_shmem_print_info,
|
||||
.pin = drm_gem_shmem_pin,
|
||||
.unpin = drm_gem_shmem_unpin,
|
||||
.get_sg_table = drm_gem_shmem_get_sg_table,
|
||||
.vmap = drm_gem_shmem_vmap,
|
||||
.vunmap = drm_gem_shmem_vunmap,
|
||||
.vm_ops = &drm_gem_shmem_vm_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_create - Allocate an object with the given size
|
||||
* @dev: DRM device
|
||||
* @size: Size of the object to allocate
|
||||
*
|
||||
* This function creates a shmem GEM object.
|
||||
*
|
||||
* Returns:
|
||||
* A struct drm_gem_shmem_object * on success or an ERR_PTR()-encoded negative
|
||||
* error code on failure.
|
||||
*/
|
||||
struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
if (dev->driver->gem_create_object)
|
||||
obj = dev->driver->gem_create_object(dev, size);
|
||||
else
|
||||
obj = kzalloc(sizeof(*shmem), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (!obj->funcs)
|
||||
obj->funcs = &drm_gem_shmem_funcs;
|
||||
|
||||
ret = drm_gem_object_init(dev, obj, size);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
ret = drm_gem_create_mmap_offset(obj);
|
||||
if (ret)
|
||||
goto err_release;
|
||||
|
||||
shmem = to_drm_gem_shmem_obj(obj);
|
||||
mutex_init(&shmem->pages_lock);
|
||||
mutex_init(&shmem->vmap_lock);
|
||||
|
||||
/*
|
||||
* Our buffers are kept pinned, so allocating them
|
||||
* from the MOVABLE zone is a really bad idea, and
|
||||
* conflicts with CMA. See comments above new_inode()
|
||||
* why this is required _and_ expected if you're
|
||||
* going to pin these pages.
|
||||
*/
|
||||
mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER |
|
||||
__GFP_RETRY_MAYFAIL | __GFP_NOWARN);
|
||||
|
||||
return shmem;
|
||||
|
||||
err_release:
|
||||
drm_gem_object_release(obj);
|
||||
err_free:
|
||||
kfree(obj);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_free_object - Free resources associated with a shmem GEM object
|
||||
* @obj: GEM object to free
|
||||
*
|
||||
* This function cleans up the GEM object state and frees the memory used to
|
||||
* store the object itself.
|
||||
*/
|
||||
void drm_gem_shmem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
WARN_ON(shmem->vmap_use_count);
|
||||
|
||||
if (obj->import_attach) {
|
||||
shmem->pages_use_count--;
|
||||
drm_prime_gem_destroy(obj, shmem->sgt);
|
||||
kvfree(shmem->pages);
|
||||
} else {
|
||||
if (shmem->sgt) {
|
||||
dma_unmap_sg(obj->dev->dev, shmem->sgt->sgl,
|
||||
shmem->sgt->nents, DMA_BIDIRECTIONAL);
|
||||
|
||||
drm_gem_shmem_put_pages(shmem);
|
||||
sg_free_table(shmem->sgt);
|
||||
kfree(shmem->sgt);
|
||||
}
|
||||
}
|
||||
|
||||
WARN_ON(shmem->pages_use_count);
|
||||
|
||||
drm_gem_object_release(obj);
|
||||
mutex_destroy(&shmem->pages_lock);
|
||||
mutex_destroy(&shmem->vmap_lock);
|
||||
kfree(shmem);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_free_object);
|
||||
|
||||
static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
struct page **pages;
|
||||
|
||||
if (shmem->pages_use_count++ > 0)
|
||||
return 0;
|
||||
|
||||
pages = drm_gem_get_pages(obj);
|
||||
if (IS_ERR(pages)) {
|
||||
DRM_DEBUG_KMS("Failed to get pages (%ld)\n", PTR_ERR(pages));
|
||||
shmem->pages_use_count = 0;
|
||||
return PTR_ERR(pages);
|
||||
}
|
||||
|
||||
shmem->pages = pages;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_gem_shmem_get_pages - Allocate backing pages for a shmem GEM object
|
||||
* @shmem: shmem GEM object
|
||||
*
|
||||
* This function makes sure that backing pages exists for the shmem GEM object
|
||||
* and increases the use count.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_gem_shmem_get_pages(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&shmem->pages_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = drm_gem_shmem_get_pages_locked(shmem);
|
||||
mutex_unlock(&shmem->pages_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_get_pages);
|
||||
|
||||
static void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
|
||||
if (WARN_ON_ONCE(!shmem->pages_use_count))
|
||||
return;
|
||||
|
||||
if (--shmem->pages_use_count > 0)
|
||||
return;
|
||||
|
||||
drm_gem_put_pages(obj, shmem->pages,
|
||||
shmem->pages_mark_dirty_on_put,
|
||||
shmem->pages_mark_accessed_on_put);
|
||||
shmem->pages = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_gem_shmem_put_pages - Decrease use count on the backing pages for a shmem GEM object
|
||||
* @shmem: shmem GEM object
|
||||
*
|
||||
* This function decreases the use count and puts the backing pages when use drops to zero.
|
||||
*/
|
||||
void drm_gem_shmem_put_pages(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
mutex_lock(&shmem->pages_lock);
|
||||
drm_gem_shmem_put_pages_locked(shmem);
|
||||
mutex_unlock(&shmem->pages_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_put_pages);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_pin - Pin backing pages for a shmem GEM object
|
||||
* @obj: GEM object
|
||||
*
|
||||
* This function makes sure the backing pages are pinned in memory while the
|
||||
* buffer is exported.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_gem_shmem_pin(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
return drm_gem_shmem_get_pages(shmem);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_pin);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_unpin - Unpin backing pages for a shmem GEM object
|
||||
* @obj: GEM object
|
||||
*
|
||||
* This function removes the requirement that the backing pages are pinned in
|
||||
* memory.
|
||||
*/
|
||||
void drm_gem_shmem_unpin(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
drm_gem_shmem_put_pages(shmem);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_unpin);
|
||||
|
||||
static void *drm_gem_shmem_vmap_locked(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
int ret;
|
||||
|
||||
if (shmem->vmap_use_count++ > 0)
|
||||
return shmem->vaddr;
|
||||
|
||||
ret = drm_gem_shmem_get_pages(shmem);
|
||||
if (ret)
|
||||
goto err_zero_use;
|
||||
|
||||
if (obj->import_attach)
|
||||
shmem->vaddr = dma_buf_vmap(obj->import_attach->dmabuf);
|
||||
else
|
||||
shmem->vaddr = vmap(shmem->pages, obj->size >> PAGE_SHIFT, VM_MAP, PAGE_KERNEL);
|
||||
|
||||
if (!shmem->vaddr) {
|
||||
DRM_DEBUG_KMS("Failed to vmap pages\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_put_pages;
|
||||
}
|
||||
|
||||
return shmem->vaddr;
|
||||
|
||||
err_put_pages:
|
||||
drm_gem_shmem_put_pages(shmem);
|
||||
err_zero_use:
|
||||
shmem->vmap_use_count = 0;
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_gem_shmem_vmap - Create a virtual mapping for a shmem GEM object
|
||||
* @shmem: shmem GEM object
|
||||
*
|
||||
* This function makes sure that a virtual address exists for the buffer backing
|
||||
* the shmem GEM object.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
void *drm_gem_shmem_vmap(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
void *vaddr;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&shmem->vmap_lock);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
vaddr = drm_gem_shmem_vmap_locked(shmem);
|
||||
mutex_unlock(&shmem->vmap_lock);
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_vmap);
|
||||
|
||||
static void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem)
|
||||
{
|
||||
struct drm_gem_object *obj = &shmem->base;
|
||||
|
||||
if (WARN_ON_ONCE(!shmem->vmap_use_count))
|
||||
return;
|
||||
|
||||
if (--shmem->vmap_use_count > 0)
|
||||
return;
|
||||
|
||||
if (obj->import_attach)
|
||||
dma_buf_vunmap(obj->import_attach->dmabuf, shmem->vaddr);
|
||||
else
|
||||
vunmap(shmem->vaddr);
|
||||
|
||||
shmem->vaddr = NULL;
|
||||
drm_gem_shmem_put_pages(shmem);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_gem_shmem_vunmap - Unmap a virtual mapping fo a shmem GEM object
|
||||
* @shmem: shmem GEM object
|
||||
*
|
||||
* This function removes the virtual address when use count drops to zero.
|
||||
*/
|
||||
void drm_gem_shmem_vunmap(struct drm_gem_object *obj, void *vaddr)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
mutex_lock(&shmem->vmap_lock);
|
||||
drm_gem_shmem_vunmap_locked(shmem);
|
||||
mutex_unlock(&shmem->vmap_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_vunmap);
|
||||
|
||||
struct drm_gem_shmem_object *
|
||||
drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
|
||||
struct drm_device *dev, size_t size,
|
||||
uint32_t *handle)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
int ret;
|
||||
|
||||
shmem = drm_gem_shmem_create(dev, size);
|
||||
if (IS_ERR(shmem))
|
||||
return shmem;
|
||||
|
||||
/*
|
||||
* Allocate an id of idr table where the obj is registered
|
||||
* and handle has the id what user can see.
|
||||
*/
|
||||
ret = drm_gem_handle_create(file_priv, &shmem->base, handle);
|
||||
/* drop reference from allocate - handle holds it now. */
|
||||
drm_gem_object_put_unlocked(&shmem->base);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return shmem;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_create_with_handle);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
|
||||
* @file: DRM file structure to create the dumb buffer for
|
||||
* @dev: DRM device
|
||||
* @args: IOCTL data
|
||||
*
|
||||
* This function computes the pitch of the dumb buffer and rounds it up to an
|
||||
* integer number of bytes per pixel. Drivers for hardware that doesn't have
|
||||
* any additional restrictions on the pitch can directly use this function as
|
||||
* their &drm_driver.dumb_create callback.
|
||||
*
|
||||
* For hardware with additional restrictions, drivers can adjust the fields
|
||||
* set up by userspace before calling into this function.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
|
||||
if (!args->pitch || !args->size) {
|
||||
args->pitch = min_pitch;
|
||||
args->size = args->pitch * args->height;
|
||||
} else {
|
||||
/* ensure sane minimum values */
|
||||
if (args->pitch < min_pitch)
|
||||
args->pitch = min_pitch;
|
||||
if (args->size < args->pitch * args->height)
|
||||
args->size = args->pitch * args->height;
|
||||
}
|
||||
|
||||
shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle);
|
||||
|
||||
return PTR_ERR_OR_ZERO(shmem);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create);
|
||||
|
||||
static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
|
||||
{
|
||||
struct vm_area_struct *vma = vmf->vma;
|
||||
struct drm_gem_object *obj = vma->vm_private_data;
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
loff_t num_pages = obj->size >> PAGE_SHIFT;
|
||||
struct page *page;
|
||||
|
||||
if (vmf->pgoff > num_pages || WARN_ON_ONCE(!shmem->pages))
|
||||
return VM_FAULT_SIGBUS;
|
||||
|
||||
page = shmem->pages[vmf->pgoff];
|
||||
|
||||
return vmf_insert_page(vma, vmf->address, page);
|
||||
}
|
||||
|
||||
static void drm_gem_shmem_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_object *obj = vma->vm_private_data;
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_shmem_get_pages(shmem);
|
||||
WARN_ON_ONCE(ret != 0);
|
||||
|
||||
drm_gem_vm_open(vma);
|
||||
}
|
||||
|
||||
static void drm_gem_shmem_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_object *obj = vma->vm_private_data;
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
drm_gem_shmem_put_pages(shmem);
|
||||
drm_gem_vm_close(vma);
|
||||
}
|
||||
|
||||
const struct vm_operations_struct drm_gem_shmem_vm_ops = {
|
||||
.fault = drm_gem_shmem_fault,
|
||||
.open = drm_gem_shmem_vm_open,
|
||||
.close = drm_gem_shmem_vm_close,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_ops);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_mmap - Memory-map a shmem GEM object
|
||||
* @filp: File object
|
||||
* @vma: VMA for the area to be mapped
|
||||
*
|
||||
* This function implements an augmented version of the GEM DRM file mmap
|
||||
* operation for shmem objects. Drivers which employ the shmem helpers should
|
||||
* use this function as their &file_operations.mmap handler in the DRM device file's
|
||||
* file_operations structure.
|
||||
*
|
||||
* Instead of directly referencing this function, drivers should use the
|
||||
* DEFINE_DRM_GEM_SHMEM_FOPS() macro.
|
||||
*
|
||||
* Returns:
|
||||
* 0 on success or a negative error code on failure.
|
||||
*/
|
||||
int drm_gem_shmem_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap(filp, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
shmem = to_drm_gem_shmem_obj(vma->vm_private_data);
|
||||
|
||||
ret = drm_gem_shmem_get_pages(shmem);
|
||||
if (ret) {
|
||||
drm_gem_vm_close(vma);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VM_PFNMAP was set by drm_gem_mmap() */
|
||||
vma->vm_flags &= ~VM_PFNMAP;
|
||||
vma->vm_flags |= VM_MIXEDMAP;
|
||||
|
||||
/* Remove the fake offset */
|
||||
vma->vm_pgoff -= drm_vma_node_start(&shmem->base.vma_node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_print_info() - Print &drm_gem_shmem_object info for debugfs
|
||||
* @p: DRM printer
|
||||
* @indent: Tab indentation level
|
||||
* @obj: GEM object
|
||||
*/
|
||||
void drm_gem_shmem_print_info(struct drm_printer *p, unsigned int indent,
|
||||
const struct drm_gem_object *obj)
|
||||
{
|
||||
const struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
drm_printf_indent(p, indent, "pages_use_count=%u\n", shmem->pages_use_count);
|
||||
drm_printf_indent(p, indent, "vmap_use_count=%u\n", shmem->vmap_use_count);
|
||||
drm_printf_indent(p, indent, "vaddr=%p\n", shmem->vaddr);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_gem_shmem_print_info);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_get_sg_table - Provide a scatter/gather table of pinned
|
||||
* pages for a shmem GEM object
|
||||
* @obj: GEM object
|
||||
*
|
||||
* This function exports a scatter/gather table suitable for PRIME usage by
|
||||
* calling the standard DMA mapping API.
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to the scatter/gather table of pinned pages or NULL on failure.
|
||||
*/
|
||||
struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
|
||||
return drm_prime_pages_to_sg(shmem->pages, obj->size >> PAGE_SHIFT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_get_sg_table);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_get_pages_sgt - Pin pages, dma map them, and return a
|
||||
* scatter/gather table for a shmem GEM object.
|
||||
* @obj: GEM object
|
||||
*
|
||||
* This function returns a scatter/gather table suitable for driver usage. If
|
||||
* the sg table doesn't exist, the pages are pinned, dma-mapped, and a sg
|
||||
* table created.
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to the scatter/gather table of pinned pages or errno on failure.
|
||||
*/
|
||||
struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_object *obj)
|
||||
{
|
||||
int ret;
|
||||
struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
|
||||
struct sg_table *sgt;
|
||||
|
||||
if (shmem->sgt)
|
||||
return shmem->sgt;
|
||||
|
||||
WARN_ON(obj->import_attach);
|
||||
|
||||
ret = drm_gem_shmem_get_pages(shmem);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
sgt = drm_gem_shmem_get_sg_table(&shmem->base);
|
||||
if (IS_ERR(sgt)) {
|
||||
ret = PTR_ERR(sgt);
|
||||
goto err_put_pages;
|
||||
}
|
||||
/* Map the pages for use by the h/w. */
|
||||
dma_map_sg(obj->dev->dev, sgt->sgl, sgt->nents, DMA_BIDIRECTIONAL);
|
||||
|
||||
shmem->sgt = sgt;
|
||||
|
||||
return sgt;
|
||||
|
||||
err_put_pages:
|
||||
drm_gem_shmem_put_pages(shmem);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_get_pages_sgt);
|
||||
|
||||
/**
|
||||
* drm_gem_shmem_prime_import_sg_table - Produce a shmem GEM object from
|
||||
* another driver's scatter/gather table of pinned pages
|
||||
* @dev: Device to import into
|
||||
* @attach: DMA-BUF attachment
|
||||
* @sgt: Scatter/gather table of pinned pages
|
||||
*
|
||||
* This function imports a scatter/gather table exported via DMA-BUF by
|
||||
* another driver. Drivers that use the shmem helpers should set this as their
|
||||
* &drm_driver.gem_prime_import_sg_table callback.
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to a newly created GEM object or an ERR_PTR-encoded negative
|
||||
* error code on failure.
|
||||
*/
|
||||
struct drm_gem_object *
|
||||
drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach,
|
||||
struct sg_table *sgt)
|
||||
{
|
||||
size_t size = PAGE_ALIGN(attach->dmabuf->size);
|
||||
size_t npages = size >> PAGE_SHIFT;
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
int ret;
|
||||
|
||||
shmem = drm_gem_shmem_create(dev, size);
|
||||
if (IS_ERR(shmem))
|
||||
return ERR_CAST(shmem);
|
||||
|
||||
shmem->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
|
||||
if (!shmem->pages) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_gem;
|
||||
}
|
||||
|
||||
ret = drm_prime_sg_to_page_addr_arrays(sgt, shmem->pages, NULL, npages);
|
||||
if (ret < 0)
|
||||
goto err_free_array;
|
||||
|
||||
shmem->sgt = sgt;
|
||||
shmem->pages_use_count = 1; /* Permanently pinned from our point of view */
|
||||
|
||||
DRM_DEBUG_PRIME("size = %zu\n", size);
|
||||
|
||||
return &shmem->base;
|
||||
|
||||
err_free_array:
|
||||
kvfree(shmem->pages);
|
||||
err_free_gem:
|
||||
drm_gem_object_put_unlocked(&shmem->base);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_shmem_prime_import_sg_table);
|
@ -39,7 +39,7 @@ MODULE_LICENSE("GPL and additional rights");
|
||||
/* Backward compatibility for drm_kms_helper.edid_firmware */
|
||||
static int edid_firmware_set(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware intead.\n");
|
||||
DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware instead.\n");
|
||||
|
||||
return __drm_set_edid_firmware_path(val);
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/export.h>
|
||||
#include <xen/xen.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "drm_legacy.h"
|
||||
|
||||
@ -150,15 +151,27 @@ void drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL(drm_legacy_ioremapfree);
|
||||
|
||||
u64 drm_get_max_iomem(void)
|
||||
bool drm_need_swiotlb(int dma_bits)
|
||||
{
|
||||
struct resource *tmp;
|
||||
resource_size_t max_iomem = 0;
|
||||
|
||||
/*
|
||||
* Xen paravirtual hosts require swiotlb regardless of requested dma
|
||||
* transfer size.
|
||||
*
|
||||
* NOTE: Really, what it requires is use of the dma_alloc_coherent
|
||||
* allocator used in ttm_dma_populate() instead of
|
||||
* ttm_populate_and_map_pages(), which bounce buffers so much in
|
||||
* Xen it leads to swiotlb buffer exhaustion.
|
||||
*/
|
||||
if (xen_pv_domain())
|
||||
return true;
|
||||
|
||||
for (tmp = iomem_resource.child; tmp; tmp = tmp->sibling) {
|
||||
max_iomem = max(max_iomem, tmp->end);
|
||||
}
|
||||
|
||||
return max_iomem;
|
||||
return max_iomem > ((u64)1 << dma_bits);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_get_max_iomem);
|
||||
EXPORT_SYMBOL(drm_need_swiotlb);
|
||||
|
@ -80,6 +80,12 @@ static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = {
|
||||
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
|
||||
};
|
||||
|
||||
static const struct drm_dmi_panel_orientation_data lcd1200x1920_rightside_up = {
|
||||
.width = 1200,
|
||||
.height = 1920,
|
||||
.orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
|
||||
};
|
||||
|
||||
static const struct dmi_system_id orientation_data[] = {
|
||||
{ /* Acer One 10 (S1003) */
|
||||
.matches = {
|
||||
@ -148,6 +154,13 @@ static const struct dmi_system_id orientation_data[] = {
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
|
||||
},
|
||||
.driver_data = (void *)&lcd800x1280_rightside_up,
|
||||
}, { /* Lenovo Ideapad D330 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81H3"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
|
||||
},
|
||||
.driver_data = (void *)&lcd1200x1920_rightside_up,
|
||||
}, { /* VIOS LTH17 */
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"),
|
||||
|
@ -504,6 +504,7 @@ struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
|
||||
.size = obj->size,
|
||||
.flags = flags,
|
||||
.priv = obj,
|
||||
.resv = obj->resv,
|
||||
};
|
||||
|
||||
if (dev->driver->gem_prime_res_obj)
|
||||
|
@ -731,7 +731,7 @@ cleanup_entries:
|
||||
*
|
||||
* Calculate the timeout in jiffies from an absolute time in sec/nsec.
|
||||
*/
|
||||
static signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
|
||||
signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
|
||||
{
|
||||
ktime_t abs_timeout, now;
|
||||
u64 timeout_ns, timeout_jiffies64;
|
||||
@ -755,6 +755,7 @@ static signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec)
|
||||
|
||||
return timeout_jiffies64 + 1;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_timeout_abs_to_jiffies);
|
||||
|
||||
static int drm_syncobj_array_wait(struct drm_device *dev,
|
||||
struct drm_file *file_private,
|
||||
|
@ -584,8 +584,8 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
|
||||
vma->vm_ops = &drm_vm_ops;
|
||||
break;
|
||||
}
|
||||
/* fall through to _DRM_FRAME_BUFFER... */
|
||||
#endif
|
||||
/* fall through - to _DRM_FRAME_BUFFER... */
|
||||
case _DRM_FRAME_BUFFER:
|
||||
case _DRM_REGISTERS:
|
||||
offset = drm_core_get_reg_ofs(dev);
|
||||
@ -610,7 +610,7 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
|
||||
vma->vm_end - vma->vm_start, vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
vma->vm_page_prot = drm_dma_prot(map->type, vma);
|
||||
/* fall through to _DRM_SHM */
|
||||
/* fall through - to _DRM_SHM */
|
||||
case _DRM_SHM:
|
||||
vma->vm_ops = &drm_vm_shm_ops;
|
||||
vma->vm_private_data = (void *)map;
|
||||
|
@ -473,7 +473,6 @@ static struct drm_driver etnaviv_drm_driver = {
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_res_obj = etnaviv_gem_prime_res_obj,
|
||||
.gem_prime_pin = etnaviv_gem_prime_pin,
|
||||
.gem_prime_unpin = etnaviv_gem_prime_unpin,
|
||||
.gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table,
|
||||
|
@ -60,7 +60,6 @@ void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj);
|
||||
void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
|
||||
int etnaviv_gem_prime_mmap(struct drm_gem_object *obj,
|
||||
struct vm_area_struct *vma);
|
||||
struct reservation_object *etnaviv_gem_prime_res_obj(struct drm_gem_object *obj);
|
||||
struct drm_gem_object *etnaviv_gem_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach, struct sg_table *sg);
|
||||
int etnaviv_gem_prime_pin(struct drm_gem_object *obj);
|
||||
|
@ -397,13 +397,13 @@ int etnaviv_gem_cpu_prep(struct drm_gem_object *obj, u32 op,
|
||||
}
|
||||
|
||||
if (op & ETNA_PREP_NOSYNC) {
|
||||
if (!reservation_object_test_signaled_rcu(etnaviv_obj->resv,
|
||||
if (!reservation_object_test_signaled_rcu(obj->resv,
|
||||
write))
|
||||
return -EBUSY;
|
||||
} else {
|
||||
unsigned long remain = etnaviv_timeout_to_jiffies(timeout);
|
||||
|
||||
ret = reservation_object_wait_timeout_rcu(etnaviv_obj->resv,
|
||||
ret = reservation_object_wait_timeout_rcu(obj->resv,
|
||||
write, true, remain);
|
||||
if (ret <= 0)
|
||||
return ret == 0 ? -ETIMEDOUT : ret;
|
||||
@ -459,7 +459,7 @@ static void etnaviv_gem_describe_fence(struct dma_fence *fence,
|
||||
static void etnaviv_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
|
||||
{
|
||||
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
|
||||
struct reservation_object *robj = etnaviv_obj->resv;
|
||||
struct reservation_object *robj = obj->resv;
|
||||
struct reservation_object_list *fobj;
|
||||
struct dma_fence *fence;
|
||||
unsigned long off = drm_vma_node_start(&obj->vma_node);
|
||||
@ -549,8 +549,6 @@ void etnaviv_gem_free_object(struct drm_gem_object *obj)
|
||||
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
etnaviv_obj->ops->release(etnaviv_obj);
|
||||
if (etnaviv_obj->resv == &etnaviv_obj->_resv)
|
||||
reservation_object_fini(&etnaviv_obj->_resv);
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
kfree(etnaviv_obj);
|
||||
@ -596,12 +594,8 @@ static int etnaviv_gem_new_impl(struct drm_device *dev, u32 size, u32 flags,
|
||||
|
||||
etnaviv_obj->flags = flags;
|
||||
etnaviv_obj->ops = ops;
|
||||
if (robj) {
|
||||
etnaviv_obj->resv = robj;
|
||||
} else {
|
||||
etnaviv_obj->resv = &etnaviv_obj->_resv;
|
||||
reservation_object_init(&etnaviv_obj->_resv);
|
||||
}
|
||||
if (robj)
|
||||
etnaviv_obj->base.resv = robj;
|
||||
|
||||
mutex_init(&etnaviv_obj->lock);
|
||||
INIT_LIST_HEAD(&etnaviv_obj->vram_list);
|
||||
|
@ -47,10 +47,6 @@ struct etnaviv_gem_object {
|
||||
struct sg_table *sgt;
|
||||
void *vaddr;
|
||||
|
||||
/* normally (resv == &_resv) except for imported bo's */
|
||||
struct reservation_object *resv;
|
||||
struct reservation_object _resv;
|
||||
|
||||
struct list_head vram_list;
|
||||
|
||||
/* cache maintenance */
|
||||
|
@ -139,10 +139,3 @@ fail:
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
struct reservation_object *etnaviv_gem_prime_res_obj(struct drm_gem_object *obj)
|
||||
{
|
||||
struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
|
||||
|
||||
return etnaviv_obj->resv;
|
||||
}
|
||||
|
@ -108,9 +108,9 @@ out_unlock:
|
||||
static void submit_unlock_object(struct etnaviv_gem_submit *submit, int i)
|
||||
{
|
||||
if (submit->bos[i].flags & BO_LOCKED) {
|
||||
struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
|
||||
struct drm_gem_object *obj = &submit->bos[i].obj->base;
|
||||
|
||||
ww_mutex_unlock(&etnaviv_obj->resv->lock);
|
||||
ww_mutex_unlock(&obj->resv->lock);
|
||||
submit->bos[i].flags &= ~BO_LOCKED;
|
||||
}
|
||||
}
|
||||
@ -122,7 +122,7 @@ static int submit_lock_objects(struct etnaviv_gem_submit *submit,
|
||||
|
||||
retry:
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
|
||||
struct drm_gem_object *obj = &submit->bos[i].obj->base;
|
||||
|
||||
if (slow_locked == i)
|
||||
slow_locked = -1;
|
||||
@ -130,7 +130,7 @@ retry:
|
||||
contended = i;
|
||||
|
||||
if (!(submit->bos[i].flags & BO_LOCKED)) {
|
||||
ret = ww_mutex_lock_interruptible(&etnaviv_obj->resv->lock,
|
||||
ret = ww_mutex_lock_interruptible(&obj->resv->lock,
|
||||
ticket);
|
||||
if (ret == -EALREADY)
|
||||
DRM_ERROR("BO at index %u already on submit list\n",
|
||||
@ -153,12 +153,12 @@ fail:
|
||||
submit_unlock_object(submit, slow_locked);
|
||||
|
||||
if (ret == -EDEADLK) {
|
||||
struct etnaviv_gem_object *etnaviv_obj;
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
etnaviv_obj = submit->bos[contended].obj;
|
||||
obj = &submit->bos[contended].obj->base;
|
||||
|
||||
/* we lost out in a seqno race, lock and retry.. */
|
||||
ret = ww_mutex_lock_slow_interruptible(&etnaviv_obj->resv->lock,
|
||||
ret = ww_mutex_lock_slow_interruptible(&obj->resv->lock,
|
||||
ticket);
|
||||
if (!ret) {
|
||||
submit->bos[contended].flags |= BO_LOCKED;
|
||||
@ -176,7 +176,7 @@ static int submit_fence_sync(struct etnaviv_gem_submit *submit)
|
||||
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
struct etnaviv_gem_submit_bo *bo = &submit->bos[i];
|
||||
struct reservation_object *robj = bo->obj->resv;
|
||||
struct reservation_object *robj = bo->obj->base.resv;
|
||||
|
||||
if (!(bo->flags & ETNA_SUBMIT_BO_WRITE)) {
|
||||
ret = reservation_object_reserve_shared(robj, 1);
|
||||
@ -207,13 +207,13 @@ static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < submit->nr_bos; i++) {
|
||||
struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
|
||||
struct drm_gem_object *obj = &submit->bos[i].obj->base;
|
||||
|
||||
if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
|
||||
reservation_object_add_excl_fence(etnaviv_obj->resv,
|
||||
reservation_object_add_excl_fence(obj->resv,
|
||||
submit->out_fence);
|
||||
else
|
||||
reservation_object_add_shared_fence(etnaviv_obj->resv,
|
||||
reservation_object_add_shared_fence(obj->resv,
|
||||
submit->out_fence);
|
||||
|
||||
submit_unlock_object(submit, i);
|
||||
|
@ -6557,13 +6557,22 @@ enum {
|
||||
#define PLANE_CTL_FORMAT_YUV422 (0 << 24)
|
||||
#define PLANE_CTL_FORMAT_NV12 (1 << 24)
|
||||
#define PLANE_CTL_FORMAT_XRGB_2101010 (2 << 24)
|
||||
#define PLANE_CTL_FORMAT_P010 (3 << 24)
|
||||
#define PLANE_CTL_FORMAT_XRGB_8888 (4 << 24)
|
||||
#define PLANE_CTL_FORMAT_P012 (5 << 24)
|
||||
#define PLANE_CTL_FORMAT_XRGB_16161616F (6 << 24)
|
||||
#define PLANE_CTL_FORMAT_P016 (7 << 24)
|
||||
#define PLANE_CTL_FORMAT_AYUV (8 << 24)
|
||||
#define PLANE_CTL_FORMAT_INDEXED (12 << 24)
|
||||
#define PLANE_CTL_FORMAT_RGB_565 (14 << 24)
|
||||
#define ICL_PLANE_CTL_FORMAT_MASK (0x1f << 23)
|
||||
#define PLANE_CTL_PIPE_CSC_ENABLE (1 << 23) /* Pre-GLK */
|
||||
#define PLANE_CTL_FORMAT_Y210 (1 << 23)
|
||||
#define PLANE_CTL_FORMAT_Y212 (3 << 23)
|
||||
#define PLANE_CTL_FORMAT_Y216 (5 << 23)
|
||||
#define PLANE_CTL_FORMAT_Y410 (7 << 23)
|
||||
#define PLANE_CTL_FORMAT_Y412 (9 << 23)
|
||||
#define PLANE_CTL_FORMAT_Y416 (0xb << 23)
|
||||
#define PLANE_CTL_KEY_ENABLE_MASK (0x3 << 21)
|
||||
#define PLANE_CTL_KEY_ENABLE_SOURCE (1 << 21)
|
||||
#define PLANE_CTL_KEY_ENABLE_DESTINATION (2 << 21)
|
||||
|
@ -126,6 +126,7 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
|
||||
*/
|
||||
if (new_conn_state->force_audio != old_conn_state->force_audio ||
|
||||
new_conn_state->broadcast_rgb != old_conn_state->broadcast_rgb ||
|
||||
new_conn_state->base.colorspace != old_conn_state->base.colorspace ||
|
||||
new_conn_state->base.picture_aspect_ratio != old_conn_state->base.picture_aspect_ratio ||
|
||||
new_conn_state->base.content_type != old_conn_state->base.content_type ||
|
||||
new_conn_state->base.scaling_mode != old_conn_state->base.scaling_mode)
|
||||
@ -234,10 +235,11 @@ static void intel_atomic_setup_scaler(struct intel_crtc_scaler_state *scaler_sta
|
||||
if (plane_state && plane_state->base.fb &&
|
||||
plane_state->base.fb->format->is_yuv &&
|
||||
plane_state->base.fb->format->num_planes > 1) {
|
||||
struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
|
||||
if (IS_GEN(dev_priv, 9) &&
|
||||
!IS_GEMINILAKE(dev_priv)) {
|
||||
mode = SKL_PS_SCALER_MODE_NV12;
|
||||
} else if (icl_is_hdr_plane(to_intel_plane(plane_state->base.plane))) {
|
||||
} else if (icl_is_hdr_plane(dev_priv, plane->id)) {
|
||||
/*
|
||||
* On gen11+'s HDR planes we only use the scaler for
|
||||
* scaling. They have a dedicated chroma upsampler, so
|
||||
|
@ -135,7 +135,7 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
|
||||
new_crtc_state->active_planes |= BIT(plane->id);
|
||||
|
||||
if (new_plane_state->base.visible &&
|
||||
new_plane_state->base.fb->format->format == DRM_FORMAT_NV12)
|
||||
is_planar_yuv_format(new_plane_state->base.fb->format->format))
|
||||
new_crtc_state->nv12_planes |= BIT(plane->id);
|
||||
|
||||
if (new_plane_state->base.visible || old_plane_state->base.visible)
|
||||
|
@ -265,3 +265,11 @@ intel_attach_aspect_ratio_property(struct drm_connector *connector)
|
||||
connector->dev->mode_config.aspect_ratio_property,
|
||||
DRM_MODE_PICTURE_ASPECT_NONE);
|
||||
}
|
||||
|
||||
void
|
||||
intel_attach_colorspace_property(struct drm_connector *connector)
|
||||
{
|
||||
if (!drm_mode_create_colorspace_property(connector))
|
||||
drm_object_attach_property(&connector->base,
|
||||
connector->colorspace_property, 0);
|
||||
}
|
||||
|
@ -2677,6 +2677,24 @@ int skl_format_to_fourcc(int format, bool rgb_order, bool alpha)
|
||||
return DRM_FORMAT_RGB565;
|
||||
case PLANE_CTL_FORMAT_NV12:
|
||||
return DRM_FORMAT_NV12;
|
||||
case PLANE_CTL_FORMAT_P010:
|
||||
return DRM_FORMAT_P010;
|
||||
case PLANE_CTL_FORMAT_P012:
|
||||
return DRM_FORMAT_P012;
|
||||
case PLANE_CTL_FORMAT_P016:
|
||||
return DRM_FORMAT_P016;
|
||||
case PLANE_CTL_FORMAT_Y210:
|
||||
return DRM_FORMAT_Y210;
|
||||
case PLANE_CTL_FORMAT_Y212:
|
||||
return DRM_FORMAT_Y212;
|
||||
case PLANE_CTL_FORMAT_Y216:
|
||||
return DRM_FORMAT_Y216;
|
||||
case PLANE_CTL_FORMAT_Y410:
|
||||
return DRM_FORMAT_XVYU2101010;
|
||||
case PLANE_CTL_FORMAT_Y412:
|
||||
return DRM_FORMAT_XVYU12_16161616;
|
||||
case PLANE_CTL_FORMAT_Y416:
|
||||
return DRM_FORMAT_XVYU16161616;
|
||||
default:
|
||||
case PLANE_CTL_FORMAT_XRGB_8888:
|
||||
if (rgb_order) {
|
||||
@ -2695,6 +2713,18 @@ int skl_format_to_fourcc(int format, bool rgb_order, bool alpha)
|
||||
return DRM_FORMAT_XBGR2101010;
|
||||
else
|
||||
return DRM_FORMAT_XRGB2101010;
|
||||
case PLANE_CTL_FORMAT_XRGB_16161616F:
|
||||
if (rgb_order) {
|
||||
if (alpha)
|
||||
return DRM_FORMAT_ABGR16161616F;
|
||||
else
|
||||
return DRM_FORMAT_XBGR16161616F;
|
||||
} else {
|
||||
if (alpha)
|
||||
return DRM_FORMAT_ARGB16161616F;
|
||||
else
|
||||
return DRM_FORMAT_XRGB16161616F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3176,7 +3206,7 @@ int skl_check_plane_surface(struct intel_plane_state *plane_state)
|
||||
* Handle the AUX surface first since
|
||||
* the main surface setup depends on it.
|
||||
*/
|
||||
if (fb->format->format == DRM_FORMAT_NV12) {
|
||||
if (is_planar_yuv_format(fb->format->format)) {
|
||||
ret = skl_check_nv12_aux_surface(plane_state);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -3590,6 +3620,12 @@ static u32 skl_plane_ctl_format(u32 pixel_format)
|
||||
return PLANE_CTL_FORMAT_XRGB_2101010;
|
||||
case DRM_FORMAT_XBGR2101010:
|
||||
return PLANE_CTL_ORDER_RGBX | PLANE_CTL_FORMAT_XRGB_2101010;
|
||||
case DRM_FORMAT_XBGR16161616F:
|
||||
case DRM_FORMAT_ABGR16161616F:
|
||||
return PLANE_CTL_FORMAT_XRGB_16161616F | PLANE_CTL_ORDER_RGBX;
|
||||
case DRM_FORMAT_XRGB16161616F:
|
||||
case DRM_FORMAT_ARGB16161616F:
|
||||
return PLANE_CTL_FORMAT_XRGB_16161616F;
|
||||
case DRM_FORMAT_YUYV:
|
||||
return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_YUYV;
|
||||
case DRM_FORMAT_YVYU:
|
||||
@ -3600,6 +3636,24 @@ static u32 skl_plane_ctl_format(u32 pixel_format)
|
||||
return PLANE_CTL_FORMAT_YUV422 | PLANE_CTL_YUV422_VYUY;
|
||||
case DRM_FORMAT_NV12:
|
||||
return PLANE_CTL_FORMAT_NV12;
|
||||
case DRM_FORMAT_P010:
|
||||
return PLANE_CTL_FORMAT_P010;
|
||||
case DRM_FORMAT_P012:
|
||||
return PLANE_CTL_FORMAT_P012;
|
||||
case DRM_FORMAT_P016:
|
||||
return PLANE_CTL_FORMAT_P016;
|
||||
case DRM_FORMAT_Y210:
|
||||
return PLANE_CTL_FORMAT_Y210;
|
||||
case DRM_FORMAT_Y212:
|
||||
return PLANE_CTL_FORMAT_Y212;
|
||||
case DRM_FORMAT_Y216:
|
||||
return PLANE_CTL_FORMAT_Y216;
|
||||
case DRM_FORMAT_XVYU2101010:
|
||||
return PLANE_CTL_FORMAT_Y410;
|
||||
case DRM_FORMAT_XVYU12_16161616:
|
||||
return PLANE_CTL_FORMAT_Y412;
|
||||
case DRM_FORMAT_XVYU16161616:
|
||||
return PLANE_CTL_FORMAT_Y416;
|
||||
default:
|
||||
MISSING_CASE(pixel_format);
|
||||
}
|
||||
@ -3772,6 +3826,8 @@ u32 glk_plane_color_ctl_crtc(const struct intel_crtc_state *crtc_state)
|
||||
u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
|
||||
const struct intel_plane_state *plane_state)
|
||||
{
|
||||
struct drm_i915_private *dev_priv =
|
||||
to_i915(plane_state->base.plane->dev);
|
||||
const struct drm_framebuffer *fb = plane_state->base.fb;
|
||||
struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
|
||||
u32 plane_color_ctl = 0;
|
||||
@ -3779,7 +3835,7 @@ u32 glk_plane_color_ctl(const struct intel_crtc_state *crtc_state,
|
||||
plane_color_ctl |= PLANE_COLOR_PLANE_GAMMA_DISABLE;
|
||||
plane_color_ctl |= glk_plane_color_ctl_alpha(plane_state);
|
||||
|
||||
if (fb->format->is_yuv && !icl_is_hdr_plane(plane)) {
|
||||
if (fb->format->is_yuv && !icl_is_hdr_plane(dev_priv, plane->id)) {
|
||||
if (plane_state->base.color_encoding == DRM_COLOR_YCBCR_BT709)
|
||||
plane_color_ctl |= PLANE_COLOR_CSC_MODE_YUV709_TO_RGB709;
|
||||
else
|
||||
@ -5036,9 +5092,9 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (format && format->format == DRM_FORMAT_NV12 &&
|
||||
if (format && is_planar_yuv_format(format->format) &&
|
||||
(src_h < SKL_MIN_YUV_420_SRC_H || src_w < SKL_MIN_YUV_420_SRC_W)) {
|
||||
DRM_DEBUG_KMS("NV12: src dimensions not met\n");
|
||||
DRM_DEBUG_KMS("Planar YUV: src dimensions not met\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -5105,14 +5161,15 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
|
||||
{
|
||||
struct intel_plane *intel_plane =
|
||||
to_intel_plane(plane_state->base.plane);
|
||||
struct drm_i915_private *dev_priv = to_i915(intel_plane->base.dev);
|
||||
struct drm_framebuffer *fb = plane_state->base.fb;
|
||||
int ret;
|
||||
bool force_detach = !fb || !plane_state->base.visible;
|
||||
bool need_scaler = false;
|
||||
|
||||
/* Pre-gen11 and SDR planes always need a scaler for planar formats. */
|
||||
if (!icl_is_hdr_plane(intel_plane) &&
|
||||
fb && fb->format->format == DRM_FORMAT_NV12)
|
||||
if (!icl_is_hdr_plane(dev_priv, intel_plane->id) &&
|
||||
fb && is_planar_yuv_format(fb->format->format))
|
||||
need_scaler = true;
|
||||
|
||||
ret = skl_update_scaler(crtc_state, force_detach,
|
||||
@ -5144,11 +5201,24 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
case DRM_FORMAT_XBGR2101010:
|
||||
case DRM_FORMAT_XBGR16161616F:
|
||||
case DRM_FORMAT_ABGR16161616F:
|
||||
case DRM_FORMAT_XRGB16161616F:
|
||||
case DRM_FORMAT_ARGB16161616F:
|
||||
case DRM_FORMAT_YUYV:
|
||||
case DRM_FORMAT_YVYU:
|
||||
case DRM_FORMAT_UYVY:
|
||||
case DRM_FORMAT_VYUY:
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_P010:
|
||||
case DRM_FORMAT_P012:
|
||||
case DRM_FORMAT_P016:
|
||||
case DRM_FORMAT_Y210:
|
||||
case DRM_FORMAT_Y212:
|
||||
case DRM_FORMAT_Y216:
|
||||
case DRM_FORMAT_XVYU2101010:
|
||||
case DRM_FORMAT_XVYU12_16161616:
|
||||
case DRM_FORMAT_XVYU16161616:
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_KMS("[PLANE:%d:%s] FB:%d unsupported scaling format 0x%x\n",
|
||||
@ -11134,7 +11204,7 @@ static int icl_check_nv12_planes(struct intel_crtc_state *crtc_state)
|
||||
}
|
||||
|
||||
if (!linked_state) {
|
||||
DRM_DEBUG_KMS("Need %d free Y planes for NV12\n",
|
||||
DRM_DEBUG_KMS("Need %d free Y planes for planar YUV\n",
|
||||
hweight8(crtc_state->nv12_planes));
|
||||
|
||||
return -EINVAL;
|
||||
@ -13767,7 +13837,7 @@ skl_max_scale(const struct intel_crtc_state *crtc_state,
|
||||
* or
|
||||
* cdclk/crtc_clock
|
||||
*/
|
||||
mult = pixel_format == DRM_FORMAT_NV12 ? 2 : 3;
|
||||
mult = is_planar_yuv_format(pixel_format) ? 2 : 3;
|
||||
tmpclk1 = (1 << 16) * mult - 1;
|
||||
tmpclk2 = (1 << 8) * ((max_dotclk << 8) / crtc_clock);
|
||||
max_scale = min(tmpclk1, tmpclk2);
|
||||
|
@ -1796,6 +1796,7 @@ int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
|
||||
void intel_attach_force_audio_property(struct drm_connector *connector);
|
||||
void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
|
||||
void intel_attach_aspect_ratio_property(struct drm_connector *connector);
|
||||
void intel_attach_colorspace_property(struct drm_connector *connector);
|
||||
|
||||
/* intel_csr.c */
|
||||
void intel_csr_ucode_init(struct drm_i915_private *);
|
||||
@ -2300,6 +2301,7 @@ bool intel_sdvo_init(struct drm_i915_private *dev_priv,
|
||||
|
||||
|
||||
/* intel_sprite.c */
|
||||
bool is_planar_yuv_format(u32 pixelformat);
|
||||
int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
|
||||
int usecs);
|
||||
struct intel_plane *intel_sprite_plane_create(struct drm_i915_private *dev_priv,
|
||||
@ -2324,12 +2326,13 @@ static inline bool icl_is_nv12_y_plane(enum plane_id id)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool icl_is_hdr_plane(struct intel_plane *plane)
|
||||
static inline bool icl_is_hdr_plane(struct drm_i915_private *dev_priv,
|
||||
enum plane_id plane_id)
|
||||
{
|
||||
if (INTEL_GEN(to_i915(plane->base.dev)) < 11)
|
||||
if (INTEL_GEN(dev_priv) < 11)
|
||||
return false;
|
||||
|
||||
return plane->id < PLANE_SPRITE2;
|
||||
return plane_id < PLANE_SPRITE2;
|
||||
}
|
||||
|
||||
/* intel_tv.c */
|
||||
|
@ -498,6 +498,8 @@ static void intel_hdmi_set_avi_infoframe(struct intel_encoder *encoder,
|
||||
else
|
||||
frame.avi.colorspace = HDMI_COLORSPACE_RGB;
|
||||
|
||||
drm_hdmi_avi_infoframe_colorspace(&frame.avi, conn_state);
|
||||
|
||||
drm_hdmi_avi_infoframe_quant_range(&frame.avi,
|
||||
conn_state->connector,
|
||||
adjusted_mode,
|
||||
@ -2143,10 +2145,21 @@ static void
|
||||
intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->dev);
|
||||
struct intel_digital_port *intel_dig_port =
|
||||
hdmi_to_dig_port(intel_hdmi);
|
||||
|
||||
intel_attach_force_audio_property(connector);
|
||||
intel_attach_broadcast_rgb_property(connector);
|
||||
intel_attach_aspect_ratio_property(connector);
|
||||
|
||||
/*
|
||||
* Attach Colorspace property for Non LSPCON based device
|
||||
* ToDo: This needs to be extended for LSPCON implementation
|
||||
* as well. Will be implemented separately.
|
||||
*/
|
||||
if (!intel_dig_port->lspcon.active)
|
||||
intel_attach_colorspace_property(connector);
|
||||
|
||||
drm_connector_attach_content_type_property(connector);
|
||||
connector->state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
|
||||
|
||||
|
@ -3970,7 +3970,7 @@ skl_ddb_get_hw_plane_state(struct drm_i915_private *dev_priv,
|
||||
val = I915_READ(PLANE_BUF_CFG(pipe, plane_id));
|
||||
val2 = I915_READ(PLANE_NV12_BUF_CFG(pipe, plane_id));
|
||||
|
||||
if (fourcc == DRM_FORMAT_NV12)
|
||||
if (is_planar_yuv_format(fourcc))
|
||||
swap(val, val2);
|
||||
|
||||
skl_ddb_entry_init_from_hw(dev_priv, ddb_y, val);
|
||||
@ -4180,7 +4180,7 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
|
||||
|
||||
if (intel_plane->id == PLANE_CURSOR)
|
||||
return 0;
|
||||
if (plane == 1 && format != DRM_FORMAT_NV12)
|
||||
if (plane == 1 && !is_planar_yuv_format(format))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -4192,7 +4192,7 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
|
||||
height = drm_rect_height(&intel_pstate->base.src) >> 16;
|
||||
|
||||
/* UV plane does 1/2 pixel sub-sampling */
|
||||
if (plane == 1 && format == DRM_FORMAT_NV12) {
|
||||
if (plane == 1 && is_planar_yuv_format(format)) {
|
||||
width /= 2;
|
||||
height /= 2;
|
||||
}
|
||||
@ -4578,9 +4578,9 @@ skl_compute_plane_wm_params(const struct intel_crtc_state *cstate,
|
||||
const struct drm_framebuffer *fb = pstate->fb;
|
||||
u32 interm_pbpl;
|
||||
|
||||
/* only NV12 format has two planes */
|
||||
if (color_plane == 1 && fb->format->format != DRM_FORMAT_NV12) {
|
||||
DRM_DEBUG_KMS("Non NV12 format have single plane\n");
|
||||
/* only planar format has two planes */
|
||||
if (color_plane == 1 && !is_planar_yuv_format(fb->format->format)) {
|
||||
DRM_DEBUG_KMS("Non planar format have single plane\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -4591,7 +4591,7 @@ skl_compute_plane_wm_params(const struct intel_crtc_state *cstate,
|
||||
wp->x_tiled = fb->modifier == I915_FORMAT_MOD_X_TILED;
|
||||
wp->rc_surface = fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
|
||||
fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS;
|
||||
wp->is_planar = fb->format->format == DRM_FORMAT_NV12;
|
||||
wp->is_planar = is_planar_yuv_format(fb->format->format);
|
||||
|
||||
if (plane->id == PLANE_CURSOR) {
|
||||
wp->width = intel_pstate->base.crtc_w;
|
||||
|
@ -41,6 +41,19 @@
|
||||
#include "i915_drv.h"
|
||||
#include <drm/drm_color_mgmt.h>
|
||||
|
||||
bool is_planar_yuv_format(u32 pixelformat)
|
||||
{
|
||||
switch (pixelformat) {
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_P010:
|
||||
case DRM_FORMAT_P012:
|
||||
case DRM_FORMAT_P016:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
|
||||
int usecs)
|
||||
{
|
||||
@ -335,8 +348,8 @@ skl_program_scaler(struct intel_plane *plane,
|
||||
0, INT_MAX);
|
||||
|
||||
/* TODO: handle sub-pixel coordinates */
|
||||
if (plane_state->base.fb->format->format == DRM_FORMAT_NV12 &&
|
||||
!icl_is_hdr_plane(plane)) {
|
||||
if (is_planar_yuv_format(plane_state->base.fb->format->format) &&
|
||||
!icl_is_hdr_plane(dev_priv, plane->id)) {
|
||||
y_hphase = skl_scaler_calc_phase(1, hscale, false);
|
||||
y_vphase = skl_scaler_calc_phase(1, vscale, false);
|
||||
|
||||
@ -518,7 +531,7 @@ skl_program_plane(struct intel_plane *plane,
|
||||
I915_WRITE_FW(PLANE_AUX_DIST(pipe, plane_id),
|
||||
(plane_state->color_plane[1].offset - surf_addr) | aux_stride);
|
||||
|
||||
if (icl_is_hdr_plane(plane)) {
|
||||
if (icl_is_hdr_plane(dev_priv, plane_id)) {
|
||||
u32 cus_ctl = 0;
|
||||
|
||||
if (linked) {
|
||||
@ -542,7 +555,7 @@ skl_program_plane(struct intel_plane *plane,
|
||||
if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
|
||||
I915_WRITE_FW(PLANE_COLOR_CTL(pipe, plane_id), plane_color_ctl);
|
||||
|
||||
if (fb->format->is_yuv && icl_is_hdr_plane(plane))
|
||||
if (fb->format->is_yuv && icl_is_hdr_plane(dev_priv, plane_id))
|
||||
icl_program_input_csc(plane, crtc_state, plane_state);
|
||||
|
||||
skl_write_plane_wm(plane, crtc_state);
|
||||
@ -1482,8 +1495,6 @@ static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state,
|
||||
/*
|
||||
* 90/270 is not allowed with RGB64 16:16:16:16 and
|
||||
* Indexed 8-bit. RGB 16-bit 5:6:5 is allowed gen11 onwards.
|
||||
* TBD: Add RGB64 case once its added in supported format
|
||||
* list.
|
||||
*/
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_RGB565:
|
||||
@ -1491,6 +1502,10 @@ static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state,
|
||||
break;
|
||||
/* fall through */
|
||||
case DRM_FORMAT_C8:
|
||||
case DRM_FORMAT_XRGB16161616F:
|
||||
case DRM_FORMAT_XBGR16161616F:
|
||||
case DRM_FORMAT_ARGB16161616F:
|
||||
case DRM_FORMAT_ABGR16161616F:
|
||||
DRM_DEBUG_KMS("Unsupported pixel format %s for 90/270!\n",
|
||||
drm_get_format_name(fb->format->format,
|
||||
&format_name));
|
||||
@ -1551,10 +1566,10 @@ static int skl_plane_check_nv12_rotation(const struct intel_plane_state *plane_s
|
||||
int src_w = drm_rect_width(&plane_state->base.src) >> 16;
|
||||
|
||||
/* Display WA #1106 */
|
||||
if (fb->format->format == DRM_FORMAT_NV12 && src_w & 3 &&
|
||||
if (is_planar_yuv_format(fb->format->format) && src_w & 3 &&
|
||||
(rotation == DRM_MODE_ROTATE_270 ||
|
||||
rotation == (DRM_MODE_REFLECT_X | DRM_MODE_ROTATE_90))) {
|
||||
DRM_DEBUG_KMS("src width must be multiple of 4 for rotated NV12\n");
|
||||
DRM_DEBUG_KMS("src width must be multiple of 4 for rotated planar YUV\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -1790,6 +1805,52 @@ static const u32 skl_plane_formats[] = {
|
||||
DRM_FORMAT_VYUY,
|
||||
};
|
||||
|
||||
static const uint32_t icl_plane_formats[] = {
|
||||
DRM_FORMAT_C8,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XRGB2101010,
|
||||
DRM_FORMAT_XBGR2101010,
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_VYUY,
|
||||
DRM_FORMAT_Y210,
|
||||
DRM_FORMAT_Y212,
|
||||
DRM_FORMAT_Y216,
|
||||
DRM_FORMAT_XVYU2101010,
|
||||
DRM_FORMAT_XVYU12_16161616,
|
||||
DRM_FORMAT_XVYU16161616,
|
||||
};
|
||||
|
||||
static const uint32_t icl_hdr_plane_formats[] = {
|
||||
DRM_FORMAT_C8,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XRGB2101010,
|
||||
DRM_FORMAT_XBGR2101010,
|
||||
DRM_FORMAT_XRGB16161616F,
|
||||
DRM_FORMAT_XBGR16161616F,
|
||||
DRM_FORMAT_ARGB16161616F,
|
||||
DRM_FORMAT_ABGR16161616F,
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_VYUY,
|
||||
DRM_FORMAT_Y210,
|
||||
DRM_FORMAT_Y212,
|
||||
DRM_FORMAT_Y216,
|
||||
DRM_FORMAT_XVYU2101010,
|
||||
DRM_FORMAT_XVYU12_16161616,
|
||||
DRM_FORMAT_XVYU16161616,
|
||||
};
|
||||
|
||||
static const u32 skl_planar_formats[] = {
|
||||
DRM_FORMAT_C8,
|
||||
DRM_FORMAT_RGB565,
|
||||
@ -1806,6 +1867,79 @@ static const u32 skl_planar_formats[] = {
|
||||
DRM_FORMAT_NV12,
|
||||
};
|
||||
|
||||
static const uint32_t glk_planar_formats[] = {
|
||||
DRM_FORMAT_C8,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XRGB2101010,
|
||||
DRM_FORMAT_XBGR2101010,
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_VYUY,
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_P010,
|
||||
DRM_FORMAT_P012,
|
||||
DRM_FORMAT_P016,
|
||||
};
|
||||
|
||||
static const uint32_t icl_planar_formats[] = {
|
||||
DRM_FORMAT_C8,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XRGB2101010,
|
||||
DRM_FORMAT_XBGR2101010,
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_VYUY,
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_P010,
|
||||
DRM_FORMAT_P012,
|
||||
DRM_FORMAT_P016,
|
||||
DRM_FORMAT_Y210,
|
||||
DRM_FORMAT_Y212,
|
||||
DRM_FORMAT_Y216,
|
||||
DRM_FORMAT_XVYU2101010,
|
||||
DRM_FORMAT_XVYU12_16161616,
|
||||
DRM_FORMAT_XVYU16161616,
|
||||
};
|
||||
|
||||
static const uint32_t icl_hdr_planar_formats[] = {
|
||||
DRM_FORMAT_C8,
|
||||
DRM_FORMAT_RGB565,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
DRM_FORMAT_XRGB2101010,
|
||||
DRM_FORMAT_XBGR2101010,
|
||||
DRM_FORMAT_XRGB16161616F,
|
||||
DRM_FORMAT_XBGR16161616F,
|
||||
DRM_FORMAT_ARGB16161616F,
|
||||
DRM_FORMAT_ABGR16161616F,
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_VYUY,
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_P010,
|
||||
DRM_FORMAT_P012,
|
||||
DRM_FORMAT_P016,
|
||||
DRM_FORMAT_Y210,
|
||||
DRM_FORMAT_Y212,
|
||||
DRM_FORMAT_Y216,
|
||||
DRM_FORMAT_XVYU2101010,
|
||||
DRM_FORMAT_XVYU12_16161616,
|
||||
DRM_FORMAT_XVYU16161616,
|
||||
};
|
||||
|
||||
static const u64 skl_plane_format_modifiers_noccs[] = {
|
||||
I915_FORMAT_MOD_Yf_TILED,
|
||||
I915_FORMAT_MOD_Y_TILED,
|
||||
@ -1945,10 +2079,23 @@ static bool skl_plane_format_mod_supported(struct drm_plane *_plane,
|
||||
case DRM_FORMAT_UYVY:
|
||||
case DRM_FORMAT_VYUY:
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_P010:
|
||||
case DRM_FORMAT_P012:
|
||||
case DRM_FORMAT_P016:
|
||||
case DRM_FORMAT_Y210:
|
||||
case DRM_FORMAT_Y212:
|
||||
case DRM_FORMAT_Y216:
|
||||
case DRM_FORMAT_XVYU2101010:
|
||||
case DRM_FORMAT_XVYU12_16161616:
|
||||
case DRM_FORMAT_XVYU16161616:
|
||||
if (modifier == I915_FORMAT_MOD_Yf_TILED)
|
||||
return true;
|
||||
/* fall through */
|
||||
case DRM_FORMAT_C8:
|
||||
case DRM_FORMAT_XBGR16161616F:
|
||||
case DRM_FORMAT_ABGR16161616F:
|
||||
case DRM_FORMAT_XRGB16161616F:
|
||||
case DRM_FORMAT_ARGB16161616F:
|
||||
if (modifier == DRM_FORMAT_MOD_LINEAR ||
|
||||
modifier == I915_FORMAT_MOD_X_TILED ||
|
||||
modifier == I915_FORMAT_MOD_Y_TILED)
|
||||
@ -2085,8 +2232,25 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv,
|
||||
plane->update_slave = icl_update_slave;
|
||||
|
||||
if (skl_plane_has_planar(dev_priv, pipe, plane_id)) {
|
||||
formats = skl_planar_formats;
|
||||
num_formats = ARRAY_SIZE(skl_planar_formats);
|
||||
if (icl_is_hdr_plane(dev_priv, plane_id)) {
|
||||
formats = icl_hdr_planar_formats;
|
||||
num_formats = ARRAY_SIZE(icl_hdr_planar_formats);
|
||||
} else if (INTEL_GEN(dev_priv) >= 11) {
|
||||
formats = icl_planar_formats;
|
||||
num_formats = ARRAY_SIZE(icl_planar_formats);
|
||||
} else if (INTEL_GEN(dev_priv) == 10 || IS_GEMINILAKE(dev_priv)) {
|
||||
formats = glk_planar_formats;
|
||||
num_formats = ARRAY_SIZE(glk_planar_formats);
|
||||
} else {
|
||||
formats = skl_planar_formats;
|
||||
num_formats = ARRAY_SIZE(skl_planar_formats);
|
||||
}
|
||||
} else if (icl_is_hdr_plane(dev_priv, plane_id)) {
|
||||
formats = icl_hdr_plane_formats;
|
||||
num_formats = ARRAY_SIZE(icl_hdr_plane_formats);
|
||||
} else if (INTEL_GEN(dev_priv) >= 11) {
|
||||
formats = icl_plane_formats;
|
||||
num_formats = ARRAY_SIZE(icl_plane_formats);
|
||||
} else {
|
||||
formats = skl_plane_formats;
|
||||
num_formats = ARRAY_SIZE(skl_plane_formats);
|
||||
|
@ -317,129 +317,6 @@ static int get_column_index_for_rc_params(u8 bits_per_component)
|
||||
}
|
||||
}
|
||||
|
||||
static int intel_compute_rc_parameters(struct drm_dsc_config *vdsc_cfg)
|
||||
{
|
||||
unsigned long groups_per_line = 0;
|
||||
unsigned long groups_total = 0;
|
||||
unsigned long num_extra_mux_bits = 0;
|
||||
unsigned long slice_bits = 0;
|
||||
unsigned long hrd_delay = 0;
|
||||
unsigned long final_scale = 0;
|
||||
unsigned long rbs_min = 0;
|
||||
|
||||
/* Number of groups used to code each line of a slice */
|
||||
groups_per_line = DIV_ROUND_UP(vdsc_cfg->slice_width,
|
||||
DSC_RC_PIXELS_PER_GROUP);
|
||||
|
||||
/* chunksize in Bytes */
|
||||
vdsc_cfg->slice_chunk_size = DIV_ROUND_UP(vdsc_cfg->slice_width *
|
||||
vdsc_cfg->bits_per_pixel,
|
||||
(8 * 16));
|
||||
|
||||
if (vdsc_cfg->convert_rgb)
|
||||
num_extra_mux_bits = 3 * (vdsc_cfg->mux_word_size +
|
||||
(4 * vdsc_cfg->bits_per_component + 4)
|
||||
- 2);
|
||||
else
|
||||
num_extra_mux_bits = 3 * vdsc_cfg->mux_word_size +
|
||||
(4 * vdsc_cfg->bits_per_component + 4) +
|
||||
2 * (4 * vdsc_cfg->bits_per_component) - 2;
|
||||
/* Number of bits in one Slice */
|
||||
slice_bits = 8 * vdsc_cfg->slice_chunk_size * vdsc_cfg->slice_height;
|
||||
|
||||
while ((num_extra_mux_bits > 0) &&
|
||||
((slice_bits - num_extra_mux_bits) % vdsc_cfg->mux_word_size))
|
||||
num_extra_mux_bits--;
|
||||
|
||||
if (groups_per_line < vdsc_cfg->initial_scale_value - 8)
|
||||
vdsc_cfg->initial_scale_value = groups_per_line + 8;
|
||||
|
||||
/* scale_decrement_interval calculation according to DSC spec 1.11 */
|
||||
if (vdsc_cfg->initial_scale_value > 8)
|
||||
vdsc_cfg->scale_decrement_interval = groups_per_line /
|
||||
(vdsc_cfg->initial_scale_value - 8);
|
||||
else
|
||||
vdsc_cfg->scale_decrement_interval = DSC_SCALE_DECREMENT_INTERVAL_MAX;
|
||||
|
||||
vdsc_cfg->final_offset = vdsc_cfg->rc_model_size -
|
||||
(vdsc_cfg->initial_xmit_delay *
|
||||
vdsc_cfg->bits_per_pixel + 8) / 16 + num_extra_mux_bits;
|
||||
|
||||
if (vdsc_cfg->final_offset >= vdsc_cfg->rc_model_size) {
|
||||
DRM_DEBUG_KMS("FinalOfs < RcModelSze for this InitialXmitDelay\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
final_scale = (vdsc_cfg->rc_model_size * 8) /
|
||||
(vdsc_cfg->rc_model_size - vdsc_cfg->final_offset);
|
||||
if (vdsc_cfg->slice_height > 1)
|
||||
/*
|
||||
* NflBpgOffset is 16 bit value with 11 fractional bits
|
||||
* hence we multiply by 2^11 for preserving the
|
||||
* fractional part
|
||||
*/
|
||||
vdsc_cfg->nfl_bpg_offset = DIV_ROUND_UP((vdsc_cfg->first_line_bpg_offset << 11),
|
||||
(vdsc_cfg->slice_height - 1));
|
||||
else
|
||||
vdsc_cfg->nfl_bpg_offset = 0;
|
||||
|
||||
/* 2^16 - 1 */
|
||||
if (vdsc_cfg->nfl_bpg_offset > 65535) {
|
||||
DRM_DEBUG_KMS("NflBpgOffset is too large for this slice height\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* Number of groups used to code the entire slice */
|
||||
groups_total = groups_per_line * vdsc_cfg->slice_height;
|
||||
|
||||
/* slice_bpg_offset is 16 bit value with 11 fractional bits */
|
||||
vdsc_cfg->slice_bpg_offset = DIV_ROUND_UP(((vdsc_cfg->rc_model_size -
|
||||
vdsc_cfg->initial_offset +
|
||||
num_extra_mux_bits) << 11),
|
||||
groups_total);
|
||||
|
||||
if (final_scale > 9) {
|
||||
/*
|
||||
* ScaleIncrementInterval =
|
||||
* finaloffset/((NflBpgOffset + SliceBpgOffset)*8(finalscale - 1.125))
|
||||
* as (NflBpgOffset + SliceBpgOffset) has 11 bit fractional value,
|
||||
* we need divide by 2^11 from pstDscCfg values
|
||||
*/
|
||||
vdsc_cfg->scale_increment_interval =
|
||||
(vdsc_cfg->final_offset * (1 << 11)) /
|
||||
((vdsc_cfg->nfl_bpg_offset +
|
||||
vdsc_cfg->slice_bpg_offset) *
|
||||
(final_scale - 9));
|
||||
} else {
|
||||
/*
|
||||
* If finalScaleValue is less than or equal to 9, a value of 0 should
|
||||
* be used to disable the scale increment at the end of the slice
|
||||
*/
|
||||
vdsc_cfg->scale_increment_interval = 0;
|
||||
}
|
||||
|
||||
if (vdsc_cfg->scale_increment_interval > 65535) {
|
||||
DRM_DEBUG_KMS("ScaleIncrementInterval is large for slice height\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* DSC spec mentions that bits_per_pixel specifies the target
|
||||
* bits/pixel (bpp) rate that is used by the encoder,
|
||||
* in steps of 1/16 of a bit per pixel
|
||||
*/
|
||||
rbs_min = vdsc_cfg->rc_model_size - vdsc_cfg->initial_offset +
|
||||
DIV_ROUND_UP(vdsc_cfg->initial_xmit_delay *
|
||||
vdsc_cfg->bits_per_pixel, 16) +
|
||||
groups_per_line * vdsc_cfg->first_line_bpg_offset;
|
||||
|
||||
hrd_delay = DIV_ROUND_UP((rbs_min * 16), vdsc_cfg->bits_per_pixel);
|
||||
vdsc_cfg->rc_bits = (hrd_delay * vdsc_cfg->bits_per_pixel) / 16;
|
||||
vdsc_cfg->initial_dec_delay = hrd_delay - vdsc_cfg->initial_xmit_delay;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intel_dp_compute_dsc_params(struct intel_dp *intel_dp,
|
||||
struct intel_crtc_state *pipe_config)
|
||||
{
|
||||
@ -491,7 +368,7 @@ int intel_dp_compute_dsc_params(struct intel_dp *intel_dp,
|
||||
DSC_1_1_MAX_LINEBUF_DEPTH_BITS : line_buf_depth;
|
||||
|
||||
/* Gen 11 does not support YCbCr */
|
||||
vdsc_cfg->enable422 = false;
|
||||
vdsc_cfg->simple_422 = false;
|
||||
/* Gen 11 does not support VBR */
|
||||
vdsc_cfg->vbr_enable = false;
|
||||
vdsc_cfg->block_pred_enable =
|
||||
@ -574,7 +451,7 @@ int intel_dp_compute_dsc_params(struct intel_dp *intel_dp,
|
||||
vdsc_cfg->initial_scale_value = (vdsc_cfg->rc_model_size << 3) /
|
||||
(vdsc_cfg->rc_model_size - vdsc_cfg->initial_offset);
|
||||
|
||||
return intel_compute_rc_parameters(vdsc_cfg);
|
||||
return drm_dsc_compute_rc_parameters(vdsc_cfg);
|
||||
}
|
||||
|
||||
enum intel_display_power_domain
|
||||
@ -618,7 +495,7 @@ static void intel_configure_pps_for_dsc_encoder(struct intel_encoder *encoder,
|
||||
pps_val |= DSC_BLOCK_PREDICTION;
|
||||
if (vdsc_cfg->convert_rgb)
|
||||
pps_val |= DSC_COLOR_SPACE_CONVERSION;
|
||||
if (vdsc_cfg->enable422)
|
||||
if (vdsc_cfg->simple_422)
|
||||
pps_val |= DSC_422_ENABLE;
|
||||
if (vdsc_cfg->vbr_enable)
|
||||
pps_val |= DSC_VBR_ENABLE;
|
||||
@ -1004,10 +881,10 @@ static void intel_dp_write_dsc_pps_sdp(struct intel_encoder *encoder,
|
||||
struct drm_dsc_pps_infoframe dp_dsc_pps_sdp;
|
||||
|
||||
/* Prepare DP SDP PPS header as per DP 1.4 spec, Table 2-123 */
|
||||
drm_dsc_dp_pps_header_init(&dp_dsc_pps_sdp);
|
||||
drm_dsc_dp_pps_header_init(&dp_dsc_pps_sdp.pps_header);
|
||||
|
||||
/* Fill the PPS payload bytes as per DSC spec 1.2 Table 4-1 */
|
||||
drm_dsc_pps_infoframe_pack(&dp_dsc_pps_sdp, vdsc_cfg);
|
||||
drm_dsc_pps_payload_pack(&dp_dsc_pps_sdp.pps_payload, vdsc_cfg);
|
||||
|
||||
intel_dig_port->write_infoframe(encoder, crtc_state,
|
||||
DP_SDP_PPS, &dp_dsc_pps_sdp,
|
||||
|
@ -185,7 +185,7 @@ static int compare_of(struct device *dev, void *data)
|
||||
}
|
||||
|
||||
/* Special case for LDB, one device for two channels */
|
||||
if (of_node_cmp(np->name, "lvds-channel") == 0) {
|
||||
if (of_node_name_eq(np, "lvds-channel")) {
|
||||
np = of_get_parent(np);
|
||||
of_node_put(np);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
|
||||
meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o meson_overlay.o
|
||||
meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_overlay.o
|
||||
|
||||
obj-$(CONFIG_DRM_MESON) += meson-drm.o
|
||||
obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o
|
||||
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/**
|
||||
* DOC: Canvas
|
||||
*
|
||||
* CANVAS is a memory zone where physical memory frames information
|
||||
* are stored for the VIU to scanout.
|
||||
*/
|
||||
|
||||
/* DMC Registers */
|
||||
#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */
|
||||
#define CANVAS_WIDTH_LBIT 29
|
||||
#define CANVAS_WIDTH_LWID 3
|
||||
#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */
|
||||
#define CANVAS_WIDTH_HBIT 0
|
||||
#define CANVAS_HEIGHT_BIT 9
|
||||
#define CANVAS_BLKMODE_BIT 24
|
||||
#define CANVAS_ENDIAN_BIT 26
|
||||
#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */
|
||||
#define CANVAS_LUT_WR_EN (0x2 << 8)
|
||||
#define CANVAS_LUT_RD_EN (0x1 << 8)
|
||||
|
||||
void meson_canvas_setup(struct meson_drm *priv,
|
||||
uint32_t canvas_index, uint32_t addr,
|
||||
uint32_t stride, uint32_t height,
|
||||
unsigned int wrap,
|
||||
unsigned int blkmode,
|
||||
unsigned int endian)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_DATAL,
|
||||
(((addr + 7) >> 3)) |
|
||||
(((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_DATAH,
|
||||
((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
|
||||
CANVAS_WIDTH_HBIT) |
|
||||
(height << CANVAS_HEIGHT_BIT) |
|
||||
(wrap << 22) |
|
||||
(blkmode << CANVAS_BLKMODE_BIT) |
|
||||
(endian << CANVAS_ENDIAN_BIT));
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_ADDR,
|
||||
CANVAS_LUT_WR_EN | canvas_index);
|
||||
|
||||
/* Force a read-back to make sure everything is flushed. */
|
||||
regmap_read(priv->dmc, DMC_CAV_LUT_DATAH, &val);
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Canvas LUT Memory */
|
||||
|
||||
#ifndef __MESON_CANVAS_H
|
||||
#define __MESON_CANVAS_H
|
||||
|
||||
#define MESON_CANVAS_ID_OSD1 0x4e
|
||||
#define MESON_CANVAS_ID_VD1_0 0x60
|
||||
#define MESON_CANVAS_ID_VD1_1 0x61
|
||||
#define MESON_CANVAS_ID_VD1_2 0x62
|
||||
|
||||
/* Canvas configuration. */
|
||||
#define MESON_CANVAS_WRAP_NONE 0x00
|
||||
#define MESON_CANVAS_WRAP_X 0x01
|
||||
#define MESON_CANVAS_WRAP_Y 0x02
|
||||
|
||||
#define MESON_CANVAS_BLKMODE_LINEAR 0x00
|
||||
#define MESON_CANVAS_BLKMODE_32x32 0x01
|
||||
#define MESON_CANVAS_BLKMODE_64x64 0x02
|
||||
|
||||
#define MESON_CANVAS_ENDIAN_SWAP16 0x1
|
||||
#define MESON_CANVAS_ENDIAN_SWAP32 0x3
|
||||
#define MESON_CANVAS_ENDIAN_SWAP64 0x7
|
||||
#define MESON_CANVAS_ENDIAN_SWAP128 0xf
|
||||
|
||||
void meson_canvas_setup(struct meson_drm *priv,
|
||||
uint32_t canvas_index, uint32_t addr,
|
||||
uint32_t stride, uint32_t height,
|
||||
unsigned int wrap,
|
||||
unsigned int blkmode,
|
||||
unsigned int endian);
|
||||
|
||||
#endif /* __MESON_CANVAS_H */
|
@ -37,7 +37,6 @@
|
||||
#include "meson_venc.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/* CRTC definition */
|
||||
@ -214,13 +213,7 @@ void meson_crtc_irq(struct meson_drm *priv)
|
||||
writel_relaxed(priv->viu.osd_sc_v_ctrl0,
|
||||
priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
|
||||
|
||||
if (priv->canvas)
|
||||
meson_canvas_config(priv->canvas, priv->canvas_id_osd1,
|
||||
priv->viu.osd1_addr, priv->viu.osd1_stride,
|
||||
priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR, 0);
|
||||
else
|
||||
meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1,
|
||||
meson_canvas_config(priv->canvas, priv->canvas_id_osd1,
|
||||
priv->viu.osd1_addr, priv->viu.osd1_stride,
|
||||
priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR, 0);
|
||||
@ -237,61 +230,34 @@ void meson_crtc_irq(struct meson_drm *priv)
|
||||
|
||||
switch (priv->viu.vd1_planes) {
|
||||
case 3:
|
||||
if (priv->canvas)
|
||||
meson_canvas_config(priv->canvas,
|
||||
priv->canvas_id_vd1_2,
|
||||
priv->viu.vd1_addr2,
|
||||
priv->viu.vd1_stride2,
|
||||
priv->viu.vd1_height2,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
else
|
||||
meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_2,
|
||||
priv->viu.vd1_addr2,
|
||||
priv->viu.vd1_stride2,
|
||||
priv->viu.vd1_height2,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
meson_canvas_config(priv->canvas,
|
||||
priv->canvas_id_vd1_2,
|
||||
priv->viu.vd1_addr2,
|
||||
priv->viu.vd1_stride2,
|
||||
priv->viu.vd1_height2,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
/* fallthrough */
|
||||
case 2:
|
||||
if (priv->canvas)
|
||||
meson_canvas_config(priv->canvas,
|
||||
priv->canvas_id_vd1_1,
|
||||
priv->viu.vd1_addr1,
|
||||
priv->viu.vd1_stride1,
|
||||
priv->viu.vd1_height1,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
else
|
||||
meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_1,
|
||||
priv->viu.vd1_addr2,
|
||||
priv->viu.vd1_stride2,
|
||||
priv->viu.vd1_height2,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
meson_canvas_config(priv->canvas,
|
||||
priv->canvas_id_vd1_1,
|
||||
priv->viu.vd1_addr1,
|
||||
priv->viu.vd1_stride1,
|
||||
priv->viu.vd1_height1,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
/* fallthrough */
|
||||
case 1:
|
||||
if (priv->canvas)
|
||||
meson_canvas_config(priv->canvas,
|
||||
priv->canvas_id_vd1_0,
|
||||
priv->viu.vd1_addr0,
|
||||
priv->viu.vd1_stride0,
|
||||
priv->viu.vd1_height0,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
else
|
||||
meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_0,
|
||||
priv->viu.vd1_addr2,
|
||||
priv->viu.vd1_stride2,
|
||||
priv->viu.vd1_height2,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
meson_canvas_config(priv->canvas,
|
||||
priv->canvas_id_vd1_0,
|
||||
priv->viu.vd1_addr0,
|
||||
priv->viu.vd1_stride0,
|
||||
priv->viu.vd1_height0,
|
||||
MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR,
|
||||
MESON_CANVAS_ENDIAN_SWAP64);
|
||||
};
|
||||
|
||||
writel_relaxed(priv->viu.vd1_if0_gen_reg,
|
||||
|
@ -48,7 +48,6 @@
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
#define DRIVER_NAME "meson"
|
||||
@ -231,50 +230,31 @@ static int meson_drv_bind_master(struct device *dev, bool has_components)
|
||||
}
|
||||
|
||||
priv->canvas = meson_canvas_get(dev);
|
||||
if (!IS_ERR(priv->canvas)) {
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
|
||||
if (ret) {
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
||||
goto free_drm;
|
||||
}
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
|
||||
if (ret) {
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
|
||||
goto free_drm;
|
||||
}
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
|
||||
if (ret) {
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
|
||||
goto free_drm;
|
||||
}
|
||||
} else {
|
||||
priv->canvas = NULL;
|
||||
if (IS_ERR(priv->canvas)) {
|
||||
ret = PTR_ERR(priv->canvas);
|
||||
goto free_drm;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
|
||||
if (!res) {
|
||||
ret = -EINVAL;
|
||||
goto free_drm;
|
||||
}
|
||||
/* Simply ioremap since it may be a shared register zone */
|
||||
regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!regs) {
|
||||
ret = -EADDRNOTAVAIL;
|
||||
goto free_drm;
|
||||
}
|
||||
|
||||
priv->dmc = devm_regmap_init_mmio(dev, regs,
|
||||
&meson_regmap_config);
|
||||
if (IS_ERR(priv->dmc)) {
|
||||
dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
|
||||
ret = PTR_ERR(priv->dmc);
|
||||
goto free_drm;
|
||||
}
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
|
||||
if (ret) {
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
||||
goto free_drm;
|
||||
}
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
|
||||
if (ret) {
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
|
||||
goto free_drm;
|
||||
}
|
||||
ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
|
||||
if (ret) {
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
|
||||
meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
|
||||
goto free_drm;
|
||||
}
|
||||
|
||||
priv->vsync_irq = platform_get_irq(pdev, 0);
|
||||
|
@ -29,7 +29,6 @@ struct meson_drm {
|
||||
struct device *dev;
|
||||
void __iomem *io_base;
|
||||
struct regmap *hhi;
|
||||
struct regmap *dmc;
|
||||
int vsync_irq;
|
||||
|
||||
struct meson_canvas *canvas;
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include "meson_overlay.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/* VD1_IF0_GEN_REG */
|
||||
@ -350,13 +349,6 @@ static void meson_overlay_atomic_update(struct drm_plane *plane,
|
||||
|
||||
DRM_DEBUG_DRIVER("\n");
|
||||
|
||||
/* Fallback is canvas provider is not available */
|
||||
if (!priv->canvas) {
|
||||
priv->canvas_id_vd1_0 = MESON_CANVAS_ID_VD1_0;
|
||||
priv->canvas_id_vd1_1 = MESON_CANVAS_ID_VD1_1;
|
||||
priv->canvas_id_vd1_2 = MESON_CANVAS_ID_VD1_2;
|
||||
}
|
||||
|
||||
interlace_mode = state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE;
|
||||
|
||||
spin_lock_irqsave(&priv->drm->event_lock, flags);
|
||||
|
@ -38,7 +38,6 @@
|
||||
#include "meson_plane.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/* OSD_SCI_WH_M1 */
|
||||
@ -148,10 +147,7 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
|
||||
(0xFF << OSD_GLOBAL_ALPHA_SHIFT) |
|
||||
OSD_BLK0_ENABLE;
|
||||
|
||||
if (priv->canvas)
|
||||
canvas_id_osd1 = priv->canvas_id_osd1;
|
||||
else
|
||||
canvas_id_osd1 = MESON_CANVAS_ID_OSD1;
|
||||
canvas_id_osd1 = priv->canvas_id_osd1;
|
||||
|
||||
/* Set up BLK0 to point to the right canvas */
|
||||
priv->viu.osd1_blk0_cfg[0] = ((canvas_id_osd1 << OSD_CANVAS_SEL) |
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "meson_viu.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/**
|
||||
|
@ -1027,7 +1027,6 @@ static struct drm_driver msm_driver = {
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_res_obj = msm_gem_prime_res_obj,
|
||||
.gem_prime_pin = msm_gem_prime_pin,
|
||||
.gem_prime_unpin = msm_gem_prime_unpin,
|
||||
.gem_prime_get_sg_table = msm_gem_prime_get_sg_table,
|
||||
|
@ -292,7 +292,6 @@ struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj);
|
||||
void *msm_gem_prime_vmap(struct drm_gem_object *obj);
|
||||
void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
|
||||
int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
|
||||
struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj);
|
||||
struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev,
|
||||
struct dma_buf_attachment *attach, struct sg_table *sg);
|
||||
int msm_gem_prime_pin(struct drm_gem_object *obj);
|
||||
|
@ -672,14 +672,13 @@ void msm_gem_vunmap(struct drm_gem_object *obj, enum msm_gem_lock subclass)
|
||||
int msm_gem_sync_object(struct drm_gem_object *obj,
|
||||
struct msm_fence_context *fctx, bool exclusive)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
struct reservation_object_list *fobj;
|
||||
struct dma_fence *fence;
|
||||
int i, ret;
|
||||
|
||||
fobj = reservation_object_get_list(msm_obj->resv);
|
||||
fobj = reservation_object_get_list(obj->resv);
|
||||
if (!fobj || (fobj->shared_count == 0)) {
|
||||
fence = reservation_object_get_excl(msm_obj->resv);
|
||||
fence = reservation_object_get_excl(obj->resv);
|
||||
/* don't need to wait on our own fences, since ring is fifo */
|
||||
if (fence && (fence->context != fctx->context)) {
|
||||
ret = dma_fence_wait(fence, true);
|
||||
@ -693,7 +692,7 @@ int msm_gem_sync_object(struct drm_gem_object *obj,
|
||||
|
||||
for (i = 0; i < fobj->shared_count; i++) {
|
||||
fence = rcu_dereference_protected(fobj->shared[i],
|
||||
reservation_object_held(msm_obj->resv));
|
||||
reservation_object_held(obj->resv));
|
||||
if (fence->context != fctx->context) {
|
||||
ret = dma_fence_wait(fence, true);
|
||||
if (ret)
|
||||
@ -711,9 +710,9 @@ void msm_gem_move_to_active(struct drm_gem_object *obj,
|
||||
WARN_ON(msm_obj->madv != MSM_MADV_WILLNEED);
|
||||
msm_obj->gpu = gpu;
|
||||
if (exclusive)
|
||||
reservation_object_add_excl_fence(msm_obj->resv, fence);
|
||||
reservation_object_add_excl_fence(obj->resv, fence);
|
||||
else
|
||||
reservation_object_add_shared_fence(msm_obj->resv, fence);
|
||||
reservation_object_add_shared_fence(obj->resv, fence);
|
||||
list_del_init(&msm_obj->mm_list);
|
||||
list_add_tail(&msm_obj->mm_list, &gpu->active_list);
|
||||
}
|
||||
@ -733,13 +732,12 @@ void msm_gem_move_to_inactive(struct drm_gem_object *obj)
|
||||
|
||||
int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
bool write = !!(op & MSM_PREP_WRITE);
|
||||
unsigned long remain =
|
||||
op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout);
|
||||
long ret;
|
||||
|
||||
ret = reservation_object_wait_timeout_rcu(msm_obj->resv, write,
|
||||
ret = reservation_object_wait_timeout_rcu(obj->resv, write,
|
||||
true, remain);
|
||||
if (ret == 0)
|
||||
return remain == 0 ? -EBUSY : -ETIMEDOUT;
|
||||
@ -771,7 +769,7 @@ static void describe_fence(struct dma_fence *fence, const char *type,
|
||||
void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
struct reservation_object *robj = msm_obj->resv;
|
||||
struct reservation_object *robj = obj->resv;
|
||||
struct reservation_object_list *fobj;
|
||||
struct dma_fence *fence;
|
||||
struct msm_gem_vma *vma;
|
||||
@ -883,9 +881,6 @@ void msm_gem_free_object(struct drm_gem_object *obj)
|
||||
put_pages(obj);
|
||||
}
|
||||
|
||||
if (msm_obj->resv == &msm_obj->_resv)
|
||||
reservation_object_fini(msm_obj->resv);
|
||||
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
mutex_unlock(&msm_obj->lock);
|
||||
@ -945,12 +940,8 @@ static int msm_gem_new_impl(struct drm_device *dev,
|
||||
msm_obj->flags = flags;
|
||||
msm_obj->madv = MSM_MADV_WILLNEED;
|
||||
|
||||
if (resv) {
|
||||
msm_obj->resv = resv;
|
||||
} else {
|
||||
msm_obj->resv = &msm_obj->_resv;
|
||||
reservation_object_init(msm_obj->resv);
|
||||
}
|
||||
if (resv)
|
||||
msm_obj->base.resv = resv;
|
||||
|
||||
INIT_LIST_HEAD(&msm_obj->submit_entry);
|
||||
INIT_LIST_HEAD(&msm_obj->vmas);
|
||||
|
@ -70,10 +70,3 @@ void msm_gem_prime_unpin(struct drm_gem_object *obj)
|
||||
if (!obj->import_attach)
|
||||
msm_gem_put_pages(obj);
|
||||
}
|
||||
|
||||
struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj)
|
||||
{
|
||||
struct msm_gem_object *msm_obj = to_msm_bo(obj);
|
||||
|
||||
return msm_obj->resv;
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ static void submit_unlock_unpin_bo(struct msm_gem_submit *submit,
|
||||
msm_gem_unpin_iova(&msm_obj->base, submit->gpu->aspace);
|
||||
|
||||
if (submit->bos[i].flags & BO_LOCKED)
|
||||
ww_mutex_unlock(&msm_obj->resv->lock);
|
||||
ww_mutex_unlock(&msm_obj->base.resv->lock);
|
||||
|
||||
if (backoff && !(submit->bos[i].flags & BO_VALID))
|
||||
submit->bos[i].iova = 0;
|
||||
@ -196,7 +196,7 @@ retry:
|
||||
contended = i;
|
||||
|
||||
if (!(submit->bos[i].flags & BO_LOCKED)) {
|
||||
ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock,
|
||||
ret = ww_mutex_lock_interruptible(&msm_obj->base.resv->lock,
|
||||
&submit->ticket);
|
||||
if (ret)
|
||||
goto fail;
|
||||
@ -218,7 +218,7 @@ fail:
|
||||
if (ret == -EDEADLK) {
|
||||
struct msm_gem_object *msm_obj = submit->bos[contended].obj;
|
||||
/* we lost out in a seqno race, lock and retry.. */
|
||||
ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock,
|
||||
ret = ww_mutex_lock_slow_interruptible(&msm_obj->base.resv->lock,
|
||||
&submit->ticket);
|
||||
if (!ret) {
|
||||
submit->bos[contended].flags |= BO_LOCKED;
|
||||
@ -244,7 +244,7 @@ static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
|
||||
* strange place to call it. OTOH this is a
|
||||
* convenient can-fail point to hook it in.
|
||||
*/
|
||||
ret = reservation_object_reserve_shared(msm_obj->resv,
|
||||
ret = reservation_object_reserve_shared(msm_obj->base.resv,
|
||||
1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -149,6 +149,15 @@ config DRM_PANEL_RAYDIUM_RM68200
|
||||
Say Y here if you want to enable support for Raydium RM68200
|
||||
720x1280 DSI video mode panel.
|
||||
|
||||
config DRM_PANEL_RONBO_RB070D30
|
||||
tristate "Ronbo Electronics RB070D30 panel"
|
||||
depends on OF
|
||||
depends on DRM_MIPI_DSI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
help
|
||||
Say Y here if you want to enable support for Ronbo Electronics
|
||||
RB070D30 1024x600 DSI panel.
|
||||
|
||||
config DRM_PANEL_SAMSUNG_S6D16D0
|
||||
tristate "Samsung S6D16D0 DSI video mode panel"
|
||||
depends on OF
|
||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
|
||||
obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
|
||||
obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
|
||||
obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o
|
||||
obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
|
||||
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
|
||||
|
258
drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
Normal file
258
drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
Normal file
@ -0,0 +1,258 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018-2019, Bridge Systems BV
|
||||
* Copyright (C) 2018-2019, Bootlin
|
||||
* Copyright (C) 2017, Free Electrons
|
||||
*
|
||||
* This file based on panel-ilitek-ili9881c.c
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
struct rb070d30_panel {
|
||||
struct drm_panel panel;
|
||||
struct mipi_dsi_device *dsi;
|
||||
struct backlight_device *backlight;
|
||||
struct regulator *supply;
|
||||
|
||||
struct {
|
||||
struct gpio_desc *power;
|
||||
struct gpio_desc *reset;
|
||||
struct gpio_desc *updn;
|
||||
struct gpio_desc *shlr;
|
||||
} gpios;
|
||||
};
|
||||
|
||||
static inline struct rb070d30_panel *panel_to_rb070d30_panel(struct drm_panel *panel)
|
||||
{
|
||||
return container_of(panel, struct rb070d30_panel, panel);
|
||||
}
|
||||
|
||||
static int rb070d30_panel_prepare(struct drm_panel *panel)
|
||||
{
|
||||
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(ctx->supply);
|
||||
if (ret < 0) {
|
||||
DRM_DEV_ERROR(&ctx->dsi->dev, "Failed to enable supply: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
gpiod_set_value(ctx->gpios.power, 1);
|
||||
msleep(20);
|
||||
gpiod_set_value(ctx->gpios.reset, 1);
|
||||
msleep(20);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rb070d30_panel_unprepare(struct drm_panel *panel)
|
||||
{
|
||||
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
|
||||
|
||||
gpiod_set_value(ctx->gpios.reset, 0);
|
||||
gpiod_set_value(ctx->gpios.power, 0);
|
||||
regulator_disable(ctx->supply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rb070d30_panel_enable(struct drm_panel *panel)
|
||||
{
|
||||
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
|
||||
int ret;
|
||||
|
||||
ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = backlight_enable(ctx->backlight);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rb070d30_panel_disable(struct drm_panel *panel)
|
||||
{
|
||||
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
|
||||
|
||||
backlight_disable(ctx->backlight);
|
||||
return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
|
||||
}
|
||||
|
||||
/* Default timings */
|
||||
static const struct drm_display_mode default_mode = {
|
||||
.clock = 51206,
|
||||
.hdisplay = 1024,
|
||||
.hsync_start = 1024 + 160,
|
||||
.hsync_end = 1024 + 160 + 80,
|
||||
.htotal = 1024 + 160 + 80 + 80,
|
||||
.vdisplay = 600,
|
||||
.vsync_start = 600 + 12,
|
||||
.vsync_end = 600 + 12 + 10,
|
||||
.vtotal = 600 + 12 + 10 + 13,
|
||||
.vrefresh = 60,
|
||||
|
||||
.width_mm = 154,
|
||||
.height_mm = 85,
|
||||
};
|
||||
|
||||
static int rb070d30_panel_get_modes(struct drm_panel *panel)
|
||||
{
|
||||
struct drm_connector *connector = panel->connector;
|
||||
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
|
||||
struct drm_display_mode *mode;
|
||||
static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
|
||||
mode = drm_mode_duplicate(panel->drm, &default_mode);
|
||||
if (!mode) {
|
||||
DRM_DEV_ERROR(&ctx->dsi->dev,
|
||||
"Failed to add mode " DRM_MODE_FMT "\n",
|
||||
DRM_MODE_ARG(&default_mode));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
drm_mode_set_name(mode);
|
||||
|
||||
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
||||
panel->connector->display_info.bpc = 8;
|
||||
panel->connector->display_info.width_mm = mode->width_mm;
|
||||
panel->connector->display_info.height_mm = mode->height_mm;
|
||||
drm_display_info_set_bus_formats(&connector->display_info,
|
||||
&bus_format, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct drm_panel_funcs rb070d30_panel_funcs = {
|
||||
.get_modes = rb070d30_panel_get_modes,
|
||||
.prepare = rb070d30_panel_prepare,
|
||||
.enable = rb070d30_panel_enable,
|
||||
.disable = rb070d30_panel_disable,
|
||||
.unprepare = rb070d30_panel_unprepare,
|
||||
};
|
||||
|
||||
static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct rb070d30_panel *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->supply = devm_regulator_get(&dsi->dev, "vcc-lcd");
|
||||
if (IS_ERR(ctx->supply))
|
||||
return PTR_ERR(ctx->supply);
|
||||
|
||||
mipi_dsi_set_drvdata(dsi, ctx);
|
||||
ctx->dsi = dsi;
|
||||
|
||||
drm_panel_init(&ctx->panel);
|
||||
ctx->panel.dev = &dsi->dev;
|
||||
ctx->panel.funcs = &rb070d30_panel_funcs;
|
||||
|
||||
ctx->gpios.reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->gpios.reset)) {
|
||||
DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n");
|
||||
return PTR_ERR(ctx->gpios.reset);
|
||||
}
|
||||
|
||||
ctx->gpios.power = devm_gpiod_get(&dsi->dev, "power", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->gpios.power)) {
|
||||
DRM_DEV_ERROR(&dsi->dev, "Couldn't get our power GPIO\n");
|
||||
return PTR_ERR(ctx->gpios.power);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't change the state of that GPIO later on but we need
|
||||
* to force it into a low state.
|
||||
*/
|
||||
ctx->gpios.updn = devm_gpiod_get(&dsi->dev, "updn", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->gpios.updn)) {
|
||||
DRM_DEV_ERROR(&dsi->dev, "Couldn't get our updn GPIO\n");
|
||||
return PTR_ERR(ctx->gpios.updn);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't change the state of that GPIO later on but we need
|
||||
* to force it into a low state.
|
||||
*/
|
||||
ctx->gpios.shlr = devm_gpiod_get(&dsi->dev, "shlr", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ctx->gpios.shlr)) {
|
||||
DRM_DEV_ERROR(&dsi->dev, "Couldn't get our shlr GPIO\n");
|
||||
return PTR_ERR(ctx->gpios.shlr);
|
||||
}
|
||||
|
||||
ctx->backlight = devm_of_find_backlight(&dsi->dev);
|
||||
if (IS_ERR(ctx->backlight)) {
|
||||
DRM_DEV_ERROR(&dsi->dev, "Couldn't get our backlight\n");
|
||||
return PTR_ERR(ctx->backlight);
|
||||
}
|
||||
|
||||
ret = drm_panel_add(&ctx->panel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM;
|
||||
dsi->format = MIPI_DSI_FMT_RGB888;
|
||||
dsi->lanes = 4;
|
||||
|
||||
return mipi_dsi_attach(dsi);
|
||||
}
|
||||
|
||||
static int rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi)
|
||||
{
|
||||
struct rb070d30_panel *ctx = mipi_dsi_get_drvdata(dsi);
|
||||
|
||||
mipi_dsi_detach(dsi);
|
||||
drm_panel_remove(&ctx->panel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rb070d30_panel_of_match[] = {
|
||||
{ .compatible = "ronbo,rb070d30" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rb070d30_panel_of_match);
|
||||
|
||||
static struct mipi_dsi_driver rb070d30_panel_driver = {
|
||||
.probe = rb070d30_panel_dsi_probe,
|
||||
.remove = rb070d30_panel_dsi_remove,
|
||||
.driver = {
|
||||
.name = "panel-ronbo-rb070d30",
|
||||
.of_match_table = rb070d30_panel_of_match,
|
||||
},
|
||||
};
|
||||
module_mipi_dsi_driver(rb070d30_panel_driver);
|
||||
|
||||
MODULE_AUTHOR("Boris Brezillon <boris.brezillon@bootlin.com>");
|
||||
MODULE_AUTHOR("Konstantin Sudakov <k.sudakov@integrasources.com>");
|
||||
MODULE_DESCRIPTION("Ronbo RB070D30 Panel Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -535,7 +535,7 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
|
||||
{
|
||||
struct qxl_device *qdev = plane->dev->dev_private;
|
||||
struct qxl_bo *bo = gem_to_qxl_bo(plane->state->fb->obj[0]);
|
||||
struct qxl_bo *bo_old, *primary;
|
||||
struct qxl_bo *primary;
|
||||
struct drm_clip_rect norect = {
|
||||
.x1 = 0,
|
||||
.y1 = 0,
|
||||
@ -544,12 +544,6 @@ static void qxl_primary_atomic_update(struct drm_plane *plane,
|
||||
};
|
||||
uint32_t dumb_shadow_offset = 0;
|
||||
|
||||
if (old_state->fb) {
|
||||
bo_old = gem_to_qxl_bo(old_state->fb->obj[0]);
|
||||
} else {
|
||||
bo_old = NULL;
|
||||
}
|
||||
|
||||
primary = bo->shadow ? bo->shadow : bo;
|
||||
|
||||
if (!primary->is_primary) {
|
||||
|
@ -1388,7 +1388,7 @@ int radeon_device_init(struct radeon_device *rdev,
|
||||
pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(32));
|
||||
pr_warn("radeon: No coherent DMA available\n");
|
||||
}
|
||||
rdev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);
|
||||
rdev->need_swiotlb = drm_need_swiotlb(dma_bits);
|
||||
|
||||
/* Registers mapping */
|
||||
/* TODO: block userspace mapping of io register */
|
||||
|
@ -1615,7 +1615,7 @@ static int igt_topdown(void *ignored)
|
||||
DRM_RND_STATE(prng, random_seed);
|
||||
const unsigned int count = 8192;
|
||||
unsigned int size;
|
||||
unsigned long *bitmap = NULL;
|
||||
unsigned long *bitmap;
|
||||
struct drm_mm mm;
|
||||
struct drm_mm_node *nodes, *node, *next;
|
||||
unsigned int *order, n, m, o = 0;
|
||||
@ -1631,8 +1631,7 @@ static int igt_topdown(void *ignored)
|
||||
if (!nodes)
|
||||
goto err;
|
||||
|
||||
bitmap = kcalloc(count / BITS_PER_LONG, sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
bitmap = bitmap_zalloc(count, GFP_KERNEL);
|
||||
if (!bitmap)
|
||||
goto err_nodes;
|
||||
|
||||
@ -1717,7 +1716,7 @@ out:
|
||||
drm_mm_takedown(&mm);
|
||||
kfree(order);
|
||||
err_bitmap:
|
||||
kfree(bitmap);
|
||||
bitmap_free(bitmap);
|
||||
err_nodes:
|
||||
vfree(nodes);
|
||||
err:
|
||||
@ -1745,8 +1744,7 @@ static int igt_bottomup(void *ignored)
|
||||
if (!nodes)
|
||||
goto err;
|
||||
|
||||
bitmap = kcalloc(count / BITS_PER_LONG, sizeof(unsigned long),
|
||||
GFP_KERNEL);
|
||||
bitmap = bitmap_zalloc(count, GFP_KERNEL);
|
||||
if (!bitmap)
|
||||
goto err_nodes;
|
||||
|
||||
@ -1818,7 +1816,7 @@ out:
|
||||
drm_mm_takedown(&mm);
|
||||
kfree(order);
|
||||
err_bitmap:
|
||||
kfree(bitmap);
|
||||
bitmap_free(bitmap);
|
||||
err_nodes:
|
||||
vfree(nodes);
|
||||
err:
|
||||
|
@ -6,7 +6,7 @@ config DRM_STM
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_PANEL_BRIDGE
|
||||
select VIDEOMODE_HELPERS
|
||||
select FB_PROVIDE_GET_FB_UNMAPPED_AREA
|
||||
select FB_PROVIDE_GET_FB_UNMAPPED_AREA if FB
|
||||
|
||||
help
|
||||
Enable support for the on-chip display controller on
|
||||
|
@ -720,33 +720,22 @@ static int sun4i_backend_free_sat(struct device *dev) {
|
||||
*/
|
||||
static int sun4i_backend_of_get_id(struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep;
|
||||
int ret = -EINVAL;
|
||||
struct device_node *ep, *remote;
|
||||
struct of_endpoint of_ep;
|
||||
|
||||
/* input is port 0 */
|
||||
port = of_graph_get_port_by_id(node, 0);
|
||||
if (!port)
|
||||
/* Input port is 0, and we want the first endpoint. */
|
||||
ep = of_graph_get_endpoint_by_regs(node, 0, -1);
|
||||
if (!ep)
|
||||
return -EINVAL;
|
||||
|
||||
/* try finding an upstream endpoint */
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
struct device_node *remote;
|
||||
u32 reg;
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
of_node_put(ep);
|
||||
if (!remote)
|
||||
return -EINVAL;
|
||||
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(remote, "reg", ®);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = reg;
|
||||
}
|
||||
|
||||
of_node_put(port);
|
||||
|
||||
return ret;
|
||||
of_graph_parse_endpoint(remote, &of_ep);
|
||||
of_node_put(remote);
|
||||
return of_ep.id;
|
||||
}
|
||||
|
||||
/* TODO: This needs to take multiple pipelines into account */
|
||||
|
@ -20,7 +20,7 @@ struct sun4i_lvds {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
|
||||
struct sun4i_tcon *tcon;
|
||||
struct drm_panel *panel;
|
||||
};
|
||||
|
||||
static inline struct sun4i_lvds *
|
||||
@ -41,9 +41,8 @@ static int sun4i_lvds_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_lvds *lvds =
|
||||
drm_connector_to_sun4i_lvds(connector);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
return drm_panel_get_modes(tcon->panel);
|
||||
return drm_panel_get_modes(lvds->panel);
|
||||
}
|
||||
|
||||
static struct drm_connector_helper_funcs sun4i_lvds_con_helper_funcs = {
|
||||
@ -54,9 +53,8 @@ static void
|
||||
sun4i_lvds_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_lvds *lvds = drm_connector_to_sun4i_lvds(connector);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
drm_panel_detach(tcon->panel);
|
||||
drm_panel_detach(lvds->panel);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
@ -71,26 +69,24 @@ static const struct drm_connector_funcs sun4i_lvds_con_funcs = {
|
||||
static void sun4i_lvds_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Enabling LVDS output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_prepare(tcon->panel);
|
||||
drm_panel_enable(tcon->panel);
|
||||
if (lvds->panel) {
|
||||
drm_panel_prepare(lvds->panel);
|
||||
drm_panel_enable(lvds->panel);
|
||||
}
|
||||
}
|
||||
|
||||
static void sun4i_lvds_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_lvds *lvds = drm_encoder_to_sun4i_lvds(encoder);
|
||||
struct sun4i_tcon *tcon = lvds->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Disabling LVDS output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_disable(tcon->panel);
|
||||
drm_panel_unprepare(tcon->panel);
|
||||
if (lvds->panel) {
|
||||
drm_panel_disable(lvds->panel);
|
||||
drm_panel_unprepare(lvds->panel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,11 +109,10 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
lvds = devm_kzalloc(drm->dev, sizeof(*lvds), GFP_KERNEL);
|
||||
if (!lvds)
|
||||
return -ENOMEM;
|
||||
lvds->tcon = tcon;
|
||||
encoder = &lvds->encoder;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
|
||||
&tcon->panel, &bridge);
|
||||
&lvds->panel, &bridge);
|
||||
if (ret) {
|
||||
dev_info(drm->dev, "No panel or bridge found... LVDS output disabled\n");
|
||||
return 0;
|
||||
@ -138,7 +133,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
/* The LVDS encoder can only work with the TCON channel 0 */
|
||||
lvds->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
|
||||
|
||||
if (tcon->panel) {
|
||||
if (lvds->panel) {
|
||||
drm_connector_helper_add(&lvds->connector,
|
||||
&sun4i_lvds_con_helper_funcs);
|
||||
ret = drm_connector_init(drm, &lvds->connector,
|
||||
@ -152,7 +147,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
drm_connector_attach_encoder(&lvds->connector,
|
||||
&lvds->encoder);
|
||||
|
||||
ret = drm_panel_attach(tcon->panel, &lvds->connector);
|
||||
ret = drm_panel_attach(lvds->panel, &lvds->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't attach our panel\n");
|
||||
goto err_cleanup_connector;
|
||||
|
@ -27,6 +27,8 @@ struct sun4i_rgb {
|
||||
struct drm_encoder encoder;
|
||||
|
||||
struct sun4i_tcon *tcon;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
};
|
||||
|
||||
static inline struct sun4i_rgb *
|
||||
@ -47,11 +49,18 @@ static int sun4i_rgb_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_rgb *rgb =
|
||||
drm_connector_to_sun4i_rgb(connector);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
return drm_panel_get_modes(tcon->panel);
|
||||
return drm_panel_get_modes(rgb->panel);
|
||||
}
|
||||
|
||||
/*
|
||||
* VESA DMT defines a tolerance of 0.5% on the pixel clock, while the
|
||||
* CVT spec reuses that tolerance in its examples, so it looks to be a
|
||||
* good default tolerance for the EDID-based modes. Define it to 5 per
|
||||
* mille to avoid floating point operations.
|
||||
*/
|
||||
#define SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE 5
|
||||
|
||||
static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
@ -59,8 +68,9 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
u32 hsync = mode->hsync_end - mode->hsync_start;
|
||||
u32 vsync = mode->vsync_end - mode->vsync_start;
|
||||
unsigned long rate = mode->clock * 1000;
|
||||
long rounded_rate;
|
||||
unsigned long long rate = mode->clock * 1000;
|
||||
unsigned long long lowest, highest;
|
||||
unsigned long long rounded_rate;
|
||||
|
||||
DRM_DEBUG_DRIVER("Validating modes...\n");
|
||||
|
||||
@ -92,15 +102,39 @@ static enum drm_mode_status sun4i_rgb_mode_valid(struct drm_encoder *crtc,
|
||||
|
||||
DRM_DEBUG_DRIVER("Vertical parameters OK\n");
|
||||
|
||||
/*
|
||||
* TODO: We should use the struct display_timing if available
|
||||
* and / or trying to stretch the timings within that
|
||||
* tolerancy to take care of panels that we wouldn't be able
|
||||
* to have a exact match for.
|
||||
*/
|
||||
if (rgb->panel) {
|
||||
DRM_DEBUG_DRIVER("RGB panel used, skipping clock rate checks");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* That shouldn't ever happen unless something is really wrong, but it
|
||||
* doesn't harm to check.
|
||||
*/
|
||||
if (!rgb->bridge)
|
||||
goto out;
|
||||
|
||||
tcon->dclk_min_div = 6;
|
||||
tcon->dclk_max_div = 127;
|
||||
rounded_rate = clk_round_rate(tcon->dclk, rate);
|
||||
if (rounded_rate < rate)
|
||||
|
||||
lowest = rate * (1000 - SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
|
||||
do_div(lowest, 1000);
|
||||
if (rounded_rate < lowest)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
if (rounded_rate > rate)
|
||||
highest = rate * (1000 + SUN4I_RGB_DOTCLOCK_TOLERANCE_PER_MILLE);
|
||||
do_div(highest, 1000);
|
||||
if (rounded_rate > highest)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
out:
|
||||
DRM_DEBUG_DRIVER("Clock rate OK\n");
|
||||
|
||||
return MODE_OK;
|
||||
@ -114,9 +148,8 @@ static void
|
||||
sun4i_rgb_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct sun4i_rgb *rgb = drm_connector_to_sun4i_rgb(connector);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
drm_panel_detach(tcon->panel);
|
||||
drm_panel_detach(rgb->panel);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
@ -131,26 +164,24 @@ static const struct drm_connector_funcs sun4i_rgb_con_funcs = {
|
||||
static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Enabling RGB output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_prepare(tcon->panel);
|
||||
drm_panel_enable(tcon->panel);
|
||||
if (rgb->panel) {
|
||||
drm_panel_prepare(rgb->panel);
|
||||
drm_panel_enable(rgb->panel);
|
||||
}
|
||||
}
|
||||
|
||||
static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct sun4i_rgb *rgb = drm_encoder_to_sun4i_rgb(encoder);
|
||||
struct sun4i_tcon *tcon = rgb->tcon;
|
||||
|
||||
DRM_DEBUG_DRIVER("Disabling RGB output\n");
|
||||
|
||||
if (tcon->panel) {
|
||||
drm_panel_disable(tcon->panel);
|
||||
drm_panel_unprepare(tcon->panel);
|
||||
if (rgb->panel) {
|
||||
drm_panel_disable(rgb->panel);
|
||||
drm_panel_unprepare(rgb->panel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,7 +203,6 @@ static struct drm_encoder_funcs sun4i_rgb_enc_funcs = {
|
||||
int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
{
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_bridge *bridge;
|
||||
struct sun4i_rgb *rgb;
|
||||
int ret;
|
||||
|
||||
@ -183,7 +213,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
encoder = &rgb->encoder;
|
||||
|
||||
ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0,
|
||||
&tcon->panel, &bridge);
|
||||
&rgb->panel, &rgb->bridge);
|
||||
if (ret) {
|
||||
dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n");
|
||||
return 0;
|
||||
@ -204,7 +234,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
/* The RGB encoder can only work with the TCON channel 0 */
|
||||
rgb->encoder.possible_crtcs = drm_crtc_mask(&tcon->crtc->crtc);
|
||||
|
||||
if (tcon->panel) {
|
||||
if (rgb->panel) {
|
||||
drm_connector_helper_add(&rgb->connector,
|
||||
&sun4i_rgb_con_helper_funcs);
|
||||
ret = drm_connector_init(drm, &rgb->connector,
|
||||
@ -218,15 +248,15 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
|
||||
drm_connector_attach_encoder(&rgb->connector,
|
||||
&rgb->encoder);
|
||||
|
||||
ret = drm_panel_attach(tcon->panel, &rgb->connector);
|
||||
ret = drm_panel_attach(rgb->panel, &rgb->connector);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't attach our panel\n");
|
||||
goto err_cleanup_connector;
|
||||
}
|
||||
}
|
||||
|
||||
if (bridge) {
|
||||
ret = drm_bridge_attach(encoder, bridge, NULL);
|
||||
if (rgb->bridge) {
|
||||
ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
|
||||
if (ret) {
|
||||
dev_err(drm->dev, "Couldn't attach our bridge\n");
|
||||
goto err_cleanup_connector;
|
||||
|
@ -341,8 +341,8 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
|
||||
u32 block_space, start_delay;
|
||||
u32 tcon_div;
|
||||
|
||||
tcon->dclk_min_div = 4;
|
||||
tcon->dclk_max_div = 127;
|
||||
tcon->dclk_min_div = SUN6I_DSI_TCON_DIV;
|
||||
tcon->dclk_max_div = SUN6I_DSI_TCON_DIV;
|
||||
|
||||
sun4i_tcon0_mode_set_common(tcon, mode);
|
||||
|
||||
|
@ -257,8 +257,6 @@ struct sun4i_tcon {
|
||||
struct reset_control *lcd_rst;
|
||||
struct reset_control *lvds_rst;
|
||||
|
||||
struct drm_panel *panel;
|
||||
|
||||
/* Platform adjustments */
|
||||
const struct sun4i_tcon_quirks *quirks;
|
||||
|
||||
|
@ -24,7 +24,9 @@
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
|
||||
#include "sun4i_crtc.h"
|
||||
#include "sun4i_drv.h"
|
||||
#include "sun4i_tcon.h"
|
||||
#include "sun6i_mipi_dsi.h"
|
||||
|
||||
#include <video/mipi_display.h>
|
||||
@ -33,6 +35,8 @@
|
||||
#define SUN6I_DSI_CTL_EN BIT(0)
|
||||
|
||||
#define SUN6I_DSI_BASIC_CTL_REG 0x00c
|
||||
#define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n) (((n) & 0xf) << 4)
|
||||
#define SUN6I_DSI_BASIC_CTL_TRAIL_FILL BIT(3)
|
||||
#define SUN6I_DSI_BASIC_CTL_HBP_DIS BIT(2)
|
||||
#define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS BIT(1)
|
||||
#define SUN6I_DSI_BASIC_CTL_VIDEO_BURST BIT(0)
|
||||
@ -153,6 +157,8 @@
|
||||
|
||||
#define SUN6I_DSI_CMD_TX_REG(n) (0x300 + (n) * 0x04)
|
||||
|
||||
#define SUN6I_DSI_SYNC_POINT 40
|
||||
|
||||
enum sun6i_dsi_start_inst {
|
||||
DSI_START_LPRX,
|
||||
DSI_START_LPTX,
|
||||
@ -358,7 +364,54 @@ static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi,
|
||||
static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
return mode->vtotal - (mode->vsync_end - mode->vdisplay) + 1;
|
||||
u16 start = clamp(mode->vtotal - mode->vdisplay - 10, 8, 100);
|
||||
u16 delay = mode->vtotal - (mode->vsync_end - mode->vdisplay) + start;
|
||||
|
||||
if (delay > mode->vtotal)
|
||||
delay = delay % mode->vtotal;
|
||||
|
||||
return max_t(u16, delay, 1);
|
||||
}
|
||||
|
||||
static u16 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
|
||||
|
||||
return mode->htotal * Bpp / device->lanes;
|
||||
}
|
||||
|
||||
static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode,
|
||||
u16 line_num, u16 edge1)
|
||||
{
|
||||
u16 edge0 = edge1;
|
||||
|
||||
edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8;
|
||||
|
||||
if (edge0 > line_num)
|
||||
return edge0 - line_num;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode,
|
||||
u16 line_num)
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
|
||||
unsigned int hbp = mode->htotal - mode->hsync_end;
|
||||
u16 edge1;
|
||||
|
||||
edge1 = SUN6I_DSI_SYNC_POINT;
|
||||
edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes;
|
||||
|
||||
if (edge1 > line_num)
|
||||
return line_num;
|
||||
|
||||
return edge1;
|
||||
}
|
||||
|
||||
static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
|
||||
@ -367,7 +420,23 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
u32 val = 0;
|
||||
|
||||
if ((mode->hsync_end - mode->hdisplay) > 20) {
|
||||
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
|
||||
u16 line_num = sun6i_dsi_get_line_num(dsi, mode);
|
||||
u16 edge0, edge1;
|
||||
|
||||
edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num);
|
||||
edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1);
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BURST_DRQ_REG,
|
||||
SUN6I_DSI_BURST_DRQ_EDGE0(edge0) |
|
||||
SUN6I_DSI_BURST_DRQ_EDGE1(edge1));
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BURST_LINE_REG,
|
||||
SUN6I_DSI_BURST_LINE_NUM(line_num) |
|
||||
SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT));
|
||||
|
||||
val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE;
|
||||
} else if ((mode->hsync_end - mode->hdisplay) > 20) {
|
||||
/* Maaaaaagic */
|
||||
u16 drq = (mode->hsync_end - mode->hdisplay) - 20;
|
||||
|
||||
@ -384,8 +453,19 @@ static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi,
|
||||
static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
u16 delay = 50 - 1;
|
||||
|
||||
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
|
||||
delay = (mode->htotal - mode->hdisplay) * 150;
|
||||
delay /= (mode->clock / 1000) * 8;
|
||||
delay -= 50;
|
||||
}
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG,
|
||||
2 << (4 * DSI_INST_ID_LP11) |
|
||||
3 << (4 * DSI_INST_ID_DLY));
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0),
|
||||
SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) |
|
||||
SUN6I_DSI_INST_LOOP_NUM_N1(delay));
|
||||
@ -451,49 +531,68 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
{
|
||||
struct mipi_dsi_device *device = dsi->device;
|
||||
unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(device->format) / 8;
|
||||
u16 hbp, hfp, hsa, hblk, vblk;
|
||||
u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0;
|
||||
u32 basic_ctl = 0;
|
||||
size_t bytes;
|
||||
u8 *buffer;
|
||||
|
||||
/* Do all timing calculations up front to allocate buffer space */
|
||||
|
||||
/*
|
||||
* A sync period is composed of a blanking packet (4 bytes +
|
||||
* payload + 2 bytes) and a sync event packet (4 bytes). Its
|
||||
* minimal size is therefore 10 bytes
|
||||
*/
|
||||
if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
|
||||
hblk = mode->hdisplay * Bpp;
|
||||
basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST |
|
||||
SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS |
|
||||
SUN6I_DSI_BASIC_CTL_HBP_DIS;
|
||||
|
||||
if (device->lanes == 4)
|
||||
basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL |
|
||||
SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc);
|
||||
} else {
|
||||
/*
|
||||
* A sync period is composed of a blanking packet (4
|
||||
* bytes + payload + 2 bytes) and a sync event packet
|
||||
* (4 bytes). Its minimal size is therefore 10 bytes
|
||||
*/
|
||||
#define HSA_PACKET_OVERHEAD 10
|
||||
hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
|
||||
(mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
|
||||
hsa = max((unsigned int)HSA_PACKET_OVERHEAD,
|
||||
(mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* The backporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
/*
|
||||
* The backporch is set using a blanking packet (4
|
||||
* bytes + payload + 2 bytes). Its minimal size is
|
||||
* therefore 6 bytes
|
||||
*/
|
||||
#define HBP_PACKET_OVERHEAD 6
|
||||
hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
|
||||
(mode->hsync_start - mode->hdisplay) * Bpp - HBP_PACKET_OVERHEAD);
|
||||
hbp = max((unsigned int)HBP_PACKET_OVERHEAD,
|
||||
(mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* The frontporch is set using a blanking packet (4 bytes +
|
||||
* payload + 2 bytes). Its minimal size is therefore 6 bytes
|
||||
*/
|
||||
/*
|
||||
* The frontporch is set using a blanking packet (4
|
||||
* bytes + payload + 2 bytes). Its minimal size is
|
||||
* therefore 6 bytes
|
||||
*/
|
||||
#define HFP_PACKET_OVERHEAD 6
|
||||
hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
|
||||
(mode->htotal - mode->hsync_end) * Bpp - HFP_PACKET_OVERHEAD);
|
||||
hfp = max((unsigned int)HFP_PACKET_OVERHEAD,
|
||||
(mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* hblk seems to be the line + porches length.
|
||||
*/
|
||||
hblk = mode->htotal * Bpp - hsa;
|
||||
/*
|
||||
* The blanking is set using a sync event (4 bytes)
|
||||
* and a blanking packet (4 bytes + payload + 2
|
||||
* bytes). Its minimal size is therefore 10 bytes.
|
||||
*/
|
||||
#define HBLK_PACKET_OVERHEAD 10
|
||||
hblk = max((unsigned int)HBLK_PACKET_OVERHEAD,
|
||||
(mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp -
|
||||
HBLK_PACKET_OVERHEAD);
|
||||
|
||||
/*
|
||||
* And I'm not entirely sure what vblk is about. The driver in
|
||||
* Allwinner BSP is using a rather convoluted calculation
|
||||
* there only for 4 lanes. However, using 0 (the !4 lanes
|
||||
* case) even with a 4 lanes screen seems to work...
|
||||
*/
|
||||
vblk = 0;
|
||||
/*
|
||||
* And I'm not entirely sure what vblk is about. The driver in
|
||||
* Allwinner BSP is using a rather convoluted calculation
|
||||
* there only for 4 lanes. However, using 0 (the !4 lanes
|
||||
* case) even with a 4 lanes screen seems to work...
|
||||
*/
|
||||
vblk = 0;
|
||||
}
|
||||
|
||||
/* How many bytes do we need to send all payloads? */
|
||||
bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk);
|
||||
@ -501,7 +600,7 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
if (WARN_ON(!buffer))
|
||||
return;
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, 0);
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_CTL_REG, basic_ctl);
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_SYNC_HSS_REG,
|
||||
sun6i_dsi_build_sync_pkt(MIPI_DSI_H_SYNC_START,
|
||||
@ -526,8 +625,8 @@ static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi,
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE0_REG,
|
||||
SUN6I_DSI_BASIC_SIZE0_VSA(mode->vsync_end -
|
||||
mode->vsync_start) |
|
||||
SUN6I_DSI_BASIC_SIZE0_VBP(mode->vsync_start -
|
||||
mode->vdisplay));
|
||||
SUN6I_DSI_BASIC_SIZE0_VBP(mode->vtotal -
|
||||
mode->vsync_end));
|
||||
|
||||
regmap_write(dsi->regs, SUN6I_DSI_BASIC_SIZE1_REG,
|
||||
SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) |
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_mipi_dsi.h>
|
||||
|
||||
#define SUN6I_DSI_TCON_DIV 4
|
||||
|
||||
struct sun6i_dsi {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
|
@ -325,38 +325,22 @@ static struct regmap_config sun8i_mixer_regmap_config = {
|
||||
|
||||
static int sun8i_mixer_of_get_id(struct device_node *node)
|
||||
{
|
||||
struct device_node *port, *ep;
|
||||
int ret = -EINVAL;
|
||||
struct device_node *ep, *remote;
|
||||
struct of_endpoint of_ep;
|
||||
|
||||
/* output is port 1 */
|
||||
port = of_graph_get_port_by_id(node, 1);
|
||||
if (!port)
|
||||
/* Output port is 1, and we want the first endpoint. */
|
||||
ep = of_graph_get_endpoint_by_regs(node, 1, -1);
|
||||
if (!ep)
|
||||
return -EINVAL;
|
||||
|
||||
/* try to find downstream endpoint */
|
||||
for_each_available_child_of_node(port, ep) {
|
||||
struct device_node *remote;
|
||||
u32 reg;
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
of_node_put(ep);
|
||||
if (!remote)
|
||||
return -EINVAL;
|
||||
|
||||
remote = of_graph_get_remote_endpoint(ep);
|
||||
if (!remote)
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(remote, "reg", ®);
|
||||
if (!ret) {
|
||||
of_node_put(remote);
|
||||
of_node_put(ep);
|
||||
of_node_put(port);
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
of_node_put(remote);
|
||||
}
|
||||
|
||||
of_node_put(port);
|
||||
|
||||
return ret;
|
||||
of_graph_parse_endpoint(remote, &of_ep);
|
||||
of_node_put(remote);
|
||||
return of_ep.id;
|
||||
}
|
||||
|
||||
static int sun8i_mixer_bind(struct device *dev, struct device *master,
|
||||
@ -554,6 +538,7 @@ static int sun8i_mixer_remove(struct platform_device *pdev)
|
||||
static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@ -561,6 +546,7 @@ static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
|
||||
static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
|
||||
.ccsc = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@ -569,6 +555,7 @@ static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.mod_rate = 432000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@ -577,6 +564,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@ -585,6 +573,7 @@ static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
|
||||
.ccsc = 1,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@ -593,6 +582,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
|
||||
.vi_num = 2,
|
||||
.ui_num = 1,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ccsc = 0,
|
||||
.mod_rate = 150000000,
|
||||
};
|
||||
@ -601,6 +591,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
|
||||
.ccsc = 0,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@ -609,6 +600,7 @@ static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
|
||||
.ccsc = 1,
|
||||
.mod_rate = 297000000,
|
||||
.scaler_mask = 0x3,
|
||||
.scanline_yuv = 2048,
|
||||
.ui_num = 1,
|
||||
.vi_num = 1,
|
||||
};
|
||||
@ -618,6 +610,7 @@ static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
|
||||
.is_de3 = true,
|
||||
.mod_rate = 600000000,
|
||||
.scaler_mask = 0xf,
|
||||
.scanline_yuv = 4096,
|
||||
.ui_num = 3,
|
||||
.vi_num = 1,
|
||||
};
|
||||
|
@ -159,6 +159,7 @@ struct de2_fmt_info {
|
||||
* @mod_rate: module clock rate that needs to be set in order to have
|
||||
* a functional block.
|
||||
* @is_de3: true, if this is next gen display engine 3.0, false otherwise.
|
||||
* @scaline_yuv: size of a scanline for VI scaler for YUV formats.
|
||||
*/
|
||||
struct sun8i_mixer_cfg {
|
||||
int vi_num;
|
||||
@ -167,6 +168,7 @@ struct sun8i_mixer_cfg {
|
||||
int ccsc;
|
||||
unsigned long mod_rate;
|
||||
unsigned int is_de3 : 1;
|
||||
unsigned int scanline_yuv;
|
||||
};
|
||||
|
||||
struct sun8i_mixer {
|
||||
|
@ -80,6 +80,8 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
u32 bld_base, ch_base;
|
||||
u32 outsize, insize;
|
||||
u32 hphase, vphase;
|
||||
u32 hn = 0, hm = 0;
|
||||
u32 vn = 0, vm = 0;
|
||||
bool subsampled;
|
||||
|
||||
DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
|
||||
@ -137,12 +139,41 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
subsampled = format->hsub > 1 || format->vsub > 1;
|
||||
|
||||
if (insize != outsize || subsampled || hphase || vphase) {
|
||||
u32 hscale, vscale;
|
||||
unsigned int scanline, required;
|
||||
struct drm_display_mode *mode;
|
||||
u32 hscale, vscale, fps;
|
||||
u64 ability;
|
||||
|
||||
DRM_DEBUG_DRIVER("HW scaling is enabled\n");
|
||||
|
||||
hscale = state->src_w / state->crtc_w;
|
||||
vscale = state->src_h / state->crtc_h;
|
||||
mode = &plane->state->crtc->state->mode;
|
||||
fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
|
||||
ability = clk_get_rate(mixer->mod_clk);
|
||||
/* BSP algorithm assumes 80% efficiency of VI scaler unit */
|
||||
ability *= 80;
|
||||
do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
|
||||
|
||||
required = src_h * 100 / dst_h;
|
||||
|
||||
if (ability < required) {
|
||||
DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
|
||||
vm = src_h;
|
||||
vn = (u32)ability * dst_h / 100;
|
||||
src_h = vn;
|
||||
}
|
||||
|
||||
/* it seems that every RGB scaler has buffer for 2048 pixels */
|
||||
scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
|
||||
|
||||
if (src_w > scanline) {
|
||||
DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
|
||||
hm = src_w;
|
||||
hn = scanline;
|
||||
src_w = hn;
|
||||
}
|
||||
|
||||
hscale = (src_w << 16) / dst_w;
|
||||
vscale = (src_h << 16) / dst_h;
|
||||
|
||||
sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
|
||||
dst_h, hscale, vscale, hphase, vphase,
|
||||
@ -153,6 +184,23 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
|
||||
sun8i_vi_scaler_enable(mixer, channel, false);
|
||||
}
|
||||
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(hm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(hm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(vm));
|
||||
regmap_write(mixer->engine.regs,
|
||||
SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
|
||||
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
|
||||
SUN8I_MIXER_CHAN_VI_DS_M(vm));
|
||||
|
||||
/* Set base coordinates */
|
||||
DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
|
||||
state->dst.x1, state->dst.y1);
|
||||
|
@ -24,6 +24,14 @@
|
||||
((base) + 0x30 * (layer) + 0x18 + 4 * (plane))
|
||||
#define SUN8I_MIXER_CHAN_VI_OVL_SIZE(base) \
|
||||
((base) + 0xe8)
|
||||
#define SUN8I_MIXER_CHAN_VI_HDS_Y(base) \
|
||||
((base) + 0xf0)
|
||||
#define SUN8I_MIXER_CHAN_VI_HDS_UV(base) \
|
||||
((base) + 0xf4)
|
||||
#define SUN8I_MIXER_CHAN_VI_VDS_Y(base) \
|
||||
((base) + 0xf8)
|
||||
#define SUN8I_MIXER_CHAN_VI_VDS_UV(base) \
|
||||
((base) + 0xfc)
|
||||
|
||||
#define SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN BIT(0)
|
||||
/* RGB mode should be set for RGB formats and cleared for YCbCr */
|
||||
@ -33,6 +41,9 @@
|
||||
#define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK GENMASK(31, 24)
|
||||
#define SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(x) ((x) << 24)
|
||||
|
||||
#define SUN8I_MIXER_CHAN_VI_DS_N(x) ((x) << 16)
|
||||
#define SUN8I_MIXER_CHAN_VI_DS_M(x) ((x) << 0)
|
||||
|
||||
struct sun8i_mixer;
|
||||
|
||||
struct sun8i_vi_layer {
|
||||
|
@ -1,3 +1,3 @@
|
||||
tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o
|
||||
tinydrm-y := tinydrm-pipe.o tinydrm-helpers.o
|
||||
|
||||
obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
|
||||
|
@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Noralf Trønnes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/tinydrm/tinydrm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/**
|
||||
* DOC: overview
|
||||
*
|
||||
* This library provides driver helpers for very simple display hardware.
|
||||
*
|
||||
* It is based on &drm_simple_display_pipe coupled with a &drm_connector which
|
||||
* has only one fixed &drm_display_mode. The framebuffers are backed by the
|
||||
* cma helper and have support for framebuffer flushing (dirty).
|
||||
* fbdev support is also included.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: core
|
||||
*
|
||||
* The driver allocates &tinydrm_device, initializes it using
|
||||
* devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
|
||||
* and registers the DRM device using devm_tinydrm_register().
|
||||
*/
|
||||
|
||||
static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
|
||||
.fb_create = drm_gem_fb_create_with_dirty,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
|
||||
struct drm_driver *driver)
|
||||
{
|
||||
struct drm_device *drm;
|
||||
|
||||
/*
|
||||
* We don't embed drm_device, because that prevent us from using
|
||||
* devm_kzalloc() to allocate tinydrm_device in the driver since
|
||||
* drm_dev_put() frees the structure. The devm_ functions provide
|
||||
* for easy error handling.
|
||||
*/
|
||||
drm = drm_dev_alloc(driver, parent);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
|
||||
tdev->drm = drm;
|
||||
drm->dev_private = tdev;
|
||||
drm_mode_config_init(drm);
|
||||
drm->mode_config.funcs = &tinydrm_mode_config_funcs;
|
||||
drm->mode_config.allow_fb_modifiers = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tinydrm_fini(struct tinydrm_device *tdev)
|
||||
{
|
||||
drm_mode_config_cleanup(tdev->drm);
|
||||
tdev->drm->dev_private = NULL;
|
||||
drm_dev_put(tdev->drm);
|
||||
}
|
||||
|
||||
static void devm_tinydrm_release(void *data)
|
||||
{
|
||||
tinydrm_fini(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_tinydrm_init - Initialize tinydrm device
|
||||
* @parent: Parent device object
|
||||
* @tdev: tinydrm device
|
||||
* @driver: DRM driver
|
||||
*
|
||||
* This function initializes @tdev, the underlying DRM device and it's
|
||||
* mode_config. Resources will be automatically freed on driver detach (devres)
|
||||
* using drm_mode_config_cleanup() and drm_dev_put().
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
|
||||
struct drm_driver *driver)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tinydrm_init(parent, tdev, driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action(parent, devm_tinydrm_release, tdev);
|
||||
if (ret)
|
||||
tinydrm_fini(tdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_tinydrm_init);
|
||||
|
||||
static int tinydrm_register(struct tinydrm_device *tdev)
|
||||
{
|
||||
struct drm_device *drm = tdev->drm;
|
||||
int ret;
|
||||
|
||||
ret = drm_dev_register(tdev->drm, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_fbdev_generic_setup(drm, 0);
|
||||
if (ret)
|
||||
DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tinydrm_unregister(struct tinydrm_device *tdev)
|
||||
{
|
||||
drm_atomic_helper_shutdown(tdev->drm);
|
||||
drm_dev_unregister(tdev->drm);
|
||||
}
|
||||
|
||||
static void devm_tinydrm_register_release(void *data)
|
||||
{
|
||||
tinydrm_unregister(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_tinydrm_register - Register tinydrm device
|
||||
* @tdev: tinydrm device
|
||||
*
|
||||
* This function registers the underlying DRM device and fbdev.
|
||||
* These resources will be automatically unregistered on driver detach (devres)
|
||||
* and the display pipeline will be disabled.
|
||||
*
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int devm_tinydrm_register(struct tinydrm_device *tdev)
|
||||
{
|
||||
struct device *dev = tdev->drm->dev;
|
||||
int ret;
|
||||
|
||||
ret = tinydrm_register(tdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
|
||||
if (ret)
|
||||
tinydrm_unregister(tdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_tinydrm_register);
|
||||
|
||||
/**
|
||||
* tinydrm_shutdown - Shutdown tinydrm
|
||||
* @tdev: tinydrm device
|
||||
*
|
||||
* This function makes sure that the display pipeline is disabled.
|
||||
* Used by drivers in their shutdown callback to turn off the display
|
||||
* on machine shutdown and reboot.
|
||||
*/
|
||||
void tinydrm_shutdown(struct tinydrm_device *tdev)
|
||||
{
|
||||
drm_atomic_helper_shutdown(tdev->drm);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_shutdown);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -365,3 +365,5 @@ int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
|
||||
EXPORT_SYMBOL(tinydrm_spi_transfer);
|
||||
|
||||
#endif /* CONFIG_SPI */
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/tinydrm/tinydrm.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
|
||||
struct tinydrm_connector {
|
||||
struct drm_connector base;
|
||||
@ -129,7 +129,8 @@ static int tinydrm_rotate_mode(struct drm_display_mode *mode,
|
||||
|
||||
/**
|
||||
* tinydrm_display_pipe_init - Initialize display pipe
|
||||
* @tdev: tinydrm device
|
||||
* @drm: DRM device
|
||||
* @pipe: Display pipe
|
||||
* @funcs: Display pipe functions
|
||||
* @connector_type: Connector type
|
||||
* @formats: Array of supported formats (DRM_FORMAT\_\*)
|
||||
@ -143,16 +144,15 @@ static int tinydrm_rotate_mode(struct drm_display_mode *mode,
|
||||
* Returns:
|
||||
* Zero on success, negative error code on failure.
|
||||
*/
|
||||
int
|
||||
tinydrm_display_pipe_init(struct tinydrm_device *tdev,
|
||||
const struct drm_simple_display_pipe_funcs *funcs,
|
||||
int connector_type,
|
||||
const uint32_t *formats,
|
||||
unsigned int format_count,
|
||||
const struct drm_display_mode *mode,
|
||||
unsigned int rotation)
|
||||
int tinydrm_display_pipe_init(struct drm_device *drm,
|
||||
struct drm_simple_display_pipe *pipe,
|
||||
const struct drm_simple_display_pipe_funcs *funcs,
|
||||
int connector_type,
|
||||
const uint32_t *formats,
|
||||
unsigned int format_count,
|
||||
const struct drm_display_mode *mode,
|
||||
unsigned int rotation)
|
||||
{
|
||||
struct drm_device *drm = tdev->drm;
|
||||
struct drm_display_mode mode_copy;
|
||||
struct drm_connector *connector;
|
||||
int ret;
|
||||
@ -177,7 +177,7 @@ tinydrm_display_pipe_init(struct tinydrm_device *tdev,
|
||||
if (IS_ERR(connector))
|
||||
return PTR_ERR(connector);
|
||||
|
||||
return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
|
||||
return drm_simple_display_pipe_init(drm, pipe, funcs, formats,
|
||||
format_count, modifiers, connector);
|
||||
}
|
||||
EXPORT_SYMBOL(tinydrm_display_pipe_init);
|
||||
|
@ -16,7 +16,9 @@
|
||||
#include <linux/property.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
@ -46,16 +48,18 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
|
||||
u8 addr_mode;
|
||||
int ret;
|
||||
int ret, idx;
|
||||
|
||||
if (!drm_dev_enter(pipe->crtc.dev, &idx))
|
||||
return;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
ret = mipi_dbi_poweron_conditional_reset(mipi);
|
||||
if (ret < 0)
|
||||
return;
|
||||
goto out_exit;
|
||||
if (ret == 1)
|
||||
goto out_enable;
|
||||
|
||||
@ -171,6 +175,8 @@ out_enable:
|
||||
}
|
||||
mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
|
||||
mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
|
||||
out_exit:
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
|
||||
@ -181,7 +187,7 @@ static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
|
||||
};
|
||||
|
||||
static const struct drm_display_mode yx350hv15_mode = {
|
||||
TINYDRM_MODE(320, 480, 60, 75),
|
||||
DRM_SIMPLE_MODE(320, 480, 60, 75),
|
||||
};
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
|
||||
@ -189,6 +195,7 @@ DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
|
||||
static struct drm_driver hx8357d_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
|
||||
.fops = &hx8357d_fops,
|
||||
.release = mipi_dbi_release,
|
||||
DRM_GEM_CMA_VMAP_DRIVER_OPS,
|
||||
.debugfs_init = mipi_dbi_debugfs_init,
|
||||
.name = "hx8357d",
|
||||
@ -213,15 +220,25 @@ MODULE_DEVICE_TABLE(spi, hx8357d_id);
|
||||
static int hx8357d_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct drm_device *drm;
|
||||
struct mipi_dbi *mipi;
|
||||
struct gpio_desc *dc;
|
||||
u32 rotation = 0;
|
||||
int ret;
|
||||
|
||||
mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
|
||||
mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
|
||||
if (!mipi)
|
||||
return -ENOMEM;
|
||||
|
||||
drm = &mipi->drm;
|
||||
ret = devm_drm_dev_init(dev, drm, &hx8357d_driver);
|
||||
if (ret) {
|
||||
kfree(mipi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(dc)) {
|
||||
DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n");
|
||||
@ -238,21 +255,36 @@ static int hx8357d_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mipi_dbi_init(&spi->dev, mipi, &hx8357d_pipe_funcs,
|
||||
&hx8357d_driver, &yx350hv15_mode, rotation);
|
||||
ret = mipi_dbi_init(mipi, &hx8357d_pipe_funcs, &yx350hv15_mode, rotation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, mipi);
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
return devm_tinydrm_register(&mipi->tinydrm);
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, drm);
|
||||
|
||||
drm_fbdev_generic_setup(drm, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx8357d_remove(struct spi_device *spi)
|
||||
{
|
||||
struct drm_device *drm = spi_get_drvdata(spi);
|
||||
|
||||
drm_dev_unplug(drm);
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hx8357d_shutdown(struct spi_device *spi)
|
||||
{
|
||||
struct mipi_dbi *mipi = spi_get_drvdata(spi);
|
||||
|
||||
tinydrm_shutdown(&mipi->tinydrm);
|
||||
drm_atomic_helper_shutdown(spi_get_drvdata(spi));
|
||||
}
|
||||
|
||||
static struct spi_driver hx8357d_spi_driver = {
|
||||
@ -262,6 +294,7 @@ static struct spi_driver hx8357d_spi_driver = {
|
||||
},
|
||||
.id_table = hx8357d_id,
|
||||
.probe = hx8357d_probe,
|
||||
.remove = hx8357d_remove,
|
||||
.shutdown = hx8357d_shutdown,
|
||||
};
|
||||
module_spi_driver(hx8357d_spi_driver);
|
||||
|
@ -20,9 +20,11 @@
|
||||
#include <linux/spi/spi.h>
|
||||
#include <video/mipi_display.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_damage_helper.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
@ -81,20 +83,22 @@ static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
|
||||
static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
struct tinydrm_device *tdev = fb->dev->dev_private;
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
|
||||
unsigned int height = rect->y2 - rect->y1;
|
||||
unsigned int width = rect->x2 - rect->x1;
|
||||
bool swap = mipi->swap_bytes;
|
||||
u16 x_start, y_start;
|
||||
u16 x1, x2, y1, y2;
|
||||
int ret = 0;
|
||||
int idx, ret = 0;
|
||||
bool full;
|
||||
void *tr;
|
||||
|
||||
if (!mipi->enabled)
|
||||
return;
|
||||
|
||||
if (!drm_dev_enter(fb->dev, &idx))
|
||||
return;
|
||||
|
||||
full = width == fb->width && height == fb->height;
|
||||
|
||||
DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
|
||||
@ -157,6 +161,8 @@ static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
|
||||
err_msg:
|
||||
if (ret)
|
||||
dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
|
||||
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
|
||||
@ -181,19 +187,21 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_plane_state *plane_state)
|
||||
{
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
|
||||
struct drm_framebuffer *fb = plane_state->fb;
|
||||
struct device *dev = tdev->drm->dev;
|
||||
struct device *dev = pipe->crtc.dev->dev;
|
||||
struct drm_rect rect = {
|
||||
.x1 = 0,
|
||||
.x2 = fb->width,
|
||||
.y1 = 0,
|
||||
.y2 = fb->height,
|
||||
};
|
||||
int ret;
|
||||
int ret, idx;
|
||||
u8 am_id;
|
||||
|
||||
if (!drm_dev_enter(pipe->crtc.dev, &idx))
|
||||
return;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
mipi_dbi_hw_reset(mipi);
|
||||
@ -207,7 +215,7 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000);
|
||||
if (ret) {
|
||||
DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
|
||||
return;
|
||||
goto out_exit;
|
||||
}
|
||||
ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000);
|
||||
ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000);
|
||||
@ -280,15 +288,23 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
|
||||
|
||||
mipi->enabled = true;
|
||||
ili9225_fb_dirty(fb, &rect);
|
||||
out_exit:
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
{
|
||||
struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
|
||||
struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
|
||||
struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
/*
|
||||
* This callback is not protected by drm_dev_enter/exit since we want to
|
||||
* turn off the display on regular driver unload. It's highly unlikely
|
||||
* that the underlying SPI controller is gone should this be called after
|
||||
* unplug.
|
||||
*/
|
||||
|
||||
if (!mipi->enabled)
|
||||
return;
|
||||
|
||||
@ -301,7 +317,7 @@ static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
|
||||
mipi->enabled = false;
|
||||
}
|
||||
|
||||
static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
|
||||
static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par,
|
||||
size_t num)
|
||||
{
|
||||
struct spi_device *spi = mipi->spi;
|
||||
@ -311,11 +327,11 @@ static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
|
||||
|
||||
gpiod_set_value_cansleep(mipi->dc, 0);
|
||||
speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
|
||||
ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, &cmd, 1);
|
||||
ret = tinydrm_spi_transfer(spi, speed_hz, NULL, 8, cmd, 1);
|
||||
if (ret || !num)
|
||||
return ret;
|
||||
|
||||
if (cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
|
||||
if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !mipi->swap_bytes)
|
||||
bpw = 16;
|
||||
|
||||
gpiod_set_value_cansleep(mipi->dc, 1);
|
||||
@ -332,7 +348,7 @@ static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
|
||||
};
|
||||
|
||||
static const struct drm_display_mode ili9225_mode = {
|
||||
TINYDRM_MODE(176, 220, 35, 44),
|
||||
DRM_SIMPLE_MODE(176, 220, 35, 44),
|
||||
};
|
||||
|
||||
DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
|
||||
@ -341,6 +357,7 @@ static struct drm_driver ili9225_driver = {
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
|
||||
DRIVER_ATOMIC,
|
||||
.fops = &ili9225_fops,
|
||||
.release = mipi_dbi_release,
|
||||
DRM_GEM_CMA_VMAP_DRIVER_OPS,
|
||||
.name = "ili9225",
|
||||
.desc = "Ilitek ILI9225",
|
||||
@ -364,15 +381,25 @@ MODULE_DEVICE_TABLE(spi, ili9225_id);
|
||||
static int ili9225_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct drm_device *drm;
|
||||
struct mipi_dbi *mipi;
|
||||
struct gpio_desc *rs;
|
||||
u32 rotation = 0;
|
||||
int ret;
|
||||
|
||||
mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
|
||||
mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
|
||||
if (!mipi)
|
||||
return -ENOMEM;
|
||||
|
||||
drm = &mipi->drm;
|
||||
ret = devm_drm_dev_init(dev, drm, &ili9225_driver);
|
||||
if (ret) {
|
||||
kfree(mipi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(mipi->reset)) {
|
||||
DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
|
||||
@ -394,21 +421,36 @@ static int ili9225_probe(struct spi_device *spi)
|
||||
/* override the command function set in mipi_dbi_spi_init() */
|
||||
mipi->command = ili9225_dbi_command;
|
||||
|
||||
ret = mipi_dbi_init(&spi->dev, mipi, &ili9225_pipe_funcs,
|
||||
&ili9225_driver, &ili9225_mode, rotation);
|
||||
ret = mipi_dbi_init(mipi, &ili9225_pipe_funcs, &ili9225_mode, rotation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, mipi);
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
return devm_tinydrm_register(&mipi->tinydrm);
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, drm);
|
||||
|
||||
drm_fbdev_generic_setup(drm, 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ili9225_remove(struct spi_device *spi)
|
||||
{
|
||||
struct drm_device *drm = spi_get_drvdata(spi);
|
||||
|
||||
drm_dev_unplug(drm);
|
||||
drm_atomic_helper_shutdown(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ili9225_shutdown(struct spi_device *spi)
|
||||
{
|
||||
struct mipi_dbi *mipi = spi_get_drvdata(spi);
|
||||
|
||||
tinydrm_shutdown(&mipi->tinydrm);
|
||||
drm_atomic_helper_shutdown(spi_get_drvdata(spi));
|
||||
}
|
||||
|
||||
static struct spi_driver ili9225_spi_driver = {
|
||||
@ -419,6 +461,7 @@ static struct spi_driver ili9225_spi_driver = {
|
||||
},
|
||||
.id_table = ili9225_id,
|
||||
.probe = ili9225_probe,
|
||||
.remove = ili9225_remove,
|
||||
.shutdown = ili9225_shutdown,
|
||||
};
|
||||
module_spi_driver(ili9225_spi_driver);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user