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:
Daniel Vetter 2019-03-25 11:05:11 +01:00
commit 0bec6219e5
170 changed files with 4562 additions and 1913 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
.. _component:
======================================
Component Helper for Aggregate Drivers
======================================

View File

@ -256,6 +256,9 @@ DMA
dmam_pool_create()
dmam_pool_destroy()
DRM
devm_drm_dev_init()
GPIO
devm_gpiod_get()
devm_gpiod_get_index()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,6 @@
#include "meson_viu.h"
#include "meson_vpp.h"
#include "meson_venc.h"
#include "meson_canvas.h"
#include "meson_registers.h"
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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", &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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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