diff options
| author | Simona Vetter <simona.vetter@ffwll.ch> | 2025-10-21 10:16:34 +0200 |
|---|---|---|
| committer | Simona Vetter <simona.vetter@ffwll.ch> | 2025-10-21 10:16:34 +0200 |
| commit | 6200442de089468ff283becb81382d6ac23c25e9 (patch) | |
| tree | af0366eaa3bcb7714fa0ec8ff3c73d2ba227747a /drivers/gpu | |
| parent | 335482a53a5766d20d7a14f0532859a7e84cec0a (diff) | |
| parent | aa1c2b073ad23847dd2e7bdc7d30009f34ed7f59 (diff) | |
Merge tag 'drm-misc-next-2025-10-02' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v6.19:
UAPI Changes:
Cross-subsystem Changes:
- fbcon cleanups.
- Make drivers depend on FB_TILEBLITTING instead of selecting it,
and hide FB_MODE_HELPERS.
Core Changes:
- More preparations for rust.
- Throttle dirty worker with vblank
- Use drm_for_each_bridge_in_chain_scoped in drm's bridge code and
assorted fixes.
- Ensure drm_client_modeset tests are enabled in UML.
- Rename ttm_bo_put to ttm_bo_fini, as a further step in removing the
TTM bo refcount.
- Add POST_LT_ADJ_REQ training sequence.
- Show list of removed but still allocated bridges.
- Add a simulated vblank interrupt for hardware without it,
and add some helpers to use them in vkms and hypervdrm.
Driver Changes:
- Assorted small fixes, cleanups and updates to host1x, tegra,
panthor, amdxdna, gud, vc4, ssd130x, ivpu, panfrost, panthor,
sysfb, bridge/sn65dsi86, solomon, ast, tidss.
- Convert drivers from using .round_rate() to .determine_rate()
- Add support for KD116N3730A07/A12, chromebook mt8189, JT101TM023,
LQ079L1SX01, raspberrypi 5" panels.
- Improve reclocking on tegra186+ with nouveau.
- Improve runtime pm in amdxdna.
- Add support for HTX_PAI in imx.
- Use a helper to calculate dumb buffer sizes in most drivers.
Signed-off-by: Simona Vetter <simona.vetter@ffwll.ch>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://lore.kernel.org/r/b412fb91-8545-466a-8102-d89c0f2758a7@linux.intel.com
Diffstat (limited to 'drivers/gpu')
123 files changed, 4506 insertions, 1634 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4b2f7d794275..c2672f369aed 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -150,7 +150,8 @@ drm_kms_helper-y := \ drm_plane_helper.o \ drm_probe_helper.o \ drm_self_refresh_helper.o \ - drm_simple_kms_helper.o + drm_simple_kms_helper.o \ + drm_vblank_helper.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index b7ebae289bea..094c508d3d44 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -198,7 +198,7 @@ static void amdgpu_gem_object_free(struct drm_gem_object *gobj) struct amdgpu_bo *aobj = gem_to_amdgpu_bo(gobj); amdgpu_hmm_unregister(aobj); - ttm_bo_put(&aobj->tbo); + ttm_bo_fini(&aobj->tbo); } int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile index 2547613155da..cdbcba3b43ad 100644 --- a/drivers/gpu/drm/ast/Makefile +++ b/drivers/gpu/drm/ast/Makefile @@ -6,7 +6,9 @@ ast-y := \ ast_2000.o \ ast_2100.o \ + ast_2200.o \ ast_2300.o \ + ast_2400.o \ ast_2500.o \ ast_2600.o \ ast_cursor.o \ @@ -14,7 +16,6 @@ ast-y := \ ast_dp501.o \ ast_dp.o \ ast_drv.o \ - ast_main.o \ ast_mm.o \ ast_mode.o \ ast_post.o \ diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c index 41c2aa1e425a..03b0dcea43d1 100644 --- a/drivers/gpu/drm/ast/ast_2000.c +++ b/drivers/gpu/drm/ast/ast_2000.c @@ -27,6 +27,9 @@ */ #include <linux/delay.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> #include "ast_drv.h" #include "ast_post.h" @@ -147,3 +150,101 @@ int ast_2000_post(struct ast_device *ast) return 0; } + +/* + * Mode setting + */ + +const struct ast_vbios_dclk_info ast_2000_dclk_table[] = { + {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */ + {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ + {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ + {0x76, 0x63, 0x01}, /* 03: VCLK36 */ + {0xee, 0x67, 0x01}, /* 04: VCLK40 */ + {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ + {0xc6, 0x64, 0x01}, /* 06: VCLK50 */ + {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ + {0x80, 0x64, 0x00}, /* 08: VCLK65 */ + {0x7b, 0x63, 0x00}, /* 09: VCLK75 */ + {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */ + {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */ + {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */ + {0x85, 0x24, 0x00}, /* 0d: VCLK135 */ + {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */ + {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */ + {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ + {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */ + {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ + {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ + {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ + {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ + {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ + {0x77, 0x58, 0x80}, /* 17: VCLK119 */ + {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ + {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ + {0x3b, 0x2c, 0x81}, /* 1a: VCLK118_25 */ +}; + +/* + * Device initialization + */ + +void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post) +{ + enum ast_tx_chip tx_chip = AST_TX_NONE; + u8 vgacra3; + + /* + * VGACRA3 Enhanced Color Mode Register, check if DVO is already + * enabled, in that case, assume we have a SIL164 TMDS transmitter + * + * Don't make that assumption if we the chip wasn't enabled and + * is at power-on reset, otherwise we'll incorrectly "detect" a + * SIL164 when there is none. + */ + if (!need_post) { + vgacra3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xff); + if (vgacra3 & AST_IO_VGACRA3_DVO_ENABLED) + tx_chip = AST_TX_SIL164; + } + + __ast_device_set_tx_chip(ast, tx_chip); +} + +struct drm_device *ast_2000_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs); + + ast_2000_detect_tx_chip(ast, need_post); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c index 829e3b8b0d19..540972daec52 100644 --- a/drivers/gpu/drm/ast/ast_2100.c +++ b/drivers/gpu/drm/ast/ast_2100.c @@ -27,6 +27,9 @@ */ #include <linux/delay.h> +#include <linux/pci.h> + +#include <drm/drm_drv.h> #include "ast_drv.h" #include "ast_post.h" @@ -386,3 +389,85 @@ int ast_2100_post(struct ast_device *ast) return 0; } + +/* + * Widescreen detection + */ + +/* Try to detect WSXGA+ on Gen2+ */ +bool __ast_2100_detect_wsxga_p(struct ast_device *ast) +{ + u8 vgacrd0 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd0); + + if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_BY_BMC)) + return true; + if (vgacrd0 & AST_IO_VGACRD0_IKVM_WIDESCREEN) + return true; + + return false; +} + +/* Try to detect WUXGA on Gen2+ */ +bool __ast_2100_detect_wuxga(struct ast_device *ast) +{ + u8 vgacrd1; + + if (ast->support_fullhd) { + vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1); + if (!(vgacrd1 & AST_IO_VGACRD1_SUPPORTS_WUXGA)) + return true; + } + + return false; +} + +static void ast_2100_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast)) { + ast->support_wsxga_p = true; + if (ast->chip == AST2100) + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +struct drm_device *ast_2100_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs); + + ast_2000_detect_tx_chip(ast, need_post); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_2100_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2200.c b/drivers/gpu/drm/ast/ast_2200.c new file mode 100644 index 000000000000..4795966dc2a7 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2200.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_drv.h> + +#include "ast_drv.h" + +static void ast_2200_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast)) { + ast->support_wsxga_p = true; + if (ast->chip == AST2200) + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +struct drm_device *ast_2200_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs); + + ast_2000_detect_tx_chip(ast, need_post); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_2200_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} + diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c index dc2a32244689..d1d63e58f3d6 100644 --- a/drivers/gpu/drm/ast/ast_2300.c +++ b/drivers/gpu/drm/ast/ast_2300.c @@ -27,6 +27,12 @@ */ #include <linux/delay.h> +#include <linux/pci.h> +#include <linux/sizes.h> + +#include <drm/drm_drv.h> +#include <drm/drm_managed.h> +#include <drm/drm_print.h> #include "ast_drv.h" #include "ast_post.h" @@ -1326,3 +1332,125 @@ int ast_2300_post(struct ast_device *ast) return 0; } + +/* + * Device initialization + */ + +void ast_2300_detect_tx_chip(struct ast_device *ast) +{ + enum ast_tx_chip tx_chip = AST_TX_NONE; + struct drm_device *dev = &ast->base; + u8 vgacrd1; + + /* + * On AST GEN4+, look at the configuration set by the SoC in + * the SOC scratch register #1 bits 11:8 (interestingly marked + * as "reserved" in the spec) + */ + vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, + AST_IO_VGACRD1_TX_TYPE_MASK); + switch (vgacrd1) { + /* + * GEN4 to GEN6 + */ + case AST_IO_VGACRD1_TX_SIL164_VBIOS: + tx_chip = AST_TX_SIL164; + break; + case AST_IO_VGACRD1_TX_DP501_VBIOS: + ast->dp501_fw_addr = drmm_kzalloc(dev, SZ_32K, GFP_KERNEL); + if (ast->dp501_fw_addr) { + /* backup firmware */ + if (ast_backup_fw(ast, ast->dp501_fw_addr, SZ_32K)) { + drmm_kfree(dev, ast->dp501_fw_addr); + ast->dp501_fw_addr = NULL; + } + } + fallthrough; + case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW: + tx_chip = AST_TX_DP501; + break; + /* + * GEN7+ + */ + case AST_IO_VGACRD1_TX_ASTDP: + tx_chip = AST_TX_ASTDP; + break; + /* + * Several of the listed TX chips are not explicitly supported + * by the ast driver. If these exist in real-world devices, they + * are most likely reported as VGA or SIL164 outputs. We warn here + * to get bug reports for these devices. If none come in for some + * time, we can begin to fail device probing on these values. + */ + case AST_IO_VGACRD1_TX_ITE66121_VBIOS: + drm_warn(dev, "ITE IT66121 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast)); + break; + case AST_IO_VGACRD1_TX_CH7003_VBIOS: + drm_warn(dev, "Chrontel CH7003 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast)); + break; + case AST_IO_VGACRD1_TX_ANX9807_VBIOS: + drm_warn(dev, "Analogix ANX9807 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast)); + break; + } + + __ast_device_set_tx_chip(ast, tx_chip); +} + +static void ast_2300_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1300) { + ast->support_wsxga_p = true; + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +struct drm_device *ast_2300_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs); + + ast_2300_detect_tx_chip(ast); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (ast->vram_size < pci_resource_len(pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + + ast_2300_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2400.c b/drivers/gpu/drm/ast/ast_2400.c new file mode 100644 index 000000000000..596338ea22f4 --- /dev/null +++ b/drivers/gpu/drm/ast/ast_2400.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ + +#include <linux/pci.h> + +#include <drm/drm_drv.h> +#include <drm/drm_print.h> + +#include "ast_drv.h" + +static void ast_2400_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1400) { + ast->support_wsxga_p = true; + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +struct drm_device *ast_2400_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs); + + ast_2300_detect_tx_chip(ast); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (ast->vram_size < pci_resource_len(pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + + ast_2400_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c index 1e541498ea67..2c56db644f06 100644 --- a/drivers/gpu/drm/ast/ast_2500.c +++ b/drivers/gpu/drm/ast/ast_2500.c @@ -27,7 +27,9 @@ */ #include <linux/delay.h> +#include <linux/pci.h> +#include <drm/drm_drv.h> #include <drm/drm_print.h> #include "ast_drv.h" @@ -567,3 +569,99 @@ int ast_2500_post(struct ast_device *ast) return 0; } + +/* + * Mode setting + */ + +const struct ast_vbios_dclk_info ast_2500_dclk_table[] = { + {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */ + {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ + {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ + {0x76, 0x63, 0x01}, /* 03: VCLK36 */ + {0xee, 0x67, 0x01}, /* 04: VCLK40 */ + {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ + {0xc6, 0x64, 0x01}, /* 06: VCLK50 */ + {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ + {0x80, 0x64, 0x00}, /* 08: VCLK65 */ + {0x7b, 0x63, 0x00}, /* 09: VCLK75 */ + {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */ + {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */ + {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */ + {0x85, 0x24, 0x00}, /* 0d: VCLK135 */ + {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */ + {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */ + {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ + {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */ + {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ + {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ + {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ + {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ + {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ + {0x58, 0x01, 0x42}, /* 17: VCLK119 */ + {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ + {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ + {0x44, 0x20, 0x43}, /* 1a: VCLK118_25 */ +}; + +/* + * Device initialization + */ + +static void ast_2500_detect_widescreen(struct ast_device *ast) +{ + if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST2510) { + ast->support_wsxga_p = true; + ast->support_fullhd = true; + } + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +struct drm_device *ast_2500_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs); + + ast_2300_detect_tx_chip(ast); + + if (need_post) { + ret = ast_post_gpu(ast); + if (ret) + return ERR_PTR(ret); + } + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + /* map reserved buffer */ + ast->dp501_fw_buf = NULL; + if (ast->vram_size < pci_resource_len(pdev, 0)) { + ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); + if (!ast->dp501_fw_buf) + drm_info(dev, "failed to map reserved buffer!\n"); + } + + ast_2500_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c index 8d75a47444f5..30490c473797 100644 --- a/drivers/gpu/drm/ast/ast_2600.c +++ b/drivers/gpu/drm/ast/ast_2600.c @@ -26,6 +26,10 @@ * Authors: Dave Airlie <airlied@redhat.com> */ +#include <linux/pci.h> + +#include <drm/drm_drv.h> + #include "ast_drv.h" #include "ast_post.h" @@ -42,3 +46,62 @@ int ast_2600_post(struct ast_device *ast) return 0; } + +/* + * Device initialization + */ + +static void ast_2600_detect_widescreen(struct ast_device *ast) +{ + ast->support_wsxga_p = true; + ast->support_fullhd = true; + if (__ast_2100_detect_wuxga(ast)) + ast->support_wuxga = true; +} + +struct drm_device *ast_2600_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post) +{ + struct drm_device *dev; + struct ast_device *ast; + int ret; + + ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); + if (IS_ERR(ast)) + return ERR_CAST(ast); + dev = &ast->base; + + ast_device_init(ast, chip, config_mode, regs, ioregs); + + ast_2300_detect_tx_chip(ast); + + switch (ast->tx_chip) { + case AST_TX_ASTDP: + ret = ast_post_gpu(ast); + break; + default: + ret = 0; + if (need_post) + ret = ast_post_gpu(ast); + break; + } + if (ret) + return ERR_PTR(ret); + + ret = ast_mm_init(ast); + if (ret) + return ERR_PTR(ret); + + ast_2600_detect_widescreen(ast); + + ret = ast_mode_config_init(ast); + if (ret) + return ERR_PTR(ret); + + return dev; +} diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 473faa92d08c..a89735c6a462 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -37,6 +37,7 @@ #include <drm/drm_fbdev_shmem.h> #include <drm/drm_gem_shmem_helper.h> #include <drm/drm_module.h> +#include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include "ast_drv.h" @@ -46,6 +47,32 @@ static int ast_modeset = -1; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, ast_modeset, int, 0400); +void ast_device_init(struct ast_device *ast, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs) +{ + ast->chip = chip; + ast->config_mode = config_mode; + ast->regs = regs; + ast->ioregs = ioregs; +} + +void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip) +{ + static const char * const info_str[] = { + "analog VGA", + "Sil164 TMDS transmitter", + "DP501 DisplayPort transmitter", + "ASPEED DisplayPort transmitter", + }; + + drm_info(&ast->base, "Using %s\n", info_str[tx_chip]); + + ast->tx_chip = tx_chip; +} + /* * DRM driver */ @@ -266,7 +293,7 @@ static int ast_detect_chip(struct pci_dev *pdev, *chip_out = chip; *config_mode_out = config_mode; - return 0; + return __AST_CHIP_GEN(chip); } static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -277,6 +304,7 @@ static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) void __iomem *ioregs; enum ast_config_mode config_mode; enum ast_chip chip; + unsigned int chip_gen; struct drm_device *drm; bool need_post = false; @@ -349,10 +377,43 @@ static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return ret; ret = ast_detect_chip(pdev, regs, ioregs, &chip, &config_mode); - if (ret) + if (ret < 0) return ret; + chip_gen = ret; - drm = ast_device_create(pdev, &ast_driver, chip, config_mode, regs, ioregs, need_post); + switch (chip_gen) { + case 1: + drm = ast_2000_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 2: + drm = ast_2100_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 3: + drm = ast_2200_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 4: + drm = ast_2300_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 5: + drm = ast_2400_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 6: + drm = ast_2500_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + case 7: + drm = ast_2600_device_create(pdev, &ast_driver, chip, config_mode, + regs, ioregs, need_post); + break; + default: + dev_err(&pdev->dev, "Gen%d not supported\n", chip_gen); + return -ENODEV; + } if (IS_ERR(drm)) return PTR_ERR(drm); pci_set_drvdata(pdev, drm); diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index c15aef014f69..35c476c85b9a 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -217,14 +217,6 @@ static inline struct ast_device *to_ast_device(struct drm_device *dev) return container_of(dev, struct ast_device, base); } -struct drm_device *ast_device_create(struct pci_dev *pdev, - const struct drm_driver *drv, - enum ast_chip chip, - enum ast_config_mode config_mode, - void __iomem *regs, - void __iomem *ioregs, - bool need_post); - static inline unsigned long __ast_gen(struct ast_device *ast) { return __AST_CHIP_GEN(ast->chip); @@ -415,21 +407,88 @@ struct ast_crtc_state { int ast_mm_init(struct ast_device *ast); +/* ast_drv.c */ +void ast_device_init(struct ast_device *ast, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs); +void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip); + /* ast_2000.c */ int ast_2000_post(struct ast_device *ast); +extern const struct ast_vbios_dclk_info ast_2000_dclk_table[]; +void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post); +struct drm_device *ast_2000_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); /* ast_2100.c */ int ast_2100_post(struct ast_device *ast); +bool __ast_2100_detect_wsxga_p(struct ast_device *ast); +bool __ast_2100_detect_wuxga(struct ast_device *ast); +struct drm_device *ast_2100_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2200.c */ +struct drm_device *ast_2200_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); /* ast_2300.c */ int ast_2300_post(struct ast_device *ast); +void ast_2300_detect_tx_chip(struct ast_device *ast); +struct drm_device *ast_2300_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); + +/* ast_2400.c */ +struct drm_device *ast_2400_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); /* ast_2500.c */ void ast_2500_patch_ahb(void __iomem *regs); int ast_2500_post(struct ast_device *ast); +extern const struct ast_vbios_dclk_info ast_2500_dclk_table[]; +struct drm_device *ast_2500_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); /* ast_2600.c */ int ast_2600_post(struct ast_device *ast); +struct drm_device *ast_2600_device_create(struct pci_dev *pdev, + const struct drm_driver *drv, + enum ast_chip chip, + enum ast_config_mode config_mode, + void __iomem *regs, + void __iomem *ioregs, + bool need_post); /* ast post */ int ast_post_gpu(struct ast_device *ast); diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c deleted file mode 100644 index 3eea6a6cdacd..000000000000 --- a/drivers/gpu/drm/ast/ast_main.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright 2012 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - */ -/* - * Authors: Dave Airlie <airlied@redhat.com> - */ - -#include <linux/of.h> -#include <linux/pci.h> - -#include <drm/drm_atomic_helper.h> -#include <drm/drm_drv.h> -#include <drm/drm_gem.h> -#include <drm/drm_managed.h> - -#include "ast_drv.h" - -/* Try to detect WSXGA+ on Gen2+ */ -static bool __ast_2100_detect_wsxga_p(struct ast_device *ast) -{ - u8 vgacrd0 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd0); - - if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_BY_BMC)) - return true; - if (vgacrd0 & AST_IO_VGACRD0_IKVM_WIDESCREEN) - return true; - - return false; -} - -/* Try to detect WUXGA on Gen2+ */ -static bool __ast_2100_detect_wuxga(struct ast_device *ast) -{ - u8 vgacrd1; - - if (ast->support_fullhd) { - vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1); - if (!(vgacrd1 & AST_IO_VGACRD1_SUPPORTS_WUXGA)) - return true; - } - - return false; -} - -static void ast_detect_widescreen(struct ast_device *ast) -{ - ast->support_wsxga_p = false; - ast->support_fullhd = false; - ast->support_wuxga = false; - - if (AST_GEN(ast) >= 7) { - ast->support_wsxga_p = true; - ast->support_fullhd = true; - if (__ast_2100_detect_wuxga(ast)) - ast->support_wuxga = true; - } else if (AST_GEN(ast) >= 6) { - if (__ast_2100_detect_wsxga_p(ast)) - ast->support_wsxga_p = true; - else if (ast->chip == AST2510) - ast->support_wsxga_p = true; - if (ast->support_wsxga_p) - ast->support_fullhd = true; - if (__ast_2100_detect_wuxga(ast)) - ast->support_wuxga = true; - } else if (AST_GEN(ast) >= 5) { - if (__ast_2100_detect_wsxga_p(ast)) - ast->support_wsxga_p = true; - else if (ast->chip == AST1400) - ast->support_wsxga_p = true; - if (ast->support_wsxga_p) - ast->support_fullhd = true; - if (__ast_2100_detect_wuxga(ast)) - ast->support_wuxga = true; - } else if (AST_GEN(ast) >= 4) { - if (__ast_2100_detect_wsxga_p(ast)) - ast->support_wsxga_p = true; - else if (ast->chip == AST1300) - ast->support_wsxga_p = true; - if (ast->support_wsxga_p) - ast->support_fullhd = true; - if (__ast_2100_detect_wuxga(ast)) - ast->support_wuxga = true; - } else if (AST_GEN(ast) >= 3) { - if (__ast_2100_detect_wsxga_p(ast)) - ast->support_wsxga_p = true; - if (ast->support_wsxga_p) { - if (ast->chip == AST2200) - ast->support_fullhd = true; - } - if (__ast_2100_detect_wuxga(ast)) - ast->support_wuxga = true; - } else if (AST_GEN(ast) >= 2) { - if (__ast_2100_detect_wsxga_p(ast)) - ast->support_wsxga_p = true; - if (ast->support_wsxga_p) { - if (ast->chip == AST2100) - ast->support_fullhd = true; - } - if (__ast_2100_detect_wuxga(ast)) - ast->support_wuxga = true; - } -} - -static void ast_detect_tx_chip(struct ast_device *ast, bool need_post) -{ - static const char * const info_str[] = { - "analog VGA", - "Sil164 TMDS transmitter", - "DP501 DisplayPort transmitter", - "ASPEED DisplayPort transmitter", - }; - - struct drm_device *dev = &ast->base; - u8 vgacra3, vgacrd1; - - /* Check 3rd Tx option (digital output afaik) */ - ast->tx_chip = AST_TX_NONE; - - if (AST_GEN(ast) <= 3) { - /* - * VGACRA3 Enhanced Color Mode Register, check if DVO is already - * enabled, in that case, assume we have a SIL164 TMDS transmitter - * - * Don't make that assumption if we the chip wasn't enabled and - * is at power-on reset, otherwise we'll incorrectly "detect" a - * SIL164 when there is none. - */ - if (!need_post) { - vgacra3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xff); - if (vgacra3 & AST_IO_VGACRA3_DVO_ENABLED) - ast->tx_chip = AST_TX_SIL164; - } - } else { - /* - * On AST GEN4+, look at the configuration set by the SoC in - * the SOC scratch register #1 bits 11:8 (interestingly marked - * as "reserved" in the spec) - */ - vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, - AST_IO_VGACRD1_TX_TYPE_MASK); - switch (vgacrd1) { - /* - * GEN4 to GEN6 - */ - case AST_IO_VGACRD1_TX_SIL164_VBIOS: - ast->tx_chip = AST_TX_SIL164; - break; - case AST_IO_VGACRD1_TX_DP501_VBIOS: - ast->dp501_fw_addr = drmm_kzalloc(dev, 32*1024, GFP_KERNEL); - if (ast->dp501_fw_addr) { - /* backup firmware */ - if (ast_backup_fw(ast, ast->dp501_fw_addr, 32*1024)) { - drmm_kfree(dev, ast->dp501_fw_addr); - ast->dp501_fw_addr = NULL; - } - } - fallthrough; - case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW: - ast->tx_chip = AST_TX_DP501; - break; - /* - * GEN7+ - */ - case AST_IO_VGACRD1_TX_ASTDP: - ast->tx_chip = AST_TX_ASTDP; - break; - /* - * Several of the listed TX chips are not explicitly supported - * by the ast driver. If these exist in real-world devices, they - * are most likely reported as VGA or SIL164 outputs. We warn here - * to get bug reports for these devices. If none come in for some - * time, we can begin to fail device probing on these values. - */ - case AST_IO_VGACRD1_TX_ITE66121_VBIOS: - drm_warn(dev, "ITE IT66121 detected, 0x%x, Gen%lu\n", - vgacrd1, AST_GEN(ast)); - break; - case AST_IO_VGACRD1_TX_CH7003_VBIOS: - drm_warn(dev, "Chrontel CH7003 detected, 0x%x, Gen%lu\n", - vgacrd1, AST_GEN(ast)); - break; - case AST_IO_VGACRD1_TX_ANX9807_VBIOS: - drm_warn(dev, "Analogix ANX9807 detected, 0x%x, Gen%lu\n", - vgacrd1, AST_GEN(ast)); - break; - } - } - - drm_info(dev, "Using %s\n", info_str[ast->tx_chip]); -} - -struct drm_device *ast_device_create(struct pci_dev *pdev, - const struct drm_driver *drv, - enum ast_chip chip, - enum ast_config_mode config_mode, - void __iomem *regs, - void __iomem *ioregs, - bool need_post) -{ - struct drm_device *dev; - struct ast_device *ast; - int ret; - - ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base); - if (IS_ERR(ast)) - return ERR_CAST(ast); - dev = &ast->base; - - ast->chip = chip; - ast->config_mode = config_mode; - ast->regs = regs; - ast->ioregs = ioregs; - - ast_detect_tx_chip(ast, need_post); - switch (ast->tx_chip) { - case AST_TX_ASTDP: - ret = ast_post_gpu(ast); - break; - default: - ret = 0; - if (need_post) - ret = ast_post_gpu(ast); - break; - } - if (ret) - return ERR_PTR(ret); - - ret = ast_mm_init(ast); - if (ret) - return ERR_PTR(ret); - - /* map reserved buffer */ - ast->dp501_fw_buf = NULL; - if (ast->vram_size < pci_resource_len(pdev, 0)) { - ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0); - if (!ast->dp501_fw_buf) - drm_info(dev, "failed to map reserved buffer!\n"); - } - - ast_detect_widescreen(ast); - - ret = ast_mode_config_init(ast); - if (ret) - return ERR_PTR(ret); - - return dev; -} diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 30b011ed0a05..9b6a7c54fbb5 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -373,9 +373,9 @@ static void ast_set_dclk_reg(struct ast_device *ast, const struct ast_vbios_dclk_info *clk_info; if (IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) - clk_info = &dclk_table_ast2500[vmode->dclk_index]; + clk_info = &ast_2500_dclk_table[vmode->dclk_index]; else - clk_info = &dclk_table[vmode->dclk_index]; + clk_info = &ast_2000_dclk_table[vmode->dclk_index]; ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc0, 0x00, clk_info->param1); ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc1, 0x00, clk_info->param2); diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h index f1c9f7e1f1fc..7da5b5c60f41 100644 --- a/drivers/gpu/drm/ast/ast_tables.h +++ b/drivers/gpu/drm/ast/ast_tables.h @@ -33,66 +33,6 @@ #define HiCModeIndex 3 #define TrueCModeIndex 4 -static const struct ast_vbios_dclk_info dclk_table[] = { - {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ - {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ - {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ - {0x76, 0x63, 0x01}, /* 03: VCLK36 */ - {0xEE, 0x67, 0x01}, /* 04: VCLK40 */ - {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ - {0xC6, 0x64, 0x01}, /* 06: VCLK50 */ - {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ - {0x80, 0x64, 0x00}, /* 08: VCLK65 */ - {0x7B, 0x63, 0x00}, /* 09: VCLK75 */ - {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */ - {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */ - {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */ - {0x85, 0x24, 0x00}, /* 0D: VCLK135 */ - {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */ - {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */ - {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ - {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */ - {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ - {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ - {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ - {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ - {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ - {0x77, 0x58, 0x80}, /* 17: VCLK119 */ - {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ - {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ - {0x3b, 0x2c, 0x81}, /* 1A: VCLK118_25 */ -}; - -static const struct ast_vbios_dclk_info dclk_table_ast2500[] = { - {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */ - {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */ - {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */ - {0x76, 0x63, 0x01}, /* 03: VCLK36 */ - {0xEE, 0x67, 0x01}, /* 04: VCLK40 */ - {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */ - {0xC6, 0x64, 0x01}, /* 06: VCLK50 */ - {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */ - {0x80, 0x64, 0x00}, /* 08: VCLK65 */ - {0x7B, 0x63, 0x00}, /* 09: VCLK75 */ - {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */ - {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */ - {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */ - {0x85, 0x24, 0x00}, /* 0D: VCLK135 */ - {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */ - {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */ - {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */ - {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */ - {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */ - {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */ - {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */ - {0x47, 0x6c, 0x80}, /* 15: VCLK71 */ - {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */ - {0x58, 0x01, 0x42}, /* 17: VCLK119 */ - {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */ - {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */ - {0x44, 0x20, 0x43}, /* 1A: VCLK118_25 */ -}; - static const struct ast_vbios_stdtable vbios_stdtable[] = { /* MD_2_3_400 */ { diff --git a/drivers/gpu/drm/bridge/imx/Kconfig b/drivers/gpu/drm/bridge/imx/Kconfig index 9a480c6abb85..b9028a5e5a06 100644 --- a/drivers/gpu/drm/bridge/imx/Kconfig +++ b/drivers/gpu/drm/bridge/imx/Kconfig @@ -18,12 +18,23 @@ config DRM_IMX8MP_DW_HDMI_BRIDGE depends on OF depends on COMMON_CLK select DRM_DW_HDMI + imply DRM_IMX8MP_HDMI_PAI imply DRM_IMX8MP_HDMI_PVI imply PHY_FSL_SAMSUNG_HDMI_PHY help Choose this to enable support for the internal HDMI encoder found on the i.MX8MP SoC. +config DRM_IMX8MP_HDMI_PAI + tristate "Freescale i.MX8MP HDMI PAI bridge support" + depends on OF + select DRM_DW_HDMI + select REGMAP + select REGMAP_MMIO + help + Choose this to enable support for the internal HDMI TX Parallel + Audio Interface found on the Freescale i.MX8MP SoC. + config DRM_IMX8MP_HDMI_PVI tristate "Freescale i.MX8MP HDMI PVI bridge support" depends on OF diff --git a/drivers/gpu/drm/bridge/imx/Makefile b/drivers/gpu/drm/bridge/imx/Makefile index dd5d48584806..8d01fda25451 100644 --- a/drivers/gpu/drm/bridge/imx/Makefile +++ b/drivers/gpu/drm/bridge/imx/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_DRM_IMX_LDB_HELPER) += imx-ldb-helper.o obj-$(CONFIG_DRM_IMX_LEGACY_BRIDGE) += imx-legacy-bridge.o obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE) += imx8mp-hdmi-tx.o +obj-$(CONFIG_DRM_IMX8MP_HDMI_PAI) += imx8mp-hdmi-pai.o obj-$(CONFIG_DRM_IMX8MP_HDMI_PVI) += imx8mp-hdmi-pvi.o obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o obj-$(CONFIG_DRM_IMX8QXP_LDB) += imx8qxp-ldb.o diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c new file mode 100644 index 000000000000..8d13a35b206a --- /dev/null +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <linux/bitfield.h> +#include <linux/component.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <drm/bridge/dw_hdmi.h> +#include <sound/asoundef.h> + +#define HTX_PAI_CTRL 0x00 +#define ENABLE BIT(0) + +#define HTX_PAI_CTRL_EXT 0x04 +#define WTMK_HIGH_MASK GENMASK(31, 24) +#define WTMK_LOW_MASK GENMASK(23, 16) +#define NUM_CH_MASK GENMASK(10, 8) +#define WTMK_HIGH(n) FIELD_PREP(WTMK_HIGH_MASK, (n)) +#define WTMK_LOW(n) FIELD_PREP(WTMK_LOW_MASK, (n)) +#define NUM_CH(n) FIELD_PREP(NUM_CH_MASK, (n) - 1) + +#define HTX_PAI_FIELD_CTRL 0x08 +#define PRE_SEL GENMASK(28, 24) +#define D_SEL GENMASK(23, 20) +#define V_SEL GENMASK(19, 15) +#define U_SEL GENMASK(14, 10) +#define C_SEL GENMASK(9, 5) +#define P_SEL GENMASK(4, 0) + +struct imx8mp_hdmi_pai { + struct regmap *regmap; +}; + +static void imx8mp_hdmi_pai_enable(struct dw_hdmi *dw_hdmi, int channel, + int width, int rate, int non_pcm, + int iec958) +{ + const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi); + struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio; + int val; + + /* PAI set control extended */ + val = WTMK_HIGH(3) | WTMK_LOW(3); + val |= NUM_CH(channel); + regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL_EXT, val); + + /* IEC60958 format */ + if (iec958) { + val = FIELD_PREP_CONST(P_SEL, + __bf_shf(IEC958_SUBFRAME_PARITY)); + val |= FIELD_PREP_CONST(C_SEL, + __bf_shf(IEC958_SUBFRAME_CHANNEL_STATUS)); + val |= FIELD_PREP_CONST(U_SEL, + __bf_shf(IEC958_SUBFRAME_USER_DATA)); + val |= FIELD_PREP_CONST(V_SEL, + __bf_shf(IEC958_SUBFRAME_VALIDITY)); + val |= FIELD_PREP_CONST(D_SEL, + __bf_shf(IEC958_SUBFRAME_SAMPLE_24_MASK)); + val |= FIELD_PREP_CONST(PRE_SEL, + __bf_shf(IEC958_SUBFRAME_PREAMBLE_MASK)); + } else { + /* + * The allowed PCM widths are 24bit and 32bit, as they are supported + * by aud2htx module. + * for 24bit, D_SEL = 0, select all the bits. + * for 32bit, D_SEL = 8, select 24bit in MSB. + */ + val = FIELD_PREP(D_SEL, width - 24); + } + + regmap_write(hdmi_pai->regmap, HTX_PAI_FIELD_CTRL, val); + + /* PAI start running */ + regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, ENABLE); +} + +static void imx8mp_hdmi_pai_disable(struct dw_hdmi *dw_hdmi) +{ + const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi); + struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio; + + /* Stop PAI */ + regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, 0); +} + +static const struct regmap_config imx8mp_hdmi_pai_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = HTX_PAI_FIELD_CTRL, +}; + +static int imx8mp_hdmi_pai_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_hdmi_plat_data *plat_data = data; + struct imx8mp_hdmi_pai *hdmi_pai; + struct resource *res; + void __iomem *base; + + hdmi_pai = devm_kzalloc(dev, sizeof(*hdmi_pai), GFP_KERNEL); + if (!hdmi_pai) + return -ENOMEM; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + + hdmi_pai->regmap = devm_regmap_init_mmio_clk(dev, "apb", base, + &imx8mp_hdmi_pai_regmap_config); + if (IS_ERR(hdmi_pai->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(hdmi_pai->regmap); + } + + plat_data->enable_audio = imx8mp_hdmi_pai_enable; + plat_data->disable_audio = imx8mp_hdmi_pai_disable; + plat_data->priv_audio = hdmi_pai; + + return 0; +} + +static const struct component_ops imx8mp_hdmi_pai_ops = { + .bind = imx8mp_hdmi_pai_bind, +}; + +static int imx8mp_hdmi_pai_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &imx8mp_hdmi_pai_ops); +} + +static void imx8mp_hdmi_pai_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &imx8mp_hdmi_pai_ops); +} + +static const struct of_device_id imx8mp_hdmi_pai_of_table[] = { + { .compatible = "fsl,imx8mp-hdmi-pai" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx8mp_hdmi_pai_of_table); + +static struct platform_driver imx8mp_hdmi_pai_platform_driver = { + .probe = imx8mp_hdmi_pai_probe, + .remove = imx8mp_hdmi_pai_remove, + .driver = { + .name = "imx8mp-hdmi-pai", + .of_match_table = imx8mp_hdmi_pai_of_table, + }, +}; +module_platform_driver(imx8mp_hdmi_pai_platform_driver); + +MODULE_DESCRIPTION("i.MX8MP HDMI PAI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c index 1e7a789ec289..32fd3554e267 100644 --- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c @@ -5,11 +5,13 @@ */ #include <linux/clk.h> +#include <linux/component.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <drm/bridge/dw_hdmi.h> #include <drm/drm_modes.h> +#include <drm/drm_of.h> struct imx8mp_hdmi { struct dw_hdmi_plat_data plat_data; @@ -79,10 +81,45 @@ static const struct dw_hdmi_phy_ops imx8mp_hdmi_phy_ops = { .update_hpd = dw_hdmi_phy_update_hpd, }; +static int imx8mp_dw_hdmi_bind(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct imx8mp_hdmi *hdmi = dev_get_drvdata(dev); + int ret; + + ret = component_bind_all(dev, &hdmi->plat_data); + if (ret) + return dev_err_probe(dev, ret, "component_bind_all failed!\n"); + + hdmi->dw_hdmi = dw_hdmi_probe(pdev, &hdmi->plat_data); + if (IS_ERR(hdmi->dw_hdmi)) { + component_unbind_all(dev, &hdmi->plat_data); + return PTR_ERR(hdmi->dw_hdmi); + } + + return 0; +} + +static void imx8mp_dw_hdmi_unbind(struct device *dev) +{ + struct imx8mp_hdmi *hdmi = dev_get_drvdata(dev); + + dw_hdmi_remove(hdmi->dw_hdmi); + + component_unbind_all(dev, &hdmi->plat_data); +} + +static const struct component_master_ops imx8mp_dw_hdmi_ops = { + .bind = imx8mp_dw_hdmi_bind, + .unbind = imx8mp_dw_hdmi_unbind, +}; + static int imx8mp_dw_hdmi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dw_hdmi_plat_data *plat_data; + struct component_match *match = NULL; + struct device_node *remote; struct imx8mp_hdmi *hdmi; hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); @@ -102,20 +139,38 @@ static int imx8mp_dw_hdmi_probe(struct platform_device *pdev) plat_data->priv_data = hdmi; plat_data->phy_force_vendor = true; - hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data); - if (IS_ERR(hdmi->dw_hdmi)) - return PTR_ERR(hdmi->dw_hdmi); - platform_set_drvdata(pdev, hdmi); + /* port@2 is for hdmi_pai device */ + remote = of_graph_get_remote_node(pdev->dev.of_node, 2, 0); + if (!remote) { + hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data); + if (IS_ERR(hdmi->dw_hdmi)) + return PTR_ERR(hdmi->dw_hdmi); + } else { + drm_of_component_match_add(dev, &match, component_compare_of, remote); + + of_node_put(remote); + + return component_master_add_with_match(dev, &imx8mp_dw_hdmi_ops, match); + } + return 0; } static void imx8mp_dw_hdmi_remove(struct platform_device *pdev) { struct imx8mp_hdmi *hdmi = platform_get_drvdata(pdev); + struct device_node *remote; - dw_hdmi_remove(hdmi->dw_hdmi); + remote = of_graph_get_remote_node(pdev->dev.of_node, 2, 0); + if (remote) { + of_node_put(remote); + + component_master_del(&pdev->dev, &imx8mp_dw_hdmi_ops); + } else { + dw_hdmi_remove(hdmi->dw_hdmi); + } } static int imx8mp_dw_hdmi_pm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c index 5d272916e200..122502968927 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c @@ -683,11 +683,6 @@ static void imx8qxp_ldb_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); } -static int imx8qxp_ldb_runtime_suspend(struct device *dev) -{ - return 0; -} - static int imx8qxp_ldb_runtime_resume(struct device *dev) { struct imx8qxp_ldb *imx8qxp_ldb = dev_get_drvdata(dev); @@ -700,7 +695,7 @@ static int imx8qxp_ldb_runtime_resume(struct device *dev) } static const struct dev_pm_ops imx8qxp_ldb_pm_ops = { - RUNTIME_PM_OPS(imx8qxp_ldb_runtime_suspend, imx8qxp_ldb_runtime_resume, NULL) + RUNTIME_PM_OPS(NULL, imx8qxp_ldb_runtime_resume, NULL) }; static const struct of_device_id imx8qxp_ldb_dt_ids[] = { diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c index ab18f9a3bf23..df7a37eb47f4 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c @@ -90,6 +90,11 @@ static int audio_hw_params(struct device *dev, void *data, params->iec.status[0] & IEC958_AES0_NONAUDIO); dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width); + if (daifmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) + dw_hdmi_set_sample_iec958(dw->data.hdmi, 1); + else + dw_hdmi_set_sample_iec958(dw->data.hdmi, 0); + return 0; } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 206b099a35e9..3b77e73ac0ea 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -177,6 +177,7 @@ struct dw_hdmi { spinlock_t audio_lock; struct mutex audio_mutex; + unsigned int sample_iec958; unsigned int sample_non_pcm; unsigned int sample_width; unsigned int sample_rate; @@ -198,6 +199,12 @@ struct dw_hdmi { enum drm_connector_status last_connector_result; }; +const struct dw_hdmi_plat_data *dw_hdmi_to_plat_data(struct dw_hdmi *hdmi) +{ + return hdmi->plat_data; +} +EXPORT_SYMBOL_GPL(dw_hdmi_to_plat_data); + #define HDMI_IH_PHY_STAT0_RX_SENSE \ (HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \ HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3) @@ -712,6 +719,14 @@ void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm) } EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_non_pcm); +void dw_hdmi_set_sample_iec958(struct dw_hdmi *hdmi, unsigned int iec958) +{ + mutex_lock(&hdmi->audio_mutex); + hdmi->sample_iec958 = iec958; + mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_iec958); + void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate) { mutex_lock(&hdmi->audio_mutex); @@ -843,7 +858,8 @@ static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi) hdmi->channels, hdmi->sample_width, hdmi->sample_rate, - hdmi->sample_non_pcm); + hdmi->sample_non_pcm, + hdmi->sample_iec958); } static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index ae0d08e5e960..276d05d25ad8 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -106,10 +106,21 @@ #define SN_PWM_EN_INV_REG 0xA5 #define SN_PWM_INV_MASK BIT(0) #define SN_PWM_EN_MASK BIT(1) + +#define SN_IRQ_EN_REG 0xE0 +#define IRQ_EN BIT(0) + +#define SN_IRQ_EVENTS_EN_REG 0xE6 +#define HPD_INSERTION_EN BIT(1) +#define HPD_REMOVAL_EN BIT(2) + #define SN_AUX_CMD_STATUS_REG 0xF4 #define AUX_IRQ_STATUS_AUX_RPLY_TOUT BIT(3) #define AUX_IRQ_STATUS_AUX_SHORT BIT(5) #define AUX_IRQ_STATUS_NAT_I2C_FAIL BIT(6) +#define SN_IRQ_STATUS_REG 0xF5 +#define HPD_REMOVAL_STATUS BIT(2) +#define HPD_INSERTION_STATUS BIT(1) #define MIN_DSI_CLK_FREQ_MHZ 40 @@ -152,7 +163,9 @@ * @ln_assign: Value to program to the LN_ASSIGN register. * @ln_polrs: Value for the 4-bit LN_POLRS field of SN_ENH_FRAME_REG. * @comms_enabled: If true then communication over the aux channel is enabled. + * @hpd_enabled: If true then HPD events are enabled. * @comms_mutex: Protects modification of comms_enabled. + * @hpd_mutex: Protects modification of hpd_enabled. * * @gchip: If we expose our GPIOs, this is used. * @gchip_output: A cache of whether we've set GPIOs to output. This @@ -190,7 +203,9 @@ struct ti_sn65dsi86 { u8 ln_assign; u8 ln_polrs; bool comms_enabled; + bool hpd_enabled; struct mutex comms_mutex; + struct mutex hpd_mutex; #if defined(CONFIG_OF_GPIO) struct gpio_chip gchip; @@ -221,6 +236,23 @@ static const struct regmap_config ti_sn65dsi86_regmap_config = { .max_register = 0xFF, }; +static int ti_sn65dsi86_read_u8(struct ti_sn65dsi86 *pdata, unsigned int reg, + u8 *val) +{ + int ret; + unsigned int reg_val; + + ret = regmap_read(pdata->regmap, reg, ®_val); + if (ret) { + dev_err(pdata->dev, "fail to read raw reg %#x: %d\n", + reg, ret); + return ret; + } + *val = (u8)reg_val; + + return 0; +} + static int __maybe_unused ti_sn65dsi86_read_u16(struct ti_sn65dsi86 *pdata, unsigned int reg, u16 *val) { @@ -379,6 +411,7 @@ static void ti_sn65dsi86_disable_comms(struct ti_sn65dsi86 *pdata) static int __maybe_unused ti_sn65dsi86_resume(struct device *dev) { struct ti_sn65dsi86 *pdata = dev_get_drvdata(dev); + const struct i2c_client *client = to_i2c_client(pdata->dev); int ret; ret = regulator_bulk_enable(SN_REGULATOR_SUPPLY_NUM, pdata->supplies); @@ -413,6 +446,13 @@ static int __maybe_unused ti_sn65dsi86_resume(struct device *dev) if (pdata->refclk) ti_sn65dsi86_enable_comms(pdata, NULL); + if (client->irq) { + ret = regmap_update_bits(pdata->regmap, SN_IRQ_EN_REG, IRQ_EN, + IRQ_EN); + if (ret) + dev_err(pdata->dev, "Failed to enable IRQ events: %d\n", ret); + } + return ret; } @@ -1211,6 +1251,8 @@ static void ti_sn65dsi86_debugfs_init(struct drm_bridge *bridge, struct dentry * static void ti_sn_bridge_hpd_enable(struct drm_bridge *bridge) { struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); + const struct i2c_client *client = to_i2c_client(pdata->dev); + int ret; /* * Device needs to be powered on before reading the HPD state @@ -1219,11 +1261,35 @@ static void ti_sn_bridge_hpd_enable(struct drm_bridge *bridge) */ pm_runtime_get_sync(pdata->dev); + + mutex_lock(&pdata->hpd_mutex); + pdata->hpd_enabled = true; + mutex_unlock(&pdata->hpd_mutex); + + if (client->irq) { + ret = regmap_set_bits(pdata->regmap, SN_IRQ_EVENTS_EN_REG, + HPD_REMOVAL_EN | HPD_INSERTION_EN); + if (ret) + dev_err(pdata->dev, "Failed to enable HPD events: %d\n", ret); + } } static void ti_sn_bridge_hpd_disable(struct drm_bridge *bridge) { struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); + const struct i2c_client *client = to_i2c_client(pdata->dev); + int ret; + + if (client->irq) { + ret = regmap_clear_bits(pdata->regmap, SN_IRQ_EVENTS_EN_REG, + HPD_REMOVAL_EN | HPD_INSERTION_EN); + if (ret) + dev_err(pdata->dev, "Failed to disable HPD events: %d\n", ret); + } + + mutex_lock(&pdata->hpd_mutex); + pdata->hpd_enabled = false; + mutex_unlock(&pdata->hpd_mutex); pm_runtime_put_autosuspend(pdata->dev); } @@ -1309,6 +1375,41 @@ static int ti_sn_bridge_parse_dsi_host(struct ti_sn65dsi86 *pdata) return 0; } +static irqreturn_t ti_sn_bridge_interrupt(int irq, void *private) +{ + struct ti_sn65dsi86 *pdata = private; + struct drm_device *dev = pdata->bridge.dev; + u8 status; + int ret; + bool hpd_event; + + ret = ti_sn65dsi86_read_u8(pdata, SN_IRQ_STATUS_REG, &status); + if (ret) { + dev_err(pdata->dev, "Failed to read IRQ status: %d\n", ret); + return IRQ_NONE; + } + + hpd_event = status & (HPD_REMOVAL_STATUS | HPD_INSERTION_STATUS); + + dev_dbg(pdata->dev, "(SN_IRQ_STATUS_REG = %#x)\n", status); + if (!status) + return IRQ_NONE; + + ret = regmap_write(pdata->regmap, SN_IRQ_STATUS_REG, status); + if (ret) { + dev_err(pdata->dev, "Failed to clear IRQ status: %d\n", ret); + return IRQ_NONE; + } + + /* Only send the HPD event if we are bound with a device. */ + mutex_lock(&pdata->hpd_mutex); + if (pdata->hpd_enabled && hpd_event) + drm_kms_helper_hotplug_event(dev); + mutex_unlock(&pdata->hpd_mutex); + + return IRQ_HANDLED; +} + static int ti_sn_bridge_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { @@ -1931,6 +2032,7 @@ static int ti_sn65dsi86_probe(struct i2c_client *client) dev_set_drvdata(dev, pdata); pdata->dev = dev; + mutex_init(&pdata->hpd_mutex); mutex_init(&pdata->comms_mutex); pdata->regmap = devm_regmap_init_i2c(client, @@ -1971,6 +2073,16 @@ static int ti_sn65dsi86_probe(struct i2c_client *client) if (strncmp(id_buf, "68ISD ", ARRAY_SIZE(id_buf))) return dev_err_probe(dev, -EOPNOTSUPP, "unsupported device id\n"); + if (client->irq) { + ret = devm_request_threaded_irq(pdata->dev, client->irq, NULL, + ti_sn_bridge_interrupt, + IRQF_ONESHOT, + dev_name(pdata->dev), pdata); + + if (ret) + return dev_err_probe(dev, ret, "failed to request interrupt\n"); + } + /* * Break ourselves up into a collection of aux devices. The only real * motiviation here is to solve the chicken-and-egg problem of probe diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index baacd21e7341..a5bdd6c10643 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -137,10 +137,9 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector, { struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector); - struct drm_bridge *bridge; /* Notify all bridges in the pipeline of hotplug events. */ - drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) { + drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) { if (bridge->funcs->hpd_notify) bridge->funcs->hpd_notify(bridge, status); } @@ -639,7 +638,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, struct drm_bridge_connector *bridge_connector; struct drm_connector *connector; struct i2c_adapter *ddc = NULL; - struct drm_bridge *bridge, *panel_bridge = NULL; + struct drm_bridge *panel_bridge = NULL; unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB); unsigned int max_bpc = 8; bool support_hdcp = false; @@ -667,7 +666,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, * detection are available, we don't support hotplug detection at all. */ connector_type = DRM_MODE_CONNECTOR_Unknown; - drm_for_each_bridge_in_chain(encoder, bridge) { + drm_for_each_bridge_in_chain_scoped(encoder, bridge) { if (!bridge->interlace_allowed) connector->interlace_allowed = false; if (!bridge->ycbcr_420_allowed) @@ -818,7 +817,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, if (bridge_connector->bridge_hdmi_cec && bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) { - bridge = bridge_connector->bridge_hdmi_cec; + struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec; ret = drmm_connector_hdmi_cec_notifier_register(connector, NULL, @@ -829,7 +828,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, if (bridge_connector->bridge_hdmi_cec && bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) { - bridge = bridge_connector->bridge_hdmi_cec; + struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec; ret = drmm_connector_hdmi_cec_register(connector, &drm_bridge_connector_hdmi_cec_funcs, diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 4aaeae4fa03c..49803528023b 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -29,6 +29,7 @@ #include <linux/init.h> #include <linux/iopoll.h> #include <linux/kernel.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/seq_file.h> @@ -123,6 +124,14 @@ bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE], } EXPORT_SYMBOL(drm_dp_clock_recovery_ok); +bool drm_dp_post_lt_adj_req_in_progress(const u8 link_status[DP_LINK_STATUS_SIZE]) +{ + u8 lane_align = dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED); + + return lane_align & DP_POST_LT_ADJ_REQ_IN_PROGRESS; +} +EXPORT_SYMBOL(drm_dp_post_lt_adj_req_in_progress); + u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE], int lane) { @@ -4128,22 +4137,61 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf { int fxp, fxp_min, fxp_max, fxp_actual, f = 1; int ret; - u8 pn, pn_min, pn_max; + u8 pn, pn_min, pn_max, bit_count; if (!bl->aux_set) return 0; - ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn); + ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &bit_count); if (ret < 0) { drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n", aux->name, ret); return -ENODEV; } - pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + bit_count &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + + ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); + if (ret < 0) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n", + aux->name, ret); + return -ENODEV; + } + pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + + ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); + if (ret < 0) { + drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n", + aux->name, ret); + return -ENODEV; + } + pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; + + if (unlikely(pn_min > pn_max)) { + drm_dbg_kms(aux->drm_dev, "%s: Invalid pwmgen bit count cap min/max returned: %d %d\n", + aux->name, pn_min, pn_max); + return -EINVAL; + } + + /* + * Per VESA eDP Spec v1.4b, section 3.3.10.2: + * If DP_EDP_PWMGEN_BIT_COUNT is less than DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, + * the sink must use the MIN value as the effective PWM bit count. + * Clamp the reported value to the [MIN, MAX] capability range to ensure + * correct brightness scaling on compliant eDP panels. + * Only enable this logic if the [MIN, MAX] range is valid in regard to Spec. + */ + pn = bit_count; + if (bit_count < pn_min) + pn = clamp(bit_count, pn_min, pn_max); + bl->max = (1 << pn) - 1; - if (!driver_pwm_freq_hz) + if (!driver_pwm_freq_hz) { + if (pn != bit_count) + goto bit_count_write_back; + return 0; + } /* * Set PWM Frequency divider to match desired frequency provided by the driver. @@ -4167,21 +4215,6 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf * - FxP is within 25% of desired value. * Note: 25% is arbitrary value and may need some tweak. */ - ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min); - if (ret < 0) { - drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n", - aux->name, ret); - return 0; - } - ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max); - if (ret < 0) { - drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n", - aux->name, ret); - return 0; - } - pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK; - /* Ensure frequency is within 25% of desired value */ fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4); fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4); @@ -4199,12 +4232,17 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf break; } +bit_count_write_back: ret = drm_dp_dpcd_write_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, pn); if (ret < 0) { drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n", aux->name, ret); return 0; } + + if (!driver_pwm_freq_hz) + return 0; + bl->pwmgen_bit_count = pn; bl->max = (1 << pn) - 1; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index cd15cf52f0c9..ed5359a71f7e 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1308,7 +1308,6 @@ drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, struct drm_encoder *encoder) { struct drm_bridge_state *bridge_state; - struct drm_bridge *bridge; if (!encoder) return 0; @@ -1317,7 +1316,7 @@ drm_atomic_add_encoder_bridges(struct drm_atomic_state *state, "Adding all bridges for [encoder:%d:%s] to %p\n", encoder->base.id, encoder->name, state); - drm_for_each_bridge_in_chain(encoder, bridge) { + drm_for_each_bridge_in_chain_scoped(encoder, bridge) { /* Skip bridges that don't implement the atomic state hooks. */ if (!bridge->funcs->atomic_duplicate_state) continue; diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index d031447eebc9..630b5e6594e0 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -197,15 +197,22 @@ * driver. */ +/* Protect bridge_list and bridge_lingering_list */ static DEFINE_MUTEX(bridge_lock); static LIST_HEAD(bridge_list); +static LIST_HEAD(bridge_lingering_list); static void __drm_bridge_free(struct kref *kref) { struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount); + mutex_lock(&bridge_lock); + list_del(&bridge->list); + mutex_unlock(&bridge_lock); + if (bridge->funcs->destroy) bridge->funcs->destroy(bridge); + kfree(bridge->container); } @@ -273,6 +280,7 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset, return ERR_PTR(-ENOMEM); bridge = container + offset; + INIT_LIST_HEAD(&bridge->list); bridge->container = container; bridge->funcs = funcs; kref_init(&bridge->refcount); @@ -286,10 +294,13 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset, EXPORT_SYMBOL(__devm_drm_bridge_alloc); /** - * drm_bridge_add - add the given bridge to the global bridge list + * drm_bridge_add - register a bridge * * @bridge: bridge control structure * + * Add the given bridge to the global list of bridges, where they can be + * found by users via of_drm_find_bridge(). + * * The bridge to be added must have been allocated by * devm_drm_bridge_alloc(). */ @@ -300,6 +311,14 @@ void drm_bridge_add(struct drm_bridge *bridge) drm_bridge_get(bridge); + /* + * If the bridge was previously added and then removed, it is now + * in bridge_lingering_list. Remove it or bridge_lingering_list will be + * corrupted when adding this bridge to bridge_list below. + */ + if (!list_empty(&bridge->list)) + list_del_init(&bridge->list); + mutex_init(&bridge->hpd_mutex); if (bridge->ops & DRM_BRIDGE_OP_HDMI) @@ -336,14 +355,19 @@ int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge) EXPORT_SYMBOL(devm_drm_bridge_add); /** - * drm_bridge_remove - remove the given bridge from the global bridge list + * drm_bridge_remove - unregister a bridge * * @bridge: bridge control structure + * + * Remove the given bridge from the global list of registered bridges, so + * it won't be found by users via of_drm_find_bridge(), and add it to the + * lingering bridge list, to keep track of it until its allocated memory is + * eventually freed. */ void drm_bridge_remove(struct drm_bridge *bridge) { mutex_lock(&bridge_lock); - list_del_init(&bridge->list); + list_move_tail(&bridge->list, &bridge_lingering_list); mutex_unlock(&bridge_lock); mutex_destroy(&bridge->hpd_mutex); @@ -1121,7 +1145,6 @@ drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge, struct drm_atomic_state *state) { struct drm_bridge_state *bridge_state, *next_bridge_state; - struct drm_bridge *next_bridge; u32 output_flags = 0; bridge_state = drm_atomic_get_new_bridge_state(state, bridge); @@ -1130,7 +1153,7 @@ drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge, if (!bridge_state) return; - next_bridge = drm_bridge_get_next_bridge(bridge); + struct drm_bridge *next_bridge __free(drm_bridge_put) = drm_bridge_get_next_bridge(bridge); /* * Let's try to apply the most common case here, that is, propagate @@ -1432,17 +1455,20 @@ EXPORT_SYMBOL(devm_drm_put_bridge); static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, struct drm_bridge *bridge, - unsigned int idx) + unsigned int idx, + bool lingering) { drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs); - drm_printf(p, "\trefcount: %u\n", kref_read(&bridge->refcount)); + drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount), + lingering ? " [lingering]" : ""); drm_printf(p, "\ttype: [%d] %s\n", bridge->type, drm_get_connector_type_name(bridge->type)); - if (bridge->of_node) + /* The OF node could be freed after drm_bridge_remove() */ + if (bridge->of_node && !lingering) drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node); drm_printf(p, "\tops: [0x%x]", bridge->ops); @@ -1468,7 +1494,10 @@ static int allbridges_show(struct seq_file *m, void *data) mutex_lock(&bridge_lock); list_for_each_entry(bridge, &bridge_list, list) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++); + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); + + list_for_each_entry(bridge, &bridge_lingering_list, list) + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true); mutex_unlock(&bridge_lock); @@ -1480,11 +1509,10 @@ static int encoder_bridges_show(struct seq_file *m, void *data) { struct drm_encoder *encoder = m->private; struct drm_printer p = drm_seq_file_printer(m); - struct drm_bridge *bridge; unsigned int idx = 0; - drm_for_each_bridge_in_chain(encoder, bridge) - drm_bridge_debugfs_show_bridge(&p, bridge, idx++); + drm_for_each_bridge_in_chain_scoped(encoder, bridge) + drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false); return 0; } diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 9c2c3b0c8c47..fc4caf7da5fc 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -1293,6 +1293,50 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode) } EXPORT_SYMBOL(drm_client_modeset_dpms); +/** + * drm_client_modeset_wait_for_vblank() - Wait for the next VBLANK to occur + * @client: DRM client + * @crtc_index: The ndex of the CRTC to wait on + * + * Block the caller until the given CRTC has seen a VBLANK. Do nothing + * if the CRTC is disabled. If there's another DRM master present, fail + * with -EBUSY. + * + * Returns: + * 0 on success, or negative error code otherwise. + */ +int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned int crtc_index) +{ + struct drm_device *dev = client->dev; + struct drm_crtc *crtc; + int ret; + + /* + * Rate-limit update frequency to vblank. If there's a DRM master + * present, it could interfere while we're waiting for the vblank + * event. Don't wait in this case. + */ + if (!drm_master_internal_acquire(dev)) + return -EBUSY; + + crtc = client->modesets[crtc_index].crtc; + + /* + * Only wait for a vblank event if the CRTC is enabled, otherwise + * just don't do anything, not even report an error. + */ + ret = drm_crtc_vblank_get(crtc); + if (!ret) { + drm_crtc_wait_one_vblank(crtc); + drm_crtc_vblank_put(crtc); + } + + drm_master_internal_release(dev); + + return 0; +} +EXPORT_SYMBOL(drm_client_modeset_wait_for_vblank); + #ifdef CONFIG_DRM_KUNIT_TEST #include "tests/drm_client_modeset_test.c" #endif diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index 70032bba1c97..e9eed9a5b760 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -25,6 +25,8 @@ #include <drm/drm_device.h> #include <drm/drm_drv.h> +#include <drm/drm_dumb_buffers.h> +#include <drm/drm_fourcc.h> #include <drm/drm_gem.h> #include <drm/drm_mode.h> @@ -57,6 +59,134 @@ * a hardware-specific ioctl to allocate suitable buffer objects. */ +static int drm_mode_align_dumb(struct drm_mode_create_dumb *args, + unsigned long hw_pitch_align, + unsigned long hw_size_align) +{ + u32 pitch = args->pitch; + u32 size; + + if (!pitch) + return -EINVAL; + + if (hw_pitch_align) + pitch = roundup(pitch, hw_pitch_align); + + if (!hw_size_align) + hw_size_align = PAGE_SIZE; + else if (!IS_ALIGNED(hw_size_align, PAGE_SIZE)) + return -EINVAL; /* TODO: handle this if necessary */ + + if (check_mul_overflow(args->height, pitch, &size)) + return -EINVAL; + size = ALIGN(size, hw_size_align); + if (!size) + return -EINVAL; + + args->pitch = pitch; + args->size = size; + + return 0; +} + +/** + * drm_mode_size_dumb - Calculates the scanline and buffer sizes for dumb buffers + * @dev: DRM device + * @args: Parameters for the dumb buffer + * @hw_pitch_align: Hardware scanline alignment in bytes + * @hw_size_align: Hardware buffer-size alignment in bytes + * + * The helper drm_mode_size_dumb() calculates the size of the buffer + * allocation and the scanline size for a dumb buffer. Callers have to + * set the buffers width, height and color mode in the argument @arg. + * The helper validates the correctness of the input and tests for + * possible overflows. If successful, it returns the dumb buffer's + * required scanline pitch and size in &args. + * + * The parameter @hw_pitch_align allows the driver to specifies an + * alignment for the scanline pitch, if the hardware requires any. The + * calculated pitch will be a multiple of the alignment. The parameter + * @hw_size_align allows to specify an alignment for buffer sizes. The + * provided alignment should represent requirements of the graphics + * hardware. drm_mode_size_dumb() handles GEM-related constraints + * automatically across all drivers and hardware. For example, the + * returned buffer size is always a multiple of PAGE_SIZE, which is + * required by mmap(). + * + * Returns: + * Zero on success, or a negative error code otherwise. + */ +int drm_mode_size_dumb(struct drm_device *dev, + struct drm_mode_create_dumb *args, + unsigned long hw_pitch_align, + unsigned long hw_size_align) +{ + u64 pitch = 0; + u32 fourcc; + + /* + * The scanline pitch depends on the buffer width and the color + * format. The latter is specified as a color-mode constant for + * which we first have to find the corresponding color format. + * + * Different color formats can have the same color-mode constant. + * For example XRGB8888 and BGRX8888 both have a color mode of 32. + * It is possible to use different formats for dumb-buffer allocation + * and rendering as long as all involved formats share the same + * color-mode constant. + */ + fourcc = drm_driver_color_mode_format(dev, args->bpp); + if (fourcc != DRM_FORMAT_INVALID) { + const struct drm_format_info *info = drm_format_info(fourcc); + + if (!info) + return -EINVAL; + pitch = drm_format_info_min_pitch(info, 0, args->width); + } else if (args->bpp) { + /* + * Some userspace throws in arbitrary values for bpp and + * relies on the kernel to figure it out. In this case we + * fall back to the old method of using bpp directly. The + * over-commitment of memory from the rounding is acceptable + * for compatibility with legacy userspace. We have a number + * of deprecated legacy values that are explicitly supported. + */ + switch (args->bpp) { + default: + drm_warn_once(dev, + "Unknown color mode %u; guessing buffer size.\n", + args->bpp); + fallthrough; + /* + * These constants represent various YUV formats supported by + * drm_gem_afbc_get_bpp(). + */ + case 12: // DRM_FORMAT_YUV420_8BIT + case 15: // DRM_FORMAT_YUV420_10BIT + case 30: // DRM_FORMAT_VUY101010 + fallthrough; + /* + * Used by Mesa and Gstreamer to allocate NV formats and others + * as RGB buffers. Technically, XRGB16161616F formats are RGB, + * but the dumb buffers are not supposed to be used for anything + * beyond 32 bits per pixels. + */ + case 10: // DRM_FORMAT_NV{15,20,30}, DRM_FORMAT_P010 + case 64: // DRM_FORMAT_{XRGB,XBGR,ARGB,ABGR}16161616F + pitch = args->width * DIV_ROUND_UP(args->bpp, SZ_8); + break; + } + } + + if (!pitch || pitch > U32_MAX) + return -EINVAL; + + args->pitch = pitch; + + return drm_mode_align_dumb(args, hw_pitch_align, hw_size_align); +} +EXPORT_SYMBOL(drm_mode_size_dumb); + int drm_mode_create_dumb(struct drm_device *dev, struct drm_mode_create_dumb *args, struct drm_file *file_priv) @@ -99,7 +229,30 @@ int drm_mode_create_dumb(struct drm_device *dev, int drm_mode_create_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - return drm_mode_create_dumb(dev, data, file_priv); + struct drm_mode_create_dumb *args = data; + int err; + + err = drm_mode_create_dumb(dev, args, file_priv); + if (err) { + args->handle = 0; + args->pitch = 0; + args->size = 0; + } + return err; +} + +static int drm_mode_mmap_dumb(struct drm_device *dev, struct drm_mode_map_dumb *args, + struct drm_file *file_priv) +{ + if (!dev->driver->dumb_create) + return -ENOSYS; + + if (dev->driver->dumb_map_offset) + return dev->driver->dumb_map_offset(file_priv, dev, args->handle, + &args->offset); + else + return drm_gem_dumb_map_offset(file_priv, dev, args->handle, + &args->offset); } /** @@ -120,17 +273,12 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_map_dumb *args = data; + int err; - if (!dev->driver->dumb_create) - return -ENOSYS; - - if (dev->driver->dumb_map_offset) - return dev->driver->dumb_map_offset(file_priv, dev, - args->handle, - &args->offset); - else - return drm_gem_dumb_map_offset(file_priv, dev, args->handle, - &args->offset); + err = drm_mode_mmap_dumb(dev, args, file_priv); + if (err) + args->offset = 0; + return err; } int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 11a5b60cb9ce..53e9dc0543de 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -368,6 +368,10 @@ static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper) unsigned long flags; int ret; + mutex_lock(&helper->lock); + drm_client_modeset_wait_for_vblank(&helper->client, 0); + mutex_unlock(&helper->lock); + if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty)) return; @@ -1068,15 +1072,9 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; - struct drm_crtc *crtc; int ret = 0; - mutex_lock(&fb_helper->lock); - if (!drm_master_internal_acquire(dev)) { - ret = -EBUSY; - goto unlock; - } + guard(mutex)(&fb_helper->lock); switch (cmd) { case FBIO_WAITFORVSYNC: @@ -1096,28 +1094,12 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, * make. If we're not smart enough here, one should * just consider switch the userspace to KMS. */ - crtc = fb_helper->client.modesets[0].crtc; - - /* - * Only wait for a vblank event if the CRTC is - * enabled, otherwise just don't do anythintg, - * not even report an error. - */ - ret = drm_crtc_vblank_get(crtc); - if (!ret) { - drm_crtc_wait_one_vblank(crtc); - drm_crtc_vblank_put(crtc); - } - - ret = 0; + ret = drm_client_modeset_wait_for_vblank(&fb_helper->client, 0); break; default: ret = -ENOTTY; } - drm_master_internal_release(dev); -unlock: - mutex_unlock(&fb_helper->lock); return ret; } EXPORT_SYMBOL(drm_fb_helper_ioctl); diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 006836554cc2..6cddf05c493b 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -1165,97 +1165,6 @@ void drm_fb_argb8888_to_argb4444(struct iosys_map *dst, const unsigned int *dst_ } EXPORT_SYMBOL(drm_fb_argb8888_to_argb4444); -/** - * drm_fb_blit - Copy parts of a framebuffer to display memory - * @dst: Array of display-memory addresses to copy to - * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines - * within @dst; can be NULL if scanlines are stored next to each other. - * @dst_format: FOURCC code of the display's color format - * @src: The framebuffer memory to copy from - * @fb: The framebuffer to copy from - * @clip: Clip rectangle area to copy - * @state: Transform and conversion state - * - * This function copies parts of a framebuffer to display memory. If the - * formats of the display and the framebuffer mismatch, the blit function - * will attempt to convert between them during the process. The parameters @dst, - * @dst_pitch and @src refer to arrays. Each array must have at least as many - * entries as there are planes in @dst_format's format. Each entry stores the - * value for the format's respective color plane at the same index. - * - * This function does not apply clipping on @dst (i.e. the destination is at the - * top-left corner). - * - * Returns: - * 0 on success, or - * -EINVAL if the color-format conversion failed, or - * a negative error code otherwise. - */ -int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t dst_format, - const struct iosys_map *src, const struct drm_framebuffer *fb, - const struct drm_rect *clip, struct drm_format_conv_state *state) -{ - uint32_t fb_format = fb->format->format; - - if (fb_format == dst_format) { - drm_fb_memcpy(dst, dst_pitch, src, fb, clip); - return 0; - } else if (fb_format == (dst_format | DRM_FORMAT_BIG_ENDIAN)) { - drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state); - return 0; - } else if (fb_format == (dst_format & ~DRM_FORMAT_BIG_ENDIAN)) { - drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state); - return 0; - } else if (fb_format == DRM_FORMAT_XRGB8888) { - if (dst_format == DRM_FORMAT_RGB565) { - drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_XRGB1555) { - drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_ARGB1555) { - drm_fb_xrgb8888_to_argb1555(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_RGBA5551) { - drm_fb_xrgb8888_to_rgba5551(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_RGB888) { - drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_BGR888) { - drm_fb_xrgb8888_to_bgr888(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_ARGB8888) { - drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_XBGR8888) { - drm_fb_xrgb8888_to_xbgr8888(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_ABGR8888) { - drm_fb_xrgb8888_to_abgr8888(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_XRGB2101010) { - drm_fb_xrgb8888_to_xrgb2101010(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_ARGB2101010) { - drm_fb_xrgb8888_to_argb2101010(dst, dst_pitch, src, fb, clip, state); - return 0; - } else if (dst_format == DRM_FORMAT_BGRX8888) { - drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state); - return 0; - } else if (dst_format == DRM_FORMAT_RGB332) { - drm_fb_xrgb8888_to_rgb332(dst, dst_pitch, src, fb, clip, state); - return 0; - } - } - - drm_warn_once(fb->dev, "No conversion helper from %p4cc to %p4cc found.\n", - &fb_format, &dst_format); - - return -EINVAL; -} -EXPORT_SYMBOL(drm_fb_blit); - static void drm_fb_gray8_to_gray2_line(void *dbuf, const void *sbuf, unsigned int pixels) { u8 *dbuf8 = dbuf; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index f884d155a832..a1a9c828938b 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -101,10 +101,8 @@ drm_gem_init(struct drm_device *dev) vma_offset_manager = drmm_kzalloc(dev, sizeof(*vma_offset_manager), GFP_KERNEL); - if (!vma_offset_manager) { - DRM_ERROR("out of memory\n"); + if (!vma_offset_manager) return -ENOMEM; - } dev->vma_offset_manager = vma_offset_manager; drm_vma_offset_manager_init(vma_offset_manager, @@ -785,9 +783,10 @@ static int objects_lookup(struct drm_file *filp, u32 *handle, int count, int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles, int count, struct drm_gem_object ***objs_out) { - int ret; - u32 *handles; + struct drm_device *dev = filp->minor->dev; struct drm_gem_object **objs; + u32 *handles; + int ret; if (!count) return 0; @@ -807,7 +806,7 @@ int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles, if (copy_from_user(handles, bo_handles, count * sizeof(u32))) { ret = -EFAULT; - DRM_DEBUG("Failed to copy in GEM handles\n"); + drm_dbg_core(dev, "Failed to copy in GEM handles\n"); goto out; } @@ -855,12 +854,13 @@ EXPORT_SYMBOL(drm_gem_object_lookup); long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle, bool wait_all, unsigned long timeout) { - long ret; + struct drm_device *dev = filep->minor->dev; struct drm_gem_object *obj; + long ret; obj = drm_gem_object_lookup(filep, handle); if (!obj) { - DRM_DEBUG("Failed to look up GEM BO %d\n", handle); + drm_dbg_core(dev, "Failed to look up GEM BO %d\n", handle); return -EINVAL; } diff --git a/drivers/gpu/drm/drm_gem_dma_helper.c b/drivers/gpu/drm/drm_gem_dma_helper.c index 4f0320df858f..9c9bfc9e85c6 100644 --- a/drivers/gpu/drm/drm_gem_dma_helper.c +++ b/drivers/gpu/drm/drm_gem_dma_helper.c @@ -20,6 +20,7 @@ #include <drm/drm.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_vma_manager.h> @@ -304,9 +305,11 @@ int drm_gem_dma_dumb_create(struct drm_file *file_priv, struct drm_mode_create_dumb *args) { struct drm_gem_dma_object *dma_obj; + int ret; - args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - args->size = args->pitch * args->height; + ret = drm_mode_size_dumb(drm, args, SZ_8, 0); + if (ret) + return ret; dma_obj = drm_gem_dma_create_with_handle(file_priv, drm, args->size, &args->handle); @@ -582,7 +585,7 @@ drm_gem_dma_prime_import_sg_table_vmap(struct drm_device *dev, ret = dma_buf_vmap_unlocked(attach->dmabuf, &map); if (ret) { - DRM_ERROR("Failed to vmap PRIME buffer\n"); + drm_err(dev, "Failed to vmap PRIME buffer\n"); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 5d1349c34afd..dc94a27710e5 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -18,6 +18,7 @@ #include <drm/drm.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_gem_shmem_helper.h> #include <drm/drm_prime.h> #include <drm/drm_print.h> @@ -48,28 +49,12 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = { .vm_ops = &drm_gem_shmem_vm_ops, }; -static struct drm_gem_shmem_object * -__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, - struct vfsmount *gemfs) +static int __drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, + size_t size, bool private, struct vfsmount *gemfs) { - struct drm_gem_shmem_object *shmem; - struct drm_gem_object *obj; + struct drm_gem_object *obj = &shmem->base; int ret = 0; - size = PAGE_ALIGN(size); - - if (dev->driver->gem_create_object) { - obj = dev->driver->gem_create_object(dev, size); - if (IS_ERR(obj)) - return ERR_CAST(obj); - shmem = to_drm_gem_shmem_obj(obj); - } else { - shmem = kzalloc(sizeof(*shmem), GFP_KERNEL); - if (!shmem) - return ERR_PTR(-ENOMEM); - obj = &shmem->base; - } - if (!obj->funcs) obj->funcs = &drm_gem_shmem_funcs; @@ -81,7 +66,7 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, } if (ret) { drm_gem_private_object_fini(obj); - goto err_free; + return ret; } ret = drm_gem_create_mmap_offset(obj); @@ -102,14 +87,55 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, __GFP_RETRY_MAYFAIL | __GFP_NOWARN); } - return shmem; - + return 0; err_release: drm_gem_object_release(obj); -err_free: - kfree(obj); + return ret; +} - return ERR_PTR(ret); +/** + * drm_gem_shmem_init - Initialize an allocated object. + * @dev: DRM device + * @obj: The allocated shmem GEM object. + * + * Returns: + * 0 on success, or a negative error code on failure. + */ +int drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, size_t size) +{ + return __drm_gem_shmem_init(dev, shmem, size, false, NULL); +} +EXPORT_SYMBOL_GPL(drm_gem_shmem_init); + +static struct drm_gem_shmem_object * +__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, + struct vfsmount *gemfs) +{ + struct drm_gem_shmem_object *shmem; + struct drm_gem_object *obj; + int ret = 0; + + size = PAGE_ALIGN(size); + + if (dev->driver->gem_create_object) { + obj = dev->driver->gem_create_object(dev, size); + if (IS_ERR(obj)) + return ERR_CAST(obj); + shmem = to_drm_gem_shmem_obj(obj); + } else { + shmem = kzalloc(sizeof(*shmem), GFP_KERNEL); + if (!shmem) + return ERR_PTR(-ENOMEM); + obj = &shmem->base; + } + + ret = __drm_gem_shmem_init(dev, shmem, size, private, gemfs); + if (ret) { + kfree(obj); + return ERR_PTR(ret); + } + + return shmem; } /** * drm_gem_shmem_create - Allocate an object with the given size @@ -150,13 +176,13 @@ struct drm_gem_shmem_object *drm_gem_shmem_create_with_mnt(struct drm_device *de EXPORT_SYMBOL_GPL(drm_gem_shmem_create_with_mnt); /** - * drm_gem_shmem_free - Free resources associated with a shmem GEM object - * @shmem: shmem GEM object to free + * drm_gem_shmem_release - Release resources associated with a shmem GEM object. + * @shmem: shmem GEM object * - * This function cleans up the GEM object state and frees the memory used to - * store the object itself. + * This function cleans up the GEM object state, but does not free the memory used to store the + * object itself. This function is meant to be a dedicated helper for the Rust GEM bindings. */ -void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem) +void drm_gem_shmem_release(struct drm_gem_shmem_object *shmem) { struct drm_gem_object *obj = &shmem->base; @@ -183,6 +209,19 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem) } drm_gem_object_release(obj); +} +EXPORT_SYMBOL_GPL(drm_gem_shmem_release); + +/** + * drm_gem_shmem_free - Free resources associated with a shmem GEM object + * @shmem: shmem 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(struct drm_gem_shmem_object *shmem) +{ + drm_gem_shmem_release(shmem); kfree(shmem); } EXPORT_SYMBOL_GPL(drm_gem_shmem_free); @@ -518,18 +557,11 @@ EXPORT_SYMBOL_GPL(drm_gem_shmem_purge_locked); 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); + int ret; - if (!args->pitch || !args->size) { - args->pitch = min_pitch; - args->size = PAGE_ALIGN(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 = PAGE_ALIGN(args->pitch * args->height); - } + ret = drm_mode_size_dumb(dev, args, SZ_8, 0); + if (ret) + return ret; return drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle); } diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index b04cde4a60e7..90760d0ca071 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -107,7 +107,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; static void drm_gem_vram_cleanup(struct drm_gem_vram_object *gbo) { - /* We got here via ttm_bo_put(), which means that the + /* We got here via ttm_bo_fini(), which means that the * TTM buffer object in 'bo' has already been cleaned * up; only release the GEM object. */ @@ -234,11 +234,11 @@ EXPORT_SYMBOL(drm_gem_vram_create); * drm_gem_vram_put() - Releases a reference to a VRAM-backed GEM object * @gbo: the GEM VRAM object * - * See ttm_bo_put() for more information. + * See ttm_bo_fini() for more information. */ void drm_gem_vram_put(struct drm_gem_vram_object *gbo) { - ttm_bo_put(&gbo->bo); + ttm_bo_fini(&gbo->bo); } EXPORT_SYMBOL(drm_gem_vram_put); diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 46f59883183d..61e211fd3c9c 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -136,8 +136,17 @@ * vblanks after a timer has expired, which can be configured through the * ``vblankoffdelay`` module parameter. * - * Drivers for hardware without support for vertical-blanking interrupts - * must not call drm_vblank_init(). For such drivers, atomic helpers will + * Drivers for hardware without support for vertical-blanking interrupts can + * use DRM vblank timers to send vblank events at the rate of the current + * display mode's refresh. While not synchronized to the hardware's + * vertical-blanking regions, the timer helps DRM clients and compositors to + * adapt their update cycle to the display output. Drivers should set up + * vblanking as usual, but call drm_crtc_vblank_start_timer() and + * drm_crtc_vblank_cancel_timer() as part of their atomic mode setting. + * See also DRM vblank helpers for more information. + * + * Drivers without support for vertical-blanking interrupts nor timers must + * not call drm_vblank_init(). For these drivers, atomic helpers will * automatically generate fake vblank events as part of the display update. * This functionality also can be controlled by the driver by enabling and * disabling struct drm_crtc_state.no_vblank. @@ -508,6 +517,9 @@ static void drm_vblank_init_release(struct drm_device *dev, void *ptr) drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && drm_core_check_feature(dev, DRIVER_MODESET)); + if (vblank->vblank_timer.crtc) + hrtimer_cancel(&vblank->vblank_timer.timer); + drm_vblank_destroy_worker(vblank); timer_delete_sync(&vblank->disable_timer); } @@ -2162,3 +2174,159 @@ err_free: return ret; } +/* + * VBLANK timer + */ + +static enum hrtimer_restart drm_vblank_timer_function(struct hrtimer *timer) +{ + struct drm_vblank_crtc_timer *vtimer = + container_of(timer, struct drm_vblank_crtc_timer, timer); + struct drm_crtc *crtc = vtimer->crtc; + const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + struct drm_device *dev = crtc->dev; + unsigned long flags; + ktime_t interval; + u64 ret_overrun; + bool succ; + + spin_lock_irqsave(&vtimer->interval_lock, flags); + interval = vtimer->interval; + spin_unlock_irqrestore(&vtimer->interval_lock, flags); + + if (!interval) + return HRTIMER_NORESTART; + + ret_overrun = hrtimer_forward_now(&vtimer->timer, interval); + if (ret_overrun != 1) + drm_dbg_vbl(dev, "vblank timer overrun\n"); + + if (crtc_funcs->handle_vblank_timeout) + succ = crtc_funcs->handle_vblank_timeout(crtc); + else + succ = drm_crtc_handle_vblank(crtc); + if (!succ) + return HRTIMER_NORESTART; + + return HRTIMER_RESTART; +} + +/** + * drm_crtc_vblank_start_timer - Starts the vblank timer on the given CRTC + * @crtc: the CRTC + * + * Drivers should call this function from their CRTC's enable_vblank + * function to start a vblank timer. The timer will fire after the duration + * of a full frame. drm_crtc_vblank_cancel_timer() disables a running timer. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int drm_crtc_vblank_start_timer(struct drm_crtc *crtc) +{ + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; + unsigned long flags; + + if (!vtimer->crtc) { + /* + * Set up the data structures on the first invocation. + */ + vtimer->crtc = crtc; + spin_lock_init(&vtimer->interval_lock); + hrtimer_setup(&vtimer->timer, drm_vblank_timer_function, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + } else { + /* + * Timer should not be active. If it is, wait for the + * previous cancel operations to finish. + */ + while (hrtimer_active(&vtimer->timer)) + hrtimer_try_to_cancel(&vtimer->timer); + } + + drm_calc_timestamping_constants(crtc, &crtc->mode); + + spin_lock_irqsave(&vtimer->interval_lock, flags); + vtimer->interval = ns_to_ktime(vblank->framedur_ns); + spin_unlock_irqrestore(&vtimer->interval_lock, flags); + + hrtimer_start(&vtimer->timer, vtimer->interval, HRTIMER_MODE_REL); + + return 0; +} +EXPORT_SYMBOL(drm_crtc_vblank_start_timer); + +/** + * drm_crtc_vblank_start_timer - Cancels the given CRTC's vblank timer + * @crtc: the CRTC + * + * Drivers should call this function from their CRTC's disable_vblank + * function to stop a vblank timer. + */ +void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc) +{ + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; + unsigned long flags; + + /* + * Calling hrtimer_cancel() can result in a deadlock with DRM's + * vblank_time_lime_lock and hrtimers' softirq_expiry_lock. So + * clear interval and indicate cancellation. The timer function + * will cancel itself on the next invocation. + */ + + spin_lock_irqsave(&vtimer->interval_lock, flags); + vtimer->interval = 0; + spin_unlock_irqrestore(&vtimer->interval_lock, flags); + + hrtimer_try_to_cancel(&vtimer->timer); +} +EXPORT_SYMBOL(drm_crtc_vblank_cancel_timer); + +/** + * drm_crtc_vblank_get_vblank_timeout - Returns the vblank timeout + * @crtc: The CRTC + * @vblank_time: Returns the next vblank timestamp + * + * The helper drm_crtc_vblank_get_vblank_timeout() returns the next vblank + * timestamp of the CRTC's vblank timer according to the timer's expiry + * time. + */ +void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time) +{ + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; + u64 cur_count; + ktime_t cur_time; + + if (!READ_ONCE(vblank->enabled)) { + *vblank_time = ktime_get(); + return; + } + + /* + * A concurrent vblank timeout could update the expires field before + * we compare it with the vblank time. Hence we'd compare the old + * expiry time to the new vblank time; deducing the timer had already + * expired. Reread until we get consistent values from both fields. + */ + do { + cur_count = drm_crtc_vblank_count_and_time(crtc, &cur_time); + *vblank_time = READ_ONCE(vtimer->timer.node.expires); + } while (cur_count != drm_crtc_vblank_count_and_time(crtc, &cur_time)); + + if (drm_WARN_ON(crtc->dev, !ktime_compare(*vblank_time, cur_time))) + return; /* Already expired */ + + /* + * To prevent races we roll the hrtimer forward before we do any + * interrupt processing - this is how real hw works (the interrupt + * is only generated after all the vblank registers are updated) + * and what the vblank core expects. Therefore we need to always + * correct the timestamp by one frame. + */ + *vblank_time = ktime_sub(*vblank_time, vtimer->interval); +} +EXPORT_SYMBOL(drm_crtc_vblank_get_vblank_timeout); diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c new file mode 100644 index 000000000000..a04a6ba1b0ca --- /dev/null +++ b/drivers/gpu/drm/drm_vblank_helper.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: MIT + +#include <drm/drm_atomic.h> +#include <drm/drm_crtc.h> +#include <drm/drm_managed.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_print.h> +#include <drm/drm_vblank.h> +#include <drm/drm_vblank_helper.h> + +/** + * DOC: overview + * + * The vblank helper library provides functions for supporting vertical + * blanking in DRM drivers. + * + * For vblank timers, several callback implementations are available. + * Drivers enable support for vblank timers by setting the vblank callbacks + * in struct &drm_crtc_funcs to the helpers provided by this library. The + * initializer macro DRM_CRTC_VBLANK_TIMER_FUNCS does this conveniently. + * The driver further has to send the VBLANK event from its atomic_flush + * callback and control vblank from the CRTC's atomic_enable and atomic_disable + * callbacks. The callbacks are located in struct &drm_crtc_helper_funcs. + * The vblank helper library provides implementations of these callbacks + * for drivers without further requirements. The initializer macro + * DRM_CRTC_HELPER_VBLANK_FUNCS sets them coveniently. + * + * Once the driver enables vblank support with drm_vblank_init(), each + * CRTC's vblank timer fires according to the programmed display mode. By + * default, the vblank timer invokes drm_crtc_handle_vblank(). Drivers with + * more specific requirements can set their own handler function in + * struct &drm_crtc_helper_funcs.handle_vblank_timeout. + */ + +/* + * VBLANK helpers + */ + +/** + * drm_crtc_vblank_atomic_flush - + * Implements struct &drm_crtc_helper_funcs.atomic_flush + * @crtc: The CRTC + * @state: The atomic state to apply + * + * The helper drm_crtc_vblank_atomic_flush() implements atomic_flush of + * struct drm_crtc_helper_funcs for CRTCs that only need to send out a + * VBLANK event. + * + * See also struct &drm_crtc_helper_funcs.atomic_flush. + */ +void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_device *dev = crtc->dev; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct drm_pending_vblank_event *event; + + spin_lock_irq(&dev->event_lock); + + event = crtc_state->event; + crtc_state->event = NULL; + + if (event) { + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + } + + spin_unlock_irq(&dev->event_lock); +} +EXPORT_SYMBOL(drm_crtc_vblank_atomic_flush); + +/** + * drm_crtc_vblank_atomic_enable - Implements struct &drm_crtc_helper_funcs.atomic_enable + * @crtc: The CRTC + * @state: The atomic state + * + * The helper drm_crtc_vblank_atomic_enable() implements atomic_enable + * of struct drm_crtc_helper_funcs for CRTCs the only need to enable VBLANKs. + * + * See also struct &drm_crtc_helper_funcs.atomic_enable. + */ +void drm_crtc_vblank_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + drm_crtc_vblank_on(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_atomic_enable); + +/** + * drm_crtc_vblank_atomic_disable - Implements struct &drm_crtc_helper_funcs.atomic_disable + * @crtc: The CRTC + * @state: The atomic state + * + * The helper drm_crtc_vblank_atomic_disable() implements atomic_disable + * of struct drm_crtc_helper_funcs for CRTCs the only need to disable VBLANKs. + * + * See also struct &drm_crtc_funcs.atomic_disable. + */ +void drm_crtc_vblank_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + drm_crtc_vblank_off(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_atomic_disable); + +/* + * VBLANK timer + */ + +/** + * drm_crtc_vblank_helper_enable_vblank_timer - Implements struct &drm_crtc_funcs.enable_vblank + * @crtc: The CRTC + * + * The helper drm_crtc_vblank_helper_enable_vblank_timer() implements + * enable_vblank of struct drm_crtc_helper_funcs for CRTCs that require + * a VBLANK timer. It sets up the timer on the first invocation. The + * started timer expires after the current frame duration. See struct + * &drm_vblank_crtc.framedur_ns. + * + * See also struct &drm_crtc_helper_funcs.enable_vblank. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc) +{ + return drm_crtc_vblank_start_timer(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_helper_enable_vblank_timer); + +/** + * drm_crtc_vblank_helper_disable_vblank_timer - Implements struct &drm_crtc_funcs.disable_vblank + * @crtc: The CRTC + * + * The helper drm_crtc_vblank_helper_disable_vblank_timer() implements + * disable_vblank of struct drm_crtc_funcs for CRTCs that require a + * VBLANK timer. + * + * See also struct &drm_crtc_helper_funcs.disable_vblank. + */ +void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc) +{ + drm_crtc_vblank_cancel_timer(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_helper_disable_vblank_timer); + +/** + * drm_crtc_vblank_helper_get_vblank_timestamp_from_timer - + * Implements struct &drm_crtc_funcs.get_vblank_timestamp + * @crtc: The CRTC + * @max_error: Maximum acceptable error + * @vblank_time: Returns the next vblank timestamp + * @in_vblank_irq: True is called from drm_crtc_handle_vblank() + * + * The helper drm_crtc_helper_get_vblank_timestamp_from_timer() implements + * get_vblank_timestamp of struct drm_crtc_funcs for CRTCs that require a + * VBLANK timer. It returns the timestamp according to the timer's expiry + * time. + * + * See also struct &drm_crtc_funcs.get_vblank_timestamp. + * + * Returns: + * True on success, or false otherwise. + */ +bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc, + int *max_error, + ktime_t *vblank_time, + bool in_vblank_irq) +{ + drm_crtc_vblank_get_vblank_timeout(crtc, vblank_time); + + return true; +} +EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_from_timer); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index e3fbb45f37a2..02714c9ab639 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -10,6 +10,7 @@ #include <linux/shmem_fs.h> #include <linux/module.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_prime.h> #include <drm/drm_vma_manager.h> #include <drm/exynos_drm.h> @@ -329,15 +330,16 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, unsigned int flags; int ret; + ret = drm_mode_size_dumb(dev, args, 0, 0); + if (ret) + return ret; + /* * allocate memory to be used for framebuffer. * - this callback would be called by user application * with DRM_IOCTL_MODE_CREATE_DUMB command. */ - args->pitch = args->width * ((args->bpp + 7) / 8); - args->size = args->pitch * args->height; - if (is_drm_iommu_supported(dev)) flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC; else diff --git a/drivers/gpu/drm/gma500/fbdev.c b/drivers/gpu/drm/gma500/fbdev.c index 32d31e5f5f1a..a6af21514cff 100644 --- a/drivers/gpu/drm/gma500/fbdev.c +++ b/drivers/gpu/drm/gma500/fbdev.c @@ -50,48 +50,6 @@ static const struct vm_operations_struct psb_fbdev_vm_ops = { * struct fb_ops */ -#define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) - -static int psb_fbdev_fb_setcolreg(unsigned int regno, - unsigned int red, unsigned int green, - unsigned int blue, unsigned int transp, - struct fb_info *info) -{ - struct drm_fb_helper *fb_helper = info->par; - struct drm_framebuffer *fb = fb_helper->fb; - uint32_t v; - - if (!fb) - return -ENOMEM; - - if (regno > 255) - return 1; - - red = CMAP_TOHW(red, info->var.red.length); - blue = CMAP_TOHW(blue, info->var.blue.length); - green = CMAP_TOHW(green, info->var.green.length); - transp = CMAP_TOHW(transp, info->var.transp.length); - - v = (red << info->var.red.offset) | - (green << info->var.green.offset) | - (blue << info->var.blue.offset) | - (transp << info->var.transp.offset); - - if (regno < 16) { - switch (fb->format->cpp[0] * 8) { - case 16: - ((uint32_t *) info->pseudo_palette)[regno] = v; - break; - case 24: - case 32: - ((uint32_t *) info->pseudo_palette)[regno] = v; - break; - } - } - - return 0; -} - static int psb_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { if (vma->vm_pgoff != 0) @@ -135,7 +93,6 @@ static const struct fb_ops psb_fbdev_fb_ops = { .owner = THIS_MODULE, __FB_DEFAULT_IOMEM_OPS_RDWR, DRM_FB_HELPER_DEFAULT_OPS, - .fb_setcolreg = psb_fbdev_fb_setcolreg, __FB_DEFAULT_IOMEM_OPS_DRAW, .fb_mmap = psb_fbdev_fb_mmap, .fb_destroy = psb_fbdev_fb_destroy, diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c index 4a15695fa933..1726a3fadff8 100644 --- a/drivers/gpu/drm/gud/gud_connector.c +++ b/drivers/gpu/drm/gud/gud_connector.c @@ -561,11 +561,11 @@ static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_conn continue; /* not a DRM property */ property = gud_connector_property_lookup(connector, prop); - if (WARN_ON(IS_ERR(property))) + if (drm_WARN_ON(drm, IS_ERR(property))) continue; state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state); - if (WARN_ON(IS_ERR(state_val))) + if (drm_WARN_ON(drm, IS_ERR(state_val))) continue; *state_val = val; @@ -593,7 +593,7 @@ int gud_connector_fill_properties(struct drm_connector_state *connector_state, unsigned int *state_val; state_val = gud_connector_tv_state_val(prop, &connector_state->tv); - if (WARN_ON_ONCE(IS_ERR(state_val))) + if (drm_WARN_ON_ONCE(connector_state->connector->dev, IS_ERR(state_val))) return PTR_ERR(state_val); val = *state_val; @@ -667,7 +667,7 @@ static int gud_connector_create(struct gud_device *gdrm, unsigned int index, return ret; } - if (WARN_ON(connector->index != index)) + if (drm_WARN_ON(drm, connector->index != index)) return -EINVAL; if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS) diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c index 54d9aa9998e5..3a208e956dff 100644 --- a/drivers/gpu/drm/gud/gud_pipe.c +++ b/drivers/gpu/drm/gud/gud_pipe.c @@ -61,7 +61,7 @@ static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format size_t len; void *buf; - WARN_ON_ONCE(format->char_per_block[0] != 1); + drm_WARN_ON_ONCE(fb->dev, format->char_per_block[0] != 1); /* Start on a byte boundary */ rect->x1 = ALIGN_DOWN(rect->x1, block_width); @@ -138,7 +138,7 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma pix = ((r >> 7) << 2) | ((g >> 7) << 1) | (b >> 7); break; default: - WARN_ON_ONCE(1); + drm_WARN_ON_ONCE(fb->dev, 1); return len; } @@ -527,7 +527,7 @@ int gud_plane_atomic_check(struct drm_plane *plane, drm_connector_list_iter_end(&conn_iter); } - if (WARN_ON_ONCE(!connector_state)) + if (drm_WARN_ON_ONCE(plane->dev, !connector_state)) return -ENOENT; len = struct_size(req, properties, @@ -539,7 +539,7 @@ int gud_plane_atomic_check(struct drm_plane *plane, gud_from_display_mode(&req->mode, mode); req->format = gud_from_fourcc(format->format); - if (WARN_ON_ONCE(!req->format)) { + if (drm_WARN_ON_ONCE(plane->dev, !req->format)) { ret = -EINVAL; goto out; } @@ -561,7 +561,7 @@ int gud_plane_atomic_check(struct drm_plane *plane, val = new_plane_state->rotation; break; default: - WARN_ON_ONCE(1); + drm_WARN_ON_ONCE(plane->dev, 1); ret = -EINVAL; goto out; } diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 945b9482bcb3..6e6eb1c12a68 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -19,6 +19,8 @@ #include <drm/drm_probe_helper.h> #include <drm/drm_panic.h> #include <drm/drm_plane.h> +#include <drm/drm_vblank.h> +#include <drm/drm_vblank_helper.h> #include "hyperv_drm.h" @@ -111,11 +113,15 @@ static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc, crtc_state->mode.hdisplay, crtc_state->mode.vdisplay, plane_state->fb->pitches[0]); + + drm_crtc_vblank_on(crtc); } static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = { .atomic_check = drm_crtc_helper_atomic_check, + .atomic_flush = drm_crtc_vblank_atomic_flush, .atomic_enable = hyperv_crtc_helper_atomic_enable, + .atomic_disable = drm_crtc_vblank_atomic_disable, }; static const struct drm_crtc_funcs hyperv_crtc_funcs = { @@ -125,6 +131,7 @@ static const struct drm_crtc_funcs hyperv_crtc_funcs = { .page_flip = drm_atomic_helper_page_flip, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + DRM_CRTC_VBLANK_TIMER_FUNCS, }; static int hyperv_plane_atomic_check(struct drm_plane *plane, @@ -321,6 +328,10 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv) return ret; } + ret = drm_vblank_init(dev, 1); + if (ret) + return ret; + drm_mode_config_reset(dev); return 0; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index 1f4814968868..57bb111d65da 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -1029,7 +1029,7 @@ static void i915_ttm_delayed_free(struct drm_i915_gem_object *obj) { GEM_BUG_ON(!obj->ttm.created); - ttm_bo_put(i915_gem_to_ttm(obj)); + ttm_bo_fini(i915_gem_to_ttm(obj)); } static vm_fault_t vm_fault_ttm(struct vm_fault *vmf) @@ -1325,7 +1325,7 @@ int __i915_gem_ttm_object_init(struct intel_memory_region *mem, * If this function fails, it will call the destructor, but * our caller still owns the object. So no freeing in the * destructor until obj->ttm.created is true. - * Similarly, in delayed_destroy, we can't call ttm_bo_put() + * Similarly, in delayed_destroy, we can't call ttm_bo_fini() * until successful initialization. */ ret = ttm_bo_init_reserved(&i915->bdev, i915_gem_to_ttm(obj), bo_type, diff --git a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c index ec5fd9a01f1e..465b5a6ad5bb 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c @@ -17,7 +17,9 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_fbdev_dma.h> +#include <drm/drm_fourcc.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_managed.h> @@ -141,17 +143,32 @@ static int imx_drm_dumb_create(struct drm_file *file_priv, struct drm_device *drm, struct drm_mode_create_dumb *args) { - u32 width = args->width; + u32 fourcc; + const struct drm_format_info *info; + u64 pitch_align; int ret; - args->width = ALIGN(width, 8); - - ret = drm_gem_dma_dumb_create(file_priv, drm, args); + /* + * Hardware requires the framebuffer width to be aligned to + * multiples of 8. The mode-setting code handles this, but + * the buffer pitch has to be aligned as well. Set the pitch + * alignment accordingly, so that the each scanline fits into + * the allocated buffer. + */ + fourcc = drm_driver_color_mode_format(drm, args->bpp); + if (fourcc == DRM_FORMAT_INVALID) + return -EINVAL; + info = drm_format_info(fourcc); + if (!info) + return -EINVAL; + pitch_align = drm_format_info_min_pitch(info, 0, SZ_8); + if (!pitch_align || pitch_align > U32_MAX) + return -EINVAL; + ret = drm_mode_size_dumb(drm, args, pitch_align, 0); if (ret) return ret; - args->width = width; - return ret; + return drm_gem_dma_dumb_create(file_priv, drm, args); } static const struct drm_driver imx_drm_driver = { diff --git a/drivers/gpu/drm/imx/ipuv3/imx-tve.c b/drivers/gpu/drm/imx/ipuv3/imx-tve.c index c5629e155d25..63f23b821b0b 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-tve.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-tve.c @@ -368,17 +368,20 @@ static unsigned long clk_tve_di_recalc_rate(struct clk_hw *hw, return 0; } -static long clk_tve_di_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_tve_di_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { unsigned long div; - div = *prate / rate; + div = req->best_parent_rate / req->rate; if (div >= 4) - return *prate / 4; + req->rate = req->best_parent_rate / 4; else if (div >= 2) - return *prate / 2; - return *prate; + req->rate = req->best_parent_rate / 2; + else + req->rate = req->best_parent_rate; + + return 0; } static int clk_tve_di_set_rate(struct clk_hw *hw, unsigned long rate, @@ -409,7 +412,7 @@ static int clk_tve_di_set_rate(struct clk_hw *hw, unsigned long rate, } static const struct clk_ops clk_tve_di_ops = { - .round_rate = clk_tve_di_round_rate, + .determine_rate = clk_tve_di_determine_rate, .set_rate = clk_tve_di_set_rate, .recalc_rate = clk_tve_di_recalc_rate, }; diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c index 6d8325c76697..dfdeb926fe9c 100644 --- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c +++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c @@ -134,10 +134,10 @@ static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge, struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); struct drm_display_info *di = &conn_state->connector->display_info; struct drm_bridge_state *next_bridge_state = NULL; - struct drm_bridge *next_bridge; u32 bus_flags, bus_fmt; - next_bridge = drm_bridge_get_next_bridge(bridge); + struct drm_bridge *next_bridge __free(drm_bridge_put) = drm_bridge_get_next_bridge(bridge); + if (next_bridge) next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state, next_bridge); diff --git a/drivers/gpu/drm/loongson/lsdc_gem.c b/drivers/gpu/drm/loongson/lsdc_gem.c index a720d8f53209..c29dd730a894 100644 --- a/drivers/gpu/drm/loongson/lsdc_gem.c +++ b/drivers/gpu/drm/loongson/lsdc_gem.c @@ -6,6 +6,7 @@ #include <linux/dma-buf.h> #include <drm/drm_debugfs.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_file.h> #include <drm/drm_gem.h> #include <drm/drm_prime.h> @@ -57,7 +58,7 @@ static void lsdc_gem_object_free(struct drm_gem_object *obj) struct ttm_buffer_object *tbo = to_ttm_bo(obj); if (tbo) - ttm_bo_put(tbo); + ttm_bo_fini(tbo); } static int lsdc_gem_object_vmap(struct drm_gem_object *obj, struct iosys_map *map) @@ -204,45 +205,31 @@ int lsdc_dumb_create(struct drm_file *file, struct drm_device *ddev, const struct lsdc_desc *descp = ldev->descp; u32 domain = LSDC_GEM_DOMAIN_VRAM; struct drm_gem_object *gobj; - size_t size; - u32 pitch; - u32 handle; int ret; - if (!args->width || !args->height) - return -EINVAL; - - if (args->bpp != 32 && args->bpp != 16) - return -EINVAL; - - pitch = args->width * args->bpp / 8; - pitch = ALIGN(pitch, descp->pitch_align); - size = pitch * args->height; - size = ALIGN(size, PAGE_SIZE); + ret = drm_mode_size_dumb(ddev, args, descp->pitch_align, 0); + if (ret) + return ret; /* Maximum single bo size allowed is the half vram size available */ - if (size > ldev->vram_size / 2) { - drm_err(ddev, "Requesting(%zuMiB) failed\n", size >> 20); + if (args->size > ldev->vram_size / 2) { + drm_err(ddev, "Requesting(%zuMiB) failed\n", (size_t)(args->size >> PAGE_SHIFT)); return -ENOMEM; } - gobj = lsdc_gem_object_create(ddev, domain, size, false, NULL, NULL); + gobj = lsdc_gem_object_create(ddev, domain, args->size, false, NULL, NULL); if (IS_ERR(gobj)) { drm_err(ddev, "Failed to create gem object\n"); return PTR_ERR(gobj); } - ret = drm_gem_handle_create(file, gobj, &handle); + ret = drm_gem_handle_create(file, gobj, &args->handle); /* drop reference from allocate, handle holds it now */ drm_gem_object_put(gobj); if (ret) return ret; - args->pitch = pitch; - args->size = size; - args->handle = handle; - return 0; } diff --git a/drivers/gpu/drm/mcde/mcde_clk_div.c b/drivers/gpu/drm/mcde/mcde_clk_div.c index 3056ac566473..8c5af2677357 100644 --- a/drivers/gpu/drm/mcde/mcde_clk_div.c +++ b/drivers/gpu/drm/mcde/mcde_clk_div.c @@ -71,12 +71,15 @@ static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate, return best_div; } -static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int mcde_clk_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - int div = mcde_clk_div_choose_div(hw, rate, prate, true); + int div = mcde_clk_div_choose_div(hw, req->rate, + &req->best_parent_rate, true); - return DIV_ROUND_UP_ULL(*prate, div); + req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div); + + return 0; } static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw, @@ -132,7 +135,7 @@ static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops mcde_clk_div_ops = { .enable = mcde_clk_div_enable, .recalc_rate = mcde_clk_div_recalc_rate, - .round_rate = mcde_clk_div_round_rate, + .determine_rate = mcde_clk_div_determine_rate, .set_rate = mcde_clk_div_set_rate, }; diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 07d8cdd6bb2e..688705a871cf 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -10,8 +10,10 @@ #include <linux/shmem_fs.h> #include <linux/dma-buf.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_prime.h> #include <drm/drm_file.h> +#include <drm/drm_fourcc.h> #include <trace/events/gpu_mem.h> @@ -698,8 +700,29 @@ void msm_gem_unpin_iova(struct drm_gem_object *obj, struct drm_gpuvm *vm) int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - args->pitch = align_pitch(args->width, args->bpp); - args->size = PAGE_ALIGN(args->pitch * args->height); + u32 fourcc; + const struct drm_format_info *info; + u64 pitch_align; + int ret; + + /* + * Adreno needs pitch aligned to 32 pixels. Compute the number + * of bytes for a block of 32 pixels at the given color format. + * Use the result as pitch alignment. + */ + fourcc = drm_driver_color_mode_format(dev, args->bpp); + if (fourcc == DRM_FORMAT_INVALID) + return -EINVAL; + info = drm_format_info(fourcc); + if (!info) + return -EINVAL; + pitch_align = drm_format_info_min_pitch(info, 0, SZ_32); + if (!pitch_align || pitch_align > U32_MAX) + return -EINVAL; + ret = drm_mode_size_dumb(dev, args, pitch_align, 0); + if (ret) + return ret; + return msm_gem_new_handle(dev, file, args->size, MSM_BO_SCANOUT | MSM_BO_WC, &args->handle, "dumb"); } diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index c88776d1e784..3b5757aed9c8 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -28,6 +28,7 @@ config DRM_NOUVEAU select THERMAL if ACPI && X86 select ACPI_VIDEO if ACPI && X86 select SND_HDA_COMPONENT if SND_HDA_CORE + select PM_DEVFREQ if ARCH_TEGRA help Choose this option for open-source NVIDIA support. diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h b/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h index 22f74fc88cd7..57bc542780bb 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h @@ -9,6 +9,8 @@ struct nvkm_device_tegra { struct nvkm_device device; struct platform_device *pdev; + void __iomem *regs; + struct reset_control *rst; struct clk *clk; struct clk *clk_ref; diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h index d5d8877064a7..6a09d397c651 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h @@ -134,4 +134,5 @@ int gf100_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct int gk104_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **); int gk20a_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **); int gm20b_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **); +int gp10b_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index d59fd12268b9..6c26beeb427f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -57,7 +57,7 @@ nouveau_bo(struct ttm_buffer_object *bo) static inline void nouveau_bo_fini(struct nouveau_bo *bo) { - ttm_bo_put(&bo->bo); + ttm_bo_fini(&bo->bo); } extern struct ttm_device_funcs nouveau_bo_driver; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 805d0a87aa54..54aed3656a4c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -30,6 +30,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_client_event.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_probe_helper.h> @@ -807,9 +808,9 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, uint32_t domain; int ret; - args->pitch = roundup(args->width * (args->bpp / 8), 256); - args->size = args->pitch * args->height; - args->size = roundup(args->size, PAGE_SIZE); + ret = drm_mode_size_dumb(dev, args, SZ_256, 0); + if (ret) + return ret; /* Use VRAM if there is any ; otherwise fallback to system memory */ if (nouveau_drm(dev)->client.device.info.ram_size != 0) diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 690e10fbf0bd..395d92ab6271 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -87,7 +87,7 @@ nouveau_gem_object_del(struct drm_gem_object *gem) return; } - ttm_bo_put(&nvbo->bo); + ttm_bo_fini(&nvbo->bo); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); diff --git a/drivers/gpu/drm/nouveau/nouveau_platform.c b/drivers/gpu/drm/nouveau/nouveau_platform.c index 8d5853deeee4..9fd351273236 100644 --- a/drivers/gpu/drm/nouveau/nouveau_platform.c +++ b/drivers/gpu/drm/nouveau/nouveau_platform.c @@ -21,6 +21,8 @@ */ #include "nouveau_platform.h" +#include <nvkm/subdev/clk/gk20a_devfreq.h> + static int nouveau_platform_probe(struct platform_device *pdev) { const struct nvkm_device_tegra_func *func; @@ -40,6 +42,21 @@ static void nouveau_platform_remove(struct platform_device *pdev) nouveau_drm_device_remove(drm); } +#ifdef CONFIG_PM_SLEEP +static int nouveau_platform_suspend(struct device *dev) +{ + return gk20a_devfreq_suspend(dev); +} + +static int nouveau_platform_resume(struct device *dev) +{ + return gk20a_devfreq_resume(dev); +} + +static SIMPLE_DEV_PM_OPS(nouveau_pm_ops, nouveau_platform_suspend, + nouveau_platform_resume); +#endif + #if IS_ENABLED(CONFIG_OF) static const struct nvkm_device_tegra_func gk20a_platform_data = { .iommu_bit = 34, @@ -81,6 +98,9 @@ struct platform_driver nouveau_platform_driver = { .driver = { .name = "nouveau", .of_match_table = of_match_ptr(nouveau_platform_match), +#ifdef CONFIG_PM_SLEEP + .pm = &nouveau_pm_ops, +#endif }, .probe = nouveau_platform_probe, .remove = nouveau_platform_remove, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 3375a59ebf1a..2517b65d8faa 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2280,6 +2280,7 @@ nv13b_chipset = { .acr = { 0x00000001, gp10b_acr_new }, .bar = { 0x00000001, gm20b_bar_new }, .bus = { 0x00000001, gf100_bus_new }, + .clk = { 0x00000001, gp10b_clk_new }, .fault = { 0x00000001, gp10b_fault_new }, .fb = { 0x00000001, gp10b_fb_new }, .fuse = { 0x00000001, gm107_fuse_new }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c index 114e50ca1827..03aa6f09ec89 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c @@ -259,6 +259,10 @@ nvkm_device_tegra_new(const struct nvkm_device_tegra_func *func, tdev->func = func; tdev->pdev = pdev; + tdev->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tdev->regs)) + return PTR_ERR(tdev->regs); + if (func->require_vdd) { tdev->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(tdev->vdd)) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild index dcecd499d8df..be8f3283ee16 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild @@ -10,6 +10,8 @@ nvkm-y += nvkm/subdev/clk/gf100.o nvkm-y += nvkm/subdev/clk/gk104.o nvkm-y += nvkm/subdev/clk/gk20a.o nvkm-y += nvkm/subdev/clk/gm20b.o +nvkm-y += nvkm/subdev/clk/gp10b.o +nvkm-$(CONFIG_PM_DEVFREQ) += nvkm/subdev/clk/gk20a_devfreq.o nvkm-y += nvkm/subdev/clk/pllnv04.o nvkm-y += nvkm/subdev/clk/pllgt215.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c index d573fb0917fc..65f5d0f1f3bf 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -23,6 +23,7 @@ * */ #include "priv.h" +#include "gk20a_devfreq.h" #include "gk20a.h" #include <core/tegra.h> @@ -589,6 +590,10 @@ gk20a_clk_init(struct nvkm_clk *base) return ret; } + ret = gk20a_devfreq_init(base, &clk->devfreq); + if (ret) + return ret; + return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h index 286413ff4a9e..ea5b0bab4cce 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h @@ -118,6 +118,7 @@ struct gk20a_clk { const struct gk20a_clk_pllg_params *params; struct gk20a_pll pll; u32 parent_rate; + struct gk20a_devfreq *devfreq; u32 (*div_to_pl)(u32); u32 (*pl_to_div)(u32); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c new file mode 100644 index 000000000000..41003cbcdbfa --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: MIT +#include <linux/clk.h> +#include <linux/math64.h> +#include <linux/platform_device.h> +#include <linux/pm_opp.h> + +#include <drm/drm_managed.h> + +#include <subdev/clk.h> + +#include "nouveau_drv.h" +#include "nouveau_chan.h" +#include "priv.h" +#include "gk20a_devfreq.h" +#include "gk20a.h" +#include "gp10b.h" + +#define PMU_BUSY_CYCLES_NORM_MAX 1000U + +#define PWR_PMU_IDLE_COUNTER_TOTAL 0U +#define PWR_PMU_IDLE_COUNTER_BUSY 4U + +#define PWR_PMU_IDLE_COUNT_REG_OFFSET 0x0010A508U +#define PWR_PMU_IDLE_COUNT_REG_SIZE 16U +#define PWR_PMU_IDLE_COUNT_MASK 0x7FFFFFFFU +#define PWR_PMU_IDLE_COUNT_RESET_VALUE (0x1U << 31U) + +#define PWR_PMU_IDLE_INTR_REG_OFFSET 0x0010A9E8U +#define PWR_PMU_IDLE_INTR_ENABLE_VALUE 0U + +#define PWR_PMU_IDLE_INTR_STATUS_REG_OFFSET 0x0010A9ECU +#define PWR_PMU_IDLE_INTR_STATUS_MASK 0x00000001U +#define PWR_PMU_IDLE_INTR_STATUS_RESET_VALUE 0x1U + +#define PWR_PMU_IDLE_THRESHOLD_REG_OFFSET 0x0010A8A0U +#define PWR_PMU_IDLE_THRESHOLD_REG_SIZE 4U +#define PWR_PMU_IDLE_THRESHOLD_MAX_VALUE 0x7FFFFFFFU + +#define PWR_PMU_IDLE_CTRL_REG_OFFSET 0x0010A50CU +#define PWR_PMU_IDLE_CTRL_REG_SIZE 16U +#define PWR_PMU_IDLE_CTRL_VALUE_MASK 0x3U +#define PWR_PMU_IDLE_CTRL_VALUE_BUSY 0x2U +#define PWR_PMU_IDLE_CTRL_VALUE_ALWAYS 0x3U +#define PWR_PMU_IDLE_CTRL_FILTER_MASK (0x1U << 2) +#define PWR_PMU_IDLE_CTRL_FILTER_DISABLED 0x0U + +#define PWR_PMU_IDLE_MASK_REG_OFFSET 0x0010A504U +#define PWR_PMU_IDLE_MASK_REG_SIZE 16U +#define PWM_PMU_IDLE_MASK_GR_ENABLED 0x1U +#define PWM_PMU_IDLE_MASK_CE_2_ENABLED 0x200000U + +/** + * struct gk20a_devfreq - Device frequency management + */ +struct gk20a_devfreq { + /** @devfreq: devfreq device. */ + struct devfreq *devfreq; + + /** @regs: Device registers. */ + void __iomem *regs; + + /** @gov_data: Governor data. */ + struct devfreq_simple_ondemand_data gov_data; + + /** @busy_time: Busy time. */ + ktime_t busy_time; + + /** @total_time: Total time. */ + ktime_t total_time; + + /** @time_last_update: Last update time. */ + ktime_t time_last_update; +}; + +static struct gk20a_devfreq *dev_to_gk20a_devfreq(struct device *dev) +{ + struct nouveau_drm *drm = dev_get_drvdata(dev); + struct nvkm_subdev *subdev = nvkm_device_subdev(drm->nvkm, NVKM_SUBDEV_CLK, 0); + struct nvkm_clk *base = nvkm_clk(subdev); + + switch (drm->nvkm->chipset) { + case 0x13b: return gp10b_clk(base)->devfreq; break; + default: return gk20a_clk(base)->devfreq; break; + } +} + +static void gk20a_pmu_init_perfmon_counter(struct gk20a_devfreq *gdevfreq) +{ + u32 data; + + // Set pmu idle intr status bit on total counter overflow + writel(PWR_PMU_IDLE_INTR_ENABLE_VALUE, + gdevfreq->regs + PWR_PMU_IDLE_INTR_REG_OFFSET); + + writel(PWR_PMU_IDLE_THRESHOLD_MAX_VALUE, + gdevfreq->regs + PWR_PMU_IDLE_THRESHOLD_REG_OFFSET + + (PWR_PMU_IDLE_COUNTER_TOTAL * PWR_PMU_IDLE_THRESHOLD_REG_SIZE)); + + // Setup counter for total cycles + data = readl(gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET + + (PWR_PMU_IDLE_COUNTER_TOTAL * PWR_PMU_IDLE_CTRL_REG_SIZE)); + data &= ~(PWR_PMU_IDLE_CTRL_VALUE_MASK | PWR_PMU_IDLE_CTRL_FILTER_MASK); + data |= PWR_PMU_IDLE_CTRL_VALUE_ALWAYS | PWR_PMU_IDLE_CTRL_FILTER_DISABLED; + writel(data, gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET + + (PWR_PMU_IDLE_COUNTER_TOTAL * PWR_PMU_IDLE_CTRL_REG_SIZE)); + + // Setup counter for busy cycles + writel(PWM_PMU_IDLE_MASK_GR_ENABLED | PWM_PMU_IDLE_MASK_CE_2_ENABLED, + gdevfreq->regs + PWR_PMU_IDLE_MASK_REG_OFFSET + + (PWR_PMU_IDLE_COUNTER_BUSY * PWR_PMU_IDLE_MASK_REG_SIZE)); + + data = readl(gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET + + (PWR_PMU_IDLE_COUNTER_BUSY * PWR_PMU_IDLE_CTRL_REG_SIZE)); + data &= ~(PWR_PMU_IDLE_CTRL_VALUE_MASK | PWR_PMU_IDLE_CTRL_FILTER_MASK); + data |= PWR_PMU_IDLE_CTRL_VALUE_BUSY | PWR_PMU_IDLE_CTRL_FILTER_DISABLED; + writel(data, gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET + + (PWR_PMU_IDLE_COUNTER_BUSY * PWR_PMU_IDLE_CTRL_REG_SIZE)); +} + +static u32 gk20a_pmu_read_idle_counter(struct gk20a_devfreq *gdevfreq, u32 counter_id) +{ + u32 ret; + + ret = readl(gdevfreq->regs + PWR_PMU_IDLE_COUNT_REG_OFFSET + + (counter_id * PWR_PMU_IDLE_COUNT_REG_SIZE)); + + return ret & PWR_PMU_IDLE_COUNT_MASK; +} + +static void gk20a_pmu_reset_idle_counter(struct gk20a_devfreq *gdevfreq, u32 counter_id) +{ + writel(PWR_PMU_IDLE_COUNT_RESET_VALUE, gdevfreq->regs + PWR_PMU_IDLE_COUNT_REG_OFFSET + + (counter_id * PWR_PMU_IDLE_COUNT_REG_SIZE)); +} + +static u32 gk20a_pmu_read_idle_intr_status(struct gk20a_devfreq *gdevfreq) +{ + u32 ret; + + ret = readl(gdevfreq->regs + PWR_PMU_IDLE_INTR_STATUS_REG_OFFSET); + + return ret & PWR_PMU_IDLE_INTR_STATUS_MASK; +} + +static void gk20a_pmu_clear_idle_intr_status(struct gk20a_devfreq *gdevfreq) +{ + writel(PWR_PMU_IDLE_INTR_STATUS_RESET_VALUE, + gdevfreq->regs + PWR_PMU_IDLE_INTR_STATUS_REG_OFFSET); +} + +static void gk20a_devfreq_update_utilization(struct gk20a_devfreq *gdevfreq) +{ + ktime_t now, last; + u64 busy_cycles, total_cycles; + u32 norm, intr_status; + + now = ktime_get(); + last = gdevfreq->time_last_update; + gdevfreq->total_time = ktime_us_delta(now, last); + + busy_cycles = gk20a_pmu_read_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_BUSY); + total_cycles = gk20a_pmu_read_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_TOTAL); + intr_status = gk20a_pmu_read_idle_intr_status(gdevfreq); + + gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_BUSY); + gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_TOTAL); + + if (intr_status != 0UL) { + norm = PMU_BUSY_CYCLES_NORM_MAX; + gk20a_pmu_clear_idle_intr_status(gdevfreq); + } else if (total_cycles == 0ULL || busy_cycles > total_cycles) { + norm = PMU_BUSY_CYCLES_NORM_MAX; + } else { + norm = (u32)div64_u64(busy_cycles * PMU_BUSY_CYCLES_NORM_MAX, + total_cycles); + } + + gdevfreq->busy_time = div_u64(gdevfreq->total_time * norm, PMU_BUSY_CYCLES_NORM_MAX); + gdevfreq->time_last_update = now; +} + +static int gk20a_devfreq_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + struct nouveau_drm *drm = dev_get_drvdata(dev); + struct nvkm_subdev *subdev = nvkm_device_subdev(drm->nvkm, NVKM_SUBDEV_CLK, 0); + struct nvkm_clk *base = nvkm_clk(subdev); + struct nvkm_pstate *pstates = base->func->pstates; + int nr_pstates = base->func->nr_pstates; + int i, ret; + + for (i = 0; i < nr_pstates - 1; i++) + if (pstates[i].base.domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV >= *freq) + break; + + ret = nvkm_clk_ustate(base, pstates[i].pstate, 0); + ret |= nvkm_clk_ustate(base, pstates[i].pstate, 1); + if (ret) { + nvkm_error(subdev, "cannot update clock\n"); + return ret; + } + + *freq = pstates[i].base.domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV; + + return 0; +} + +static int gk20a_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct nouveau_drm *drm = dev_get_drvdata(dev); + struct nvkm_subdev *subdev = nvkm_device_subdev(drm->nvkm, NVKM_SUBDEV_CLK, 0); + struct nvkm_clk *base = nvkm_clk(subdev); + + *freq = nvkm_clk_read(base, nv_clk_src_gpc) * GK20A_CLK_GPC_MDIV; + + return 0; +} + +static void gk20a_devfreq_reset(struct gk20a_devfreq *gdevfreq) +{ + gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_BUSY); + gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_TOTAL); + gk20a_pmu_clear_idle_intr_status(gdevfreq); + + gdevfreq->busy_time = 0; + gdevfreq->total_time = 0; + gdevfreq->time_last_update = ktime_get(); +} + +static int gk20a_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *status) +{ + struct nouveau_drm *drm = dev_get_drvdata(dev); + struct gk20a_devfreq *gdevfreq = dev_to_gk20a_devfreq(dev); + + gk20a_devfreq_get_cur_freq(dev, &status->current_frequency); + + gk20a_devfreq_update_utilization(gdevfreq); + + status->busy_time = ktime_to_ns(gdevfreq->busy_time); + status->total_time = ktime_to_ns(gdevfreq->total_time); + + gk20a_devfreq_reset(gdevfreq); + + NV_DEBUG(drm, "busy %lu total %lu %lu %% freq %lu MHz\n", + status->busy_time, status->total_time, + status->busy_time / (status->total_time / 100), + status->current_frequency / 1000 / 1000); + + return 0; +} + +static struct devfreq_dev_profile gk20a_devfreq_profile = { + .timer = DEVFREQ_TIMER_DELAYED, + .polling_ms = 50, + .target = gk20a_devfreq_target, + .get_cur_freq = gk20a_devfreq_get_cur_freq, + .get_dev_status = gk20a_devfreq_get_dev_status, +}; + +int gk20a_devfreq_init(struct nvkm_clk *base, struct gk20a_devfreq **gdevfreq) +{ + struct nvkm_device *device = base->subdev.device; + struct nouveau_drm *drm = dev_get_drvdata(device->dev); + struct nvkm_device_tegra *tdev = device->func->tegra(device); + struct nvkm_pstate *pstates = base->func->pstates; + int nr_pstates = base->func->nr_pstates; + struct gk20a_devfreq *new_gdevfreq; + int i; + + new_gdevfreq = drmm_kzalloc(drm->dev, sizeof(struct gk20a_devfreq), GFP_KERNEL); + if (!new_gdevfreq) + return -ENOMEM; + + new_gdevfreq->regs = tdev->regs; + + for (i = 0; i < nr_pstates; i++) + dev_pm_opp_add(base->subdev.device->dev, + pstates[i].base.domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV, 0); + + gk20a_pmu_init_perfmon_counter(new_gdevfreq); + gk20a_devfreq_reset(new_gdevfreq); + + gk20a_devfreq_profile.initial_freq = + nvkm_clk_read(base, nv_clk_src_gpc) * GK20A_CLK_GPC_MDIV; + + new_gdevfreq->gov_data.upthreshold = 45; + new_gdevfreq->gov_data.downdifferential = 5; + + new_gdevfreq->devfreq = devm_devfreq_add_device(device->dev, + &gk20a_devfreq_profile, + DEVFREQ_GOV_SIMPLE_ONDEMAND, + &new_gdevfreq->gov_data); + if (IS_ERR(new_gdevfreq->devfreq)) + return PTR_ERR(new_gdevfreq->devfreq); + + *gdevfreq = new_gdevfreq; + + return 0; +} + +int gk20a_devfreq_resume(struct device *dev) +{ + struct gk20a_devfreq *gdevfreq = dev_to_gk20a_devfreq(dev); + + if (!gdevfreq || !gdevfreq->devfreq) + return 0; + + return devfreq_resume_device(gdevfreq->devfreq); +} + +int gk20a_devfreq_suspend(struct device *dev) +{ + struct gk20a_devfreq *gdevfreq = dev_to_gk20a_devfreq(dev); + + if (!gdevfreq || !gdevfreq->devfreq) + return 0; + + return devfreq_suspend_device(gdevfreq->devfreq); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h new file mode 100644 index 000000000000..5b7ca8a7a5cd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __GK20A_DEVFREQ_H__ +#define __GK20A_DEVFREQ_H__ + +#include <linux/devfreq.h> + +struct gk20a_devfreq; + +#if defined(CONFIG_PM_DEVFREQ) +int gk20a_devfreq_init(struct nvkm_clk *base, struct gk20a_devfreq **devfreq); + +int gk20a_devfreq_resume(struct device *dev); +int gk20a_devfreq_suspend(struct device *dev); +#else +static inline int gk20a_devfreq_init(struct nvkm_clk *base, struct gk20a_devfreq **devfreq) +{ + return 0; +} + +static inline int gk20a_devfreq_resume(struct device dev) { return 0; } +static inline int gk20a_devfreq_suspend(struct device *dev) { return 0; } +#endif /* CONFIG_PM_DEVFREQ */ + +#endif /* __GK20A_DEVFREQ_H__ */ diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c index 7c33542f651b..fa8ca53acbd1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c @@ -27,6 +27,7 @@ #include <core/tegra.h> #include "priv.h" +#include "gk20a_devfreq.h" #include "gk20a.h" #define GPCPLL_CFG_SYNC_MODE BIT(2) @@ -869,6 +870,10 @@ gm20b_clk_init(struct nvkm_clk *base) return ret; } + ret = gk20a_devfreq_init(base, &clk->devfreq); + if (ret) + return ret; + return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c new file mode 100644 index 000000000000..492b62c0ee96 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: MIT +#include <subdev/clk.h> +#include <subdev/timer.h> +#include <core/device.h> +#include <core/tegra.h> + +#include "priv.h" +#include "gk20a_devfreq.h" +#include "gk20a.h" +#include "gp10b.h" + +static int +gp10b_clk_init(struct nvkm_clk *base) +{ + struct gp10b_clk *clk = gp10b_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + int ret; + + /* Start with the highest frequency, matching the BPMP default */ + base->func->calc(base, &base->func->pstates[base->func->nr_pstates - 1].base); + ret = base->func->prog(base); + if (ret) { + nvkm_error(subdev, "cannot initialize clock\n"); + return ret; + } + + ret = gk20a_devfreq_init(base, &clk->devfreq); + if (ret) + return ret; + + return 0; +} + +static int +gp10b_clk_read(struct nvkm_clk *base, enum nv_clk_src src) +{ + struct gp10b_clk *clk = gp10b_clk(base); + struct nvkm_subdev *subdev = &clk->base.subdev; + + switch (src) { + case nv_clk_src_gpc: + return clk_get_rate(clk->clk) / GK20A_CLK_GPC_MDIV; + default: + nvkm_error(subdev, "invalid clock source %d\n", src); + return -EINVAL; + } +} + +static int +gp10b_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) +{ + struct gp10b_clk *clk = gp10b_clk(base); + u32 target_rate = cstate->domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV; + + clk->new_rate = clk_round_rate(clk->clk, target_rate) / GK20A_CLK_GPC_MDIV; + + return 0; +} + +static int +gp10b_clk_prog(struct nvkm_clk *base) +{ + struct gp10b_clk *clk = gp10b_clk(base); + int ret; + + ret = clk_set_rate(clk->clk, clk->new_rate * GK20A_CLK_GPC_MDIV); + if (ret < 0) + return ret; + + clk->rate = clk_get_rate(clk->clk) / GK20A_CLK_GPC_MDIV; + + return 0; +} + +static struct nvkm_pstate +gp10b_pstates[] = { + { + .base = { + .domain[nv_clk_src_gpc] = 114750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 216750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 318750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 420750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 522750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 624750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 726750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 828750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 930750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 1032750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 1134750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 1236750, + }, + }, + { + .base = { + .domain[nv_clk_src_gpc] = 1300500, + }, + }, +}; + +static const struct nvkm_clk_func +gp10b_clk = { + .init = gp10b_clk_init, + .read = gp10b_clk_read, + .calc = gp10b_clk_calc, + .prog = gp10b_clk_prog, + .tidy = gk20a_clk_tidy, + .pstates = gp10b_pstates, + .nr_pstates = ARRAY_SIZE(gp10b_pstates), + .domains = { + { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV }, + { nv_clk_src_max } + } +}; + +int +gp10b_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_clk **pclk) +{ + struct nvkm_device_tegra *tdev = device->func->tegra(device); + const struct nvkm_clk_func *func = &gp10b_clk; + struct gp10b_clk *clk; + int ret, i; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) + return -ENOMEM; + *pclk = &clk->base; + clk->clk = tdev->clk; + + /* Finish initializing the pstates */ + for (i = 0; i < func->nr_pstates; i++) { + INIT_LIST_HEAD(&func->pstates[i].list); + func->pstates[i].pstate = i + 1; + } + + ret = nvkm_clk_ctor(func, device, type, inst, true, &clk->base); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h new file mode 100644 index 000000000000..178e3bcdbbf7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_CLK_GP10B_H__ +#define __NVKM_CLK_GP10B_H__ + +struct gp10b_clk { + /* currently applied parameters */ + struct nvkm_clk base; + struct gk20a_devfreq *devfreq; + struct clk *clk; + u32 rate; + + /* new parameters to apply */ + u32 new_rate; +}; + +#define gp10b_clk(p) container_of((p), struct gp10b_clk, base) + +#endif diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 4dd05bc732da..195715b162e3 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -77,7 +77,6 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, struct omap_dss_device *output = omap_encoder->output; struct drm_device *dev = encoder->dev; struct drm_connector *connector; - struct drm_bridge *bridge; struct videomode vm = { 0 }; u32 bus_flags; @@ -97,8 +96,7 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder, * * A better solution is to use DRM's bus-flags through the whole driver. */ - for (bridge = output->bridge; bridge; - bridge = drm_bridge_get_next_bridge(bridge)) { + drm_for_each_bridge_in_chain_from(output->bridge, bridge) { if (!bridge->timings) continue; diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 381552bfb409..78563a8d8732 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -10,6 +10,7 @@ #include <linux/spinlock.h> #include <linux/vmalloc.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_prime.h> #include <drm/drm_vma_manager.h> @@ -580,15 +581,13 @@ static int omap_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - union omap_gem_size gsize; - - args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - - args->size = PAGE_ALIGN(args->pitch * args->height); + union omap_gem_size gsize = { }; + int ret; - gsize = (union omap_gem_size){ - .bytes = args->size, - }; + ret = drm_mode_size_dumb(dev, args, SZ_8, 0); + if (ret) + return ret; + gsize.bytes = args->size; return omap_gem_new_handle(dev, file, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC, &args->handle); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 407c5f6a268b..045ffb2ccd0f 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -888,6 +888,21 @@ config DRM_PANEL_SEIKO_43WVF1G Say Y here if you want to enable support for the Seiko 43WVF1G controller for 800x480 LCD panels +config DRM_PANEL_SHARP_LQ079L1SX01 + tristate "Sharp LQ079L1SX01 panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select VIDEOMODE_HELPERS + help + Say Y here if you want to enable support for Sharp LQ079L1SX01 + TFT-LCD modules. The panel has a 2560x1600 resolution and uses + 24 bit RGB per pixel. It provides a dual MIPI DSI interface to + the host. + + To compile this driver as a module, choose M here: the module + will be called panel-sharp-lq079l1sx01. + config DRM_PANEL_SHARP_LQ101R1SX01 tristate "Sharp LQ101R1SX01 panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 3615a761b44f..0356775a443a 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA5X01_AMS561RA01) += panel-samsung-s6e8aa5x01-ams561ra01.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o +obj-$(CONFIG_DRM_PANEL_SHARP_LQ079L1SX01) += panel-sharp-lq079l1sx01.o obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 62435e3cd9f4..ad6b03b59b52 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1909,6 +1909,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x8bba, &delay_200_500_e50, "B140UAN08.5"), EDP_PANEL_ENTRY('A', 'U', 'O', 0xa199, &delay_200_500_e50, "B116XAN06.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0xa7b3, &delay_200_500_e50, "B140UAN04.4"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0xb7a9, &delay_200_500_e50, "B140HAK03.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0xc4b4, &delay_200_500_e50, "B116XAT04.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0xc9a8, &delay_200_500_e50, "B140QAN08.H"), EDP_PANEL_ENTRY('A', 'U', 'O', 0xcdba, &delay_200_500_e50, "B140UAX01.2"), @@ -1974,6 +1975,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c20, &delay_200_500_e80, "NT140FHM-N47"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c93, &delay_200_500_e200, "Unknown"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cb6, &delay_200_500_e200, "NT116WHM-N44"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cf2, &delay_200_500_e200, "NV156FHM-N4S"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cf6, &delay_200_500_e200, "NV140WUM-N64"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d45, &delay_200_500_e80, "NV116WHM-N4B"), @@ -2007,10 +2009,12 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'M', 'N', 0x1441, &delay_200_500_e80_d50, "N140JCA-ELK"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x144f, &delay_200_500_e80_d50, "N140HGA-EA1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1468, &delay_200_500_e80, "N140HGA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x148f, &delay_200_500_e80, "N140HCA-EAC"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x14a8, &delay_200_500_e80, "N140JCA-ELP"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d6, &delay_200_500_e80_d50, "N140BGA-EA4"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x14e5, &delay_200_500_e80_d50, "N140HGA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x1565, &delay_200_500_e80, "N156HCA-EAB"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x162b, &delay_200_500_e80_d50, "N160JCE-ELL"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x7402, &delay_200_500_e200_d50, "N116BCA-EAK"), @@ -2022,10 +2026,12 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'S', 'W', 0x1104, &delay_200_500_e50_d100, "MNB601LS1-4"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x143f, &delay_200_500_e50, "MNE007QS3-6"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1448, &delay_200_500_e50, "MNE007QS3-7"), + EDP_PANEL_ENTRY('C', 'S', 'W', 0x144b, &delay_200_500_e80, "MNE001BS1-4"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1457, &delay_80_500_e80_p2e200, "MNE007QS3-8"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1462, &delay_200_500_e50, "MNE007QS5-2"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1468, &delay_200_500_e50, "MNE007QB2-2"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"), + EDP_PANEL_ENTRY('C', 'S', 'W', 0x1519, &delay_200_500_e80_d50, "MNF601BS1-3"), EDP_PANEL_ENTRY('E', 'T', 'C', 0x0000, &delay_50_500_e200_d200_po2e335, "LP079QX1-SP0V"), @@ -2046,6 +2052,8 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('K', 'D', 'B', 0x1212, &delay_200_500_e50, "KD116N0930A16"), EDP_PANEL_ENTRY('K', 'D', 'B', 0x1707, &delay_200_150_e50, "KD116N2130B12"), + EDP_PANEL_ENTRY('K', 'D', 'C', 0x0110, &delay_200_500_e50, "KD116N3730A07"), + EDP_PANEL_ENTRY('K', 'D', 'C', 0x0397, &delay_200_500_e50, "KD116N3730A12"), EDP_PANEL_ENTRY('K', 'D', 'C', 0x044f, &delay_200_500_e50, "KD116N9-30NH-F3"), EDP_PANEL_ENTRY('K', 'D', 'C', 0x05f1, &delay_200_500_e80_d50, "KD116N5-30NV-G7"), EDP_PANEL_ENTRY('K', 'D', 'C', 0x0809, &delay_200_500_e50, "KD116N2930A15"), diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index ad4993b2f92a..7ecb81225981 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -100,7 +100,7 @@ static const struct ili9881c_instr lhr050h41_init[] = { ILI9881C_COMMAND_INSTR(0x13, 0x00), ILI9881C_COMMAND_INSTR(0x14, 0x00), ILI9881C_COMMAND_INSTR(0x15, 0x00), - ILI9881C_COMMAND_INSTR(0x16, 0x0C), + ILI9881C_COMMAND_INSTR(0x16, 0x0c), ILI9881C_COMMAND_INSTR(0x17, 0x00), ILI9881C_COMMAND_INSTR(0x18, 0x00), ILI9881C_COMMAND_INSTR(0x19, 0x00), @@ -108,7 +108,7 @@ static const struct ili9881c_instr lhr050h41_init[] = { ILI9881C_COMMAND_INSTR(0x1b, 0x00), ILI9881C_COMMAND_INSTR(0x1c, 0x00), ILI9881C_COMMAND_INSTR(0x1d, 0x00), - ILI9881C_COMMAND_INSTR(0x1e, 0xC0), + ILI9881C_COMMAND_INSTR(0x1e, 0xc0), ILI9881C_COMMAND_INSTR(0x1f, 0x80), ILI9881C_COMMAND_INSTR(0x20, 0x04), ILI9881C_COMMAND_INSTR(0x21, 0x01), @@ -134,7 +134,7 @@ static const struct ili9881c_instr lhr050h41_init[] = { ILI9881C_COMMAND_INSTR(0x35, 0x00), ILI9881C_COMMAND_INSTR(0x36, 0x00), ILI9881C_COMMAND_INSTR(0x37, 0x00), - ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x38, 0x3c), ILI9881C_COMMAND_INSTR(0x39, 0x00), ILI9881C_COMMAND_INSTR(0x3a, 0x00), ILI9881C_COMMAND_INSTR(0x3b, 0x00), @@ -173,11 +173,11 @@ static const struct ili9881c_instr lhr050h41_init[] = { ILI9881C_COMMAND_INSTR(0x67, 0x02), ILI9881C_COMMAND_INSTR(0x68, 0x02), ILI9881C_COMMAND_INSTR(0x69, 0x02), - ILI9881C_COMMAND_INSTR(0x6a, 0x0C), + ILI9881C_COMMAND_INSTR(0x6a, 0x0c), ILI9881C_COMMAND_INSTR(0x6b, 0x02), - ILI9881C_COMMAND_INSTR(0x6c, 0x0F), - ILI9881C_COMMAND_INSTR(0x6d, 0x0E), - ILI9881C_COMMAND_INSTR(0x6e, 0x0D), + ILI9881C_COMMAND_INSTR(0x6c, 0x0f), + ILI9881C_COMMAND_INSTR(0x6d, 0x0e), + ILI9881C_COMMAND_INSTR(0x6e, 0x0d), ILI9881C_COMMAND_INSTR(0x6f, 0x06), ILI9881C_COMMAND_INSTR(0x70, 0x07), ILI9881C_COMMAND_INSTR(0x71, 0x02), @@ -195,74 +195,74 @@ static const struct ili9881c_instr lhr050h41_init[] = { ILI9881C_COMMAND_INSTR(0x7d, 0x02), ILI9881C_COMMAND_INSTR(0x7e, 0x02), ILI9881C_COMMAND_INSTR(0x7f, 0x02), - ILI9881C_COMMAND_INSTR(0x80, 0x0C), + ILI9881C_COMMAND_INSTR(0x80, 0x0c), ILI9881C_COMMAND_INSTR(0x81, 0x02), - ILI9881C_COMMAND_INSTR(0x82, 0x0F), - ILI9881C_COMMAND_INSTR(0x83, 0x0E), - ILI9881C_COMMAND_INSTR(0x84, 0x0D), + ILI9881C_COMMAND_INSTR(0x82, 0x0f), + ILI9881C_COMMAND_INSTR(0x83, 0x0e), + ILI9881C_COMMAND_INSTR(0x84, 0x0d), ILI9881C_COMMAND_INSTR(0x85, 0x06), ILI9881C_COMMAND_INSTR(0x86, 0x07), ILI9881C_COMMAND_INSTR(0x87, 0x02), ILI9881C_COMMAND_INSTR(0x88, 0x02), ILI9881C_COMMAND_INSTR(0x89, 0x02), - ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), ILI9881C_SWITCH_PAGE_INSTR(4), - ILI9881C_COMMAND_INSTR(0x6C, 0x15), - ILI9881C_COMMAND_INSTR(0x6E, 0x22), - ILI9881C_COMMAND_INSTR(0x6F, 0x33), - ILI9881C_COMMAND_INSTR(0x3A, 0xA4), - ILI9881C_COMMAND_INSTR(0x8D, 0x0D), - ILI9881C_COMMAND_INSTR(0x87, 0xBA), + ILI9881C_COMMAND_INSTR(0x6c, 0x15), + ILI9881C_COMMAND_INSTR(0x6e, 0x22), + ILI9881C_COMMAND_INSTR(0x6f, 0x33), + ILI9881C_COMMAND_INSTR(0x3a, 0xa4), + ILI9881C_COMMAND_INSTR(0x8d, 0x0d), + ILI9881C_COMMAND_INSTR(0x87, 0xba), ILI9881C_COMMAND_INSTR(0x26, 0x76), - ILI9881C_COMMAND_INSTR(0xB2, 0xD1), + ILI9881C_COMMAND_INSTR(0xb2, 0xd1), ILI9881C_SWITCH_PAGE_INSTR(1), - ILI9881C_COMMAND_INSTR(0x22, 0x0A), - ILI9881C_COMMAND_INSTR(0x53, 0xDC), - ILI9881C_COMMAND_INSTR(0x55, 0xA7), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), + ILI9881C_COMMAND_INSTR(0x53, 0xdc), + ILI9881C_COMMAND_INSTR(0x55, 0xa7), ILI9881C_COMMAND_INSTR(0x50, 0x78), ILI9881C_COMMAND_INSTR(0x51, 0x78), ILI9881C_COMMAND_INSTR(0x31, 0x02), ILI9881C_COMMAND_INSTR(0x60, 0x14), - ILI9881C_COMMAND_INSTR(0xA0, 0x2A), - ILI9881C_COMMAND_INSTR(0xA1, 0x39), - ILI9881C_COMMAND_INSTR(0xA2, 0x46), - ILI9881C_COMMAND_INSTR(0xA3, 0x0e), - ILI9881C_COMMAND_INSTR(0xA4, 0x12), - ILI9881C_COMMAND_INSTR(0xA5, 0x25), - ILI9881C_COMMAND_INSTR(0xA6, 0x19), - ILI9881C_COMMAND_INSTR(0xA7, 0x1d), - ILI9881C_COMMAND_INSTR(0xA8, 0xa6), - ILI9881C_COMMAND_INSTR(0xA9, 0x1C), - ILI9881C_COMMAND_INSTR(0xAA, 0x29), - ILI9881C_COMMAND_INSTR(0xAB, 0x85), - ILI9881C_COMMAND_INSTR(0xAC, 0x1C), - ILI9881C_COMMAND_INSTR(0xAD, 0x1B), - ILI9881C_COMMAND_INSTR(0xAE, 0x51), - ILI9881C_COMMAND_INSTR(0xAF, 0x22), - ILI9881C_COMMAND_INSTR(0xB0, 0x2d), - ILI9881C_COMMAND_INSTR(0xB1, 0x4f), - ILI9881C_COMMAND_INSTR(0xB2, 0x59), - ILI9881C_COMMAND_INSTR(0xB3, 0x3F), - ILI9881C_COMMAND_INSTR(0xC0, 0x2A), - ILI9881C_COMMAND_INSTR(0xC1, 0x3a), - ILI9881C_COMMAND_INSTR(0xC2, 0x45), - ILI9881C_COMMAND_INSTR(0xC3, 0x0e), - ILI9881C_COMMAND_INSTR(0xC4, 0x11), - ILI9881C_COMMAND_INSTR(0xC5, 0x24), - ILI9881C_COMMAND_INSTR(0xC6, 0x1a), - ILI9881C_COMMAND_INSTR(0xC7, 0x1c), - ILI9881C_COMMAND_INSTR(0xC8, 0xaa), - ILI9881C_COMMAND_INSTR(0xC9, 0x1C), - ILI9881C_COMMAND_INSTR(0xCA, 0x29), - ILI9881C_COMMAND_INSTR(0xCB, 0x96), - ILI9881C_COMMAND_INSTR(0xCC, 0x1C), - ILI9881C_COMMAND_INSTR(0xCD, 0x1B), - ILI9881C_COMMAND_INSTR(0xCE, 0x51), - ILI9881C_COMMAND_INSTR(0xCF, 0x22), - ILI9881C_COMMAND_INSTR(0xD0, 0x2b), - ILI9881C_COMMAND_INSTR(0xD1, 0x4b), - ILI9881C_COMMAND_INSTR(0xD2, 0x59), - ILI9881C_COMMAND_INSTR(0xD3, 0x3F), + ILI9881C_COMMAND_INSTR(0xa0, 0x2a), + ILI9881C_COMMAND_INSTR(0xa1, 0x39), + ILI9881C_COMMAND_INSTR(0xa2, 0x46), + ILI9881C_COMMAND_INSTR(0xa3, 0x0e), + ILI9881C_COMMAND_INSTR(0xa4, 0x12), + ILI9881C_COMMAND_INSTR(0xa5, 0x25), + ILI9881C_COMMAND_INSTR(0xa6, 0x19), + ILI9881C_COMMAND_INSTR(0xa7, 0x1d), + ILI9881C_COMMAND_INSTR(0xa8, 0xa6), + ILI9881C_COMMAND_INSTR(0xa9, 0x1c), + ILI9881C_COMMAND_INSTR(0xaa, 0x29), + ILI9881C_COMMAND_INSTR(0xab, 0x85), + ILI9881C_COMMAND_INSTR(0xac, 0x1c), + ILI9881C_COMMAND_INSTR(0xad, 0x1b), + ILI9881C_COMMAND_INSTR(0xae, 0x51), + ILI9881C_COMMAND_INSTR(0xaf, 0x22), + ILI9881C_COMMAND_INSTR(0xb0, 0x2d), + ILI9881C_COMMAND_INSTR(0xb1, 0x4f), + ILI9881C_COMMAND_INSTR(0xb2, 0x59), + ILI9881C_COMMAND_INSTR(0xb3, 0x3f), + ILI9881C_COMMAND_INSTR(0xc0, 0x2a), + ILI9881C_COMMAND_INSTR(0xc1, 0x3a), + ILI9881C_COMMAND_INSTR(0xc2, 0x45), + ILI9881C_COMMAND_INSTR(0xc3, 0x0e), + ILI9881C_COMMAND_INSTR(0xc4, 0x11), + ILI9881C_COMMAND_INSTR(0xc5, 0x24), + ILI9881C_COMMAND_INSTR(0xc6, 0x1a), + ILI9881C_COMMAND_INSTR(0xc7, 0x1c), + ILI9881C_COMMAND_INSTR(0xc8, 0xaa), + ILI9881C_COMMAND_INSTR(0xc9, 0x1c), + ILI9881C_COMMAND_INSTR(0xca, 0x29), + ILI9881C_COMMAND_INSTR(0xcb, 0x96), + ILI9881C_COMMAND_INSTR(0xcc, 0x1c), + ILI9881C_COMMAND_INSTR(0xcd, 0x1b), + ILI9881C_COMMAND_INSTR(0xce, 0x51), + ILI9881C_COMMAND_INSTR(0xcf, 0x22), + ILI9881C_COMMAND_INSTR(0xd0, 0x2b), + ILI9881C_COMMAND_INSTR(0xd1, 0x4b), + ILI9881C_COMMAND_INSTR(0xd2, 0x59), + ILI9881C_COMMAND_INSTR(0xd3, 0x3f), }; static const struct ili9881c_instr k101_im2byl02_init[] = { @@ -276,12 +276,12 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0x07, 0x00), ILI9881C_COMMAND_INSTR(0x08, 0x00), ILI9881C_COMMAND_INSTR(0x09, 0x00), - ILI9881C_COMMAND_INSTR(0x0A, 0x01), - ILI9881C_COMMAND_INSTR(0x0B, 0x01), - ILI9881C_COMMAND_INSTR(0x0C, 0x00), - ILI9881C_COMMAND_INSTR(0x0D, 0x01), - ILI9881C_COMMAND_INSTR(0x0E, 0x01), - ILI9881C_COMMAND_INSTR(0x0F, 0x00), + ILI9881C_COMMAND_INSTR(0x0a, 0x01), + ILI9881C_COMMAND_INSTR(0x0b, 0x01), + ILI9881C_COMMAND_INSTR(0x0c, 0x00), + ILI9881C_COMMAND_INSTR(0x0d, 0x01), + ILI9881C_COMMAND_INSTR(0x0e, 0x01), + ILI9881C_COMMAND_INSTR(0x0f, 0x00), ILI9881C_COMMAND_INSTR(0x10, 0x00), ILI9881C_COMMAND_INSTR(0x11, 0x00), ILI9881C_COMMAND_INSTR(0x12, 0x00), @@ -292,12 +292,12 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0x17, 0x00), ILI9881C_COMMAND_INSTR(0x18, 0x00), ILI9881C_COMMAND_INSTR(0x19, 0x00), - ILI9881C_COMMAND_INSTR(0x1A, 0x00), - ILI9881C_COMMAND_INSTR(0x1B, 0x00), - ILI9881C_COMMAND_INSTR(0x1C, 0x00), - ILI9881C_COMMAND_INSTR(0x1D, 0x00), - ILI9881C_COMMAND_INSTR(0x1E, 0x40), - ILI9881C_COMMAND_INSTR(0x1F, 0xC0), + ILI9881C_COMMAND_INSTR(0x1a, 0x00), + ILI9881C_COMMAND_INSTR(0x1b, 0x00), + ILI9881C_COMMAND_INSTR(0x1c, 0x00), + ILI9881C_COMMAND_INSTR(0x1d, 0x00), + ILI9881C_COMMAND_INSTR(0x1e, 0x40), + ILI9881C_COMMAND_INSTR(0x1f, 0xc0), ILI9881C_COMMAND_INSTR(0x20, 0x06), ILI9881C_COMMAND_INSTR(0x21, 0x01), ILI9881C_COMMAND_INSTR(0x22, 0x06), @@ -306,14 +306,14 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0x25, 0x88), ILI9881C_COMMAND_INSTR(0x26, 0x00), ILI9881C_COMMAND_INSTR(0x27, 0x00), - ILI9881C_COMMAND_INSTR(0x28, 0x3B), + ILI9881C_COMMAND_INSTR(0x28, 0x3b), ILI9881C_COMMAND_INSTR(0x29, 0x03), - ILI9881C_COMMAND_INSTR(0x2A, 0x00), - ILI9881C_COMMAND_INSTR(0x2B, 0x00), - ILI9881C_COMMAND_INSTR(0x2C, 0x00), - ILI9881C_COMMAND_INSTR(0x2D, 0x00), - ILI9881C_COMMAND_INSTR(0x2E, 0x00), - ILI9881C_COMMAND_INSTR(0x2F, 0x00), + ILI9881C_COMMAND_INSTR(0x2a, 0x00), + ILI9881C_COMMAND_INSTR(0x2b, 0x00), + ILI9881C_COMMAND_INSTR(0x2c, 0x00), + ILI9881C_COMMAND_INSTR(0x2d, 0x00), + ILI9881C_COMMAND_INSTR(0x2e, 0x00), + ILI9881C_COMMAND_INSTR(0x2f, 0x00), ILI9881C_COMMAND_INSTR(0x30, 0x00), ILI9881C_COMMAND_INSTR(0x31, 0x00), ILI9881C_COMMAND_INSTR(0x32, 0x00), @@ -324,12 +324,12 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0x37, 0x00), ILI9881C_COMMAND_INSTR(0x38, 0x00), ILI9881C_COMMAND_INSTR(0x39, 0x00), - ILI9881C_COMMAND_INSTR(0x3A, 0x00), - ILI9881C_COMMAND_INSTR(0x3B, 0x00), - ILI9881C_COMMAND_INSTR(0x3C, 0x00), - ILI9881C_COMMAND_INSTR(0x3D, 0x00), - ILI9881C_COMMAND_INSTR(0x3E, 0x00), - ILI9881C_COMMAND_INSTR(0x3F, 0x00), + ILI9881C_COMMAND_INSTR(0x3a, 0x00), + ILI9881C_COMMAND_INSTR(0x3b, 0x00), + ILI9881C_COMMAND_INSTR(0x3c, 0x00), + ILI9881C_COMMAND_INSTR(0x3d, 0x00), + ILI9881C_COMMAND_INSTR(0x3e, 0x00), + ILI9881C_COMMAND_INSTR(0x3f, 0x00), ILI9881C_COMMAND_INSTR(0x40, 0x00), ILI9881C_COMMAND_INSTR(0x41, 0x00), ILI9881C_COMMAND_INSTR(0x42, 0x00), @@ -340,17 +340,17 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0x52, 0x45), ILI9881C_COMMAND_INSTR(0x53, 0x67), ILI9881C_COMMAND_INSTR(0x54, 0x89), - ILI9881C_COMMAND_INSTR(0x55, 0xAB), + ILI9881C_COMMAND_INSTR(0x55, 0xab), ILI9881C_COMMAND_INSTR(0x56, 0x01), ILI9881C_COMMAND_INSTR(0x57, 0x23), ILI9881C_COMMAND_INSTR(0x58, 0x45), ILI9881C_COMMAND_INSTR(0x59, 0x67), - ILI9881C_COMMAND_INSTR(0x5A, 0x89), - ILI9881C_COMMAND_INSTR(0x5B, 0xAB), - ILI9881C_COMMAND_INSTR(0x5C, 0xCD), - ILI9881C_COMMAND_INSTR(0x5D, 0xEF), - ILI9881C_COMMAND_INSTR(0x5E, 0x00), - ILI9881C_COMMAND_INSTR(0x5F, 0x01), + ILI9881C_COMMAND_INSTR(0x5a, 0x89), + ILI9881C_COMMAND_INSTR(0x5b, 0xab), + ILI9881C_COMMAND_INSTR(0x5c, 0xcd), + ILI9881C_COMMAND_INSTR(0x5d, 0xef), + ILI9881C_COMMAND_INSTR(0x5e, 0x00), + ILI9881C_COMMAND_INSTR(0x5f, 0x01), ILI9881C_COMMAND_INSTR(0x60, 0x01), ILI9881C_COMMAND_INSTR(0x61, 0x06), ILI9881C_COMMAND_INSTR(0x62, 0x06), @@ -361,101 +361,101 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0x67, 0x02), ILI9881C_COMMAND_INSTR(0x68, 0x02), ILI9881C_COMMAND_INSTR(0x69, 0x05), - ILI9881C_COMMAND_INSTR(0x6A, 0x05), - ILI9881C_COMMAND_INSTR(0x6B, 0x02), - ILI9881C_COMMAND_INSTR(0x6C, 0x0D), - ILI9881C_COMMAND_INSTR(0x6D, 0x0D), - ILI9881C_COMMAND_INSTR(0x6E, 0x0C), - ILI9881C_COMMAND_INSTR(0x6F, 0x0C), - ILI9881C_COMMAND_INSTR(0x70, 0x0F), - ILI9881C_COMMAND_INSTR(0x71, 0x0F), - ILI9881C_COMMAND_INSTR(0x72, 0x0E), - ILI9881C_COMMAND_INSTR(0x73, 0x0E), + ILI9881C_COMMAND_INSTR(0x6a, 0x05), + ILI9881C_COMMAND_INSTR(0x6b, 0x02), + ILI9881C_COMMAND_INSTR(0x6c, 0x0d), + ILI9881C_COMMAND_INSTR(0x6d, 0x0d), + ILI9881C_COMMAND_INSTR(0x6e, 0x0c), + ILI9881C_COMMAND_INSTR(0x6f, 0x0c), + ILI9881C_COMMAND_INSTR(0x70, 0x0f), + ILI9881C_COMMAND_INSTR(0x71, 0x0f), + ILI9881C_COMMAND_INSTR(0x72, 0x0e), + ILI9881C_COMMAND_INSTR(0x73, 0x0e), ILI9881C_COMMAND_INSTR(0x74, 0x02), ILI9881C_COMMAND_INSTR(0x75, 0x01), ILI9881C_COMMAND_INSTR(0x76, 0x01), ILI9881C_COMMAND_INSTR(0x77, 0x06), ILI9881C_COMMAND_INSTR(0x78, 0x06), ILI9881C_COMMAND_INSTR(0x79, 0x07), - ILI9881C_COMMAND_INSTR(0x7A, 0x07), - ILI9881C_COMMAND_INSTR(0x7B, 0x00), - ILI9881C_COMMAND_INSTR(0x7C, 0x00), - ILI9881C_COMMAND_INSTR(0x7D, 0x02), - ILI9881C_COMMAND_INSTR(0x7E, 0x02), - ILI9881C_COMMAND_INSTR(0x7F, 0x05), + ILI9881C_COMMAND_INSTR(0x7a, 0x07), + ILI9881C_COMMAND_INSTR(0x7b, 0x00), + ILI9881C_COMMAND_INSTR(0x7c, 0x00), + ILI9881C_COMMAND_INSTR(0x7d, 0x02), + ILI9881C_COMMAND_INSTR(0x7e, 0x02), + ILI9881C_COMMAND_INSTR(0x7f, 0x05), ILI9881C_COMMAND_INSTR(0x80, 0x05), ILI9881C_COMMAND_INSTR(0x81, 0x02), - ILI9881C_COMMAND_INSTR(0x82, 0x0D), - ILI9881C_COMMAND_INSTR(0x83, 0x0D), - ILI9881C_COMMAND_INSTR(0x84, 0x0C), - ILI9881C_COMMAND_INSTR(0x85, 0x0C), - ILI9881C_COMMAND_INSTR(0x86, 0x0F), - ILI9881C_COMMAND_INSTR(0x87, 0x0F), - ILI9881C_COMMAND_INSTR(0x88, 0x0E), - ILI9881C_COMMAND_INSTR(0x89, 0x0E), - ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_COMMAND_INSTR(0x82, 0x0d), + ILI9881C_COMMAND_INSTR(0x83, 0x0d), + ILI9881C_COMMAND_INSTR(0x84, 0x0c), + ILI9881C_COMMAND_INSTR(0x85, 0x0c), + ILI9881C_COMMAND_INSTR(0x86, 0x0f), + ILI9881C_COMMAND_INSTR(0x87, 0x0f), + ILI9881C_COMMAND_INSTR(0x88, 0x0e), + ILI9881C_COMMAND_INSTR(0x89, 0x0e), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), ILI9881C_SWITCH_PAGE_INSTR(4), - ILI9881C_COMMAND_INSTR(0x3B, 0xC0), /* ILI4003D sel */ - ILI9881C_COMMAND_INSTR(0x6C, 0x15), /* Set VCORE voltage = 1.5V */ - ILI9881C_COMMAND_INSTR(0x6E, 0x2A), /* di_pwr_reg=0 for power mode 2A, VGH clamp 18V */ - ILI9881C_COMMAND_INSTR(0x6F, 0x33), /* pumping ratio VGH=5x VGL=-3x */ - ILI9881C_COMMAND_INSTR(0x8D, 0x1B), /* VGL clamp -10V */ - ILI9881C_COMMAND_INSTR(0x87, 0xBA), /* ESD */ - ILI9881C_COMMAND_INSTR(0x3A, 0x24), /* POWER SAVING */ + ILI9881C_COMMAND_INSTR(0x3b, 0xc0), /* ILI4003D sel */ + ILI9881C_COMMAND_INSTR(0x6c, 0x15), /* Set VCORE voltage = 1.5V */ + ILI9881C_COMMAND_INSTR(0x6e, 0x2a), /* di_pwr_reg=0 for power mode 2A, VGH clamp 18V */ + ILI9881C_COMMAND_INSTR(0x6f, 0x33), /* pumping ratio VGH=5x VGL=-3x */ + ILI9881C_COMMAND_INSTR(0x8d, 0x1b), /* VGL clamp -10V */ + ILI9881C_COMMAND_INSTR(0x87, 0xba), /* ESD */ + ILI9881C_COMMAND_INSTR(0x3a, 0x24), /* POWER SAVING */ ILI9881C_COMMAND_INSTR(0x26, 0x76), - ILI9881C_COMMAND_INSTR(0xB2, 0xD1), + ILI9881C_COMMAND_INSTR(0xb2, 0xd1), ILI9881C_SWITCH_PAGE_INSTR(1), - ILI9881C_COMMAND_INSTR(0x22, 0x0A), /* BGR, SS */ + ILI9881C_COMMAND_INSTR(0x22, 0x0a), /* BGR, SS */ ILI9881C_COMMAND_INSTR(0x31, 0x00), /* Zigzag type3 inversion */ ILI9881C_COMMAND_INSTR(0x40, 0x53), /* ILI4003D sel */ ILI9881C_COMMAND_INSTR(0x43, 0x66), - ILI9881C_COMMAND_INSTR(0x53, 0x4C), + ILI9881C_COMMAND_INSTR(0x53, 0x4c), ILI9881C_COMMAND_INSTR(0x50, 0x87), ILI9881C_COMMAND_INSTR(0x51, 0x82), ILI9881C_COMMAND_INSTR(0x60, 0x15), ILI9881C_COMMAND_INSTR(0x61, 0x01), - ILI9881C_COMMAND_INSTR(0x62, 0x0C), + ILI9881C_COMMAND_INSTR(0x62, 0x0c), ILI9881C_COMMAND_INSTR(0x63, 0x00), - ILI9881C_COMMAND_INSTR(0xA0, 0x00), - ILI9881C_COMMAND_INSTR(0xA1, 0x13), /* VP251 */ - ILI9881C_COMMAND_INSTR(0xA2, 0x23), /* VP247 */ - ILI9881C_COMMAND_INSTR(0xA3, 0x14), /* VP243 */ - ILI9881C_COMMAND_INSTR(0xA4, 0x16), /* VP239 */ - ILI9881C_COMMAND_INSTR(0xA5, 0x29), /* VP231 */ - ILI9881C_COMMAND_INSTR(0xA6, 0x1E), /* VP219 */ - ILI9881C_COMMAND_INSTR(0xA7, 0x1D), /* VP203 */ - ILI9881C_COMMAND_INSTR(0xA8, 0x86), /* VP175 */ - ILI9881C_COMMAND_INSTR(0xA9, 0x1E), /* VP144 */ - ILI9881C_COMMAND_INSTR(0xAA, 0x29), /* VP111 */ - ILI9881C_COMMAND_INSTR(0xAB, 0x74), /* VP80 */ - ILI9881C_COMMAND_INSTR(0xAC, 0x19), /* VP52 */ - ILI9881C_COMMAND_INSTR(0xAD, 0x17), /* VP36 */ - ILI9881C_COMMAND_INSTR(0xAE, 0x4B), /* VP24 */ - ILI9881C_COMMAND_INSTR(0xAF, 0x20), /* VP16 */ - ILI9881C_COMMAND_INSTR(0xB0, 0x26), /* VP12 */ - ILI9881C_COMMAND_INSTR(0xB1, 0x4C), /* VP8 */ - ILI9881C_COMMAND_INSTR(0xB2, 0x5D), /* VP4 */ - ILI9881C_COMMAND_INSTR(0xB3, 0x3F), /* VP0 */ - ILI9881C_COMMAND_INSTR(0xC0, 0x00), /* VN255 GAMMA N */ - ILI9881C_COMMAND_INSTR(0xC1, 0x13), /* VN251 */ - ILI9881C_COMMAND_INSTR(0xC2, 0x23), /* VN247 */ - ILI9881C_COMMAND_INSTR(0xC3, 0x14), /* VN243 */ - ILI9881C_COMMAND_INSTR(0xC4, 0x16), /* VN239 */ - ILI9881C_COMMAND_INSTR(0xC5, 0x29), /* VN231 */ - ILI9881C_COMMAND_INSTR(0xC6, 0x1E), /* VN219 */ - ILI9881C_COMMAND_INSTR(0xC7, 0x1D), /* VN203 */ - ILI9881C_COMMAND_INSTR(0xC8, 0x86), /* VN175 */ - ILI9881C_COMMAND_INSTR(0xC9, 0x1E), /* VN144 */ - ILI9881C_COMMAND_INSTR(0xCA, 0x29), /* VN111 */ - ILI9881C_COMMAND_INSTR(0xCB, 0x74), /* VN80 */ - ILI9881C_COMMAND_INSTR(0xCC, 0x19), /* VN52 */ - ILI9881C_COMMAND_INSTR(0xCD, 0x17), /* VN36 */ - ILI9881C_COMMAND_INSTR(0xCE, 0x4B), /* VN24 */ - ILI9881C_COMMAND_INSTR(0xCF, 0x20), /* VN16 */ - ILI9881C_COMMAND_INSTR(0xD0, 0x26), /* VN12 */ - ILI9881C_COMMAND_INSTR(0xD1, 0x4C), /* VN8 */ - ILI9881C_COMMAND_INSTR(0xD2, 0x5D), /* VN4 */ - ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */ + ILI9881C_COMMAND_INSTR(0xa0, 0x00), + ILI9881C_COMMAND_INSTR(0xa1, 0x13), /* VP251 */ + ILI9881C_COMMAND_INSTR(0xa2, 0x23), /* VP247 */ + ILI9881C_COMMAND_INSTR(0xa3, 0x14), /* VP243 */ + ILI9881C_COMMAND_INSTR(0xa4, 0x16), /* VP239 */ + ILI9881C_COMMAND_INSTR(0xa5, 0x29), /* VP231 */ + ILI9881C_COMMAND_INSTR(0xa6, 0x1e), /* VP219 */ + ILI9881C_COMMAND_INSTR(0xa7, 0x1d), /* VP203 */ + ILI9881C_COMMAND_INSTR(0xa8, 0x86), /* VP175 */ + ILI9881C_COMMAND_INSTR(0xa9, 0x1e), /* VP144 */ + ILI9881C_COMMAND_INSTR(0xaa, 0x29), /* VP111 */ + ILI9881C_COMMAND_INSTR(0xab, 0x74), /* VP80 */ + ILI9881C_COMMAND_INSTR(0xac, 0x19), /* VP52 */ + ILI9881C_COMMAND_INSTR(0xad, 0x17), /* VP36 */ + ILI9881C_COMMAND_INSTR(0xae, 0x4b), /* VP24 */ + ILI9881C_COMMAND_INSTR(0xaf, 0x20), /* VP16 */ + ILI9881C_COMMAND_INSTR(0xb0, 0x26), /* VP12 */ + ILI9881C_COMMAND_INSTR(0xb1, 0x4c), /* VP8 */ + ILI9881C_COMMAND_INSTR(0xb2, 0x5d), /* VP4 */ + ILI9881C_COMMAND_INSTR(0xb3, 0x3f), /* VP0 */ + ILI9881C_COMMAND_INSTR(0xc0, 0x00), /* VN255 GAMMA N */ + ILI9881C_COMMAND_INSTR(0xc1, 0x13), /* VN251 */ + ILI9881C_COMMAND_INSTR(0xc2, 0x23), /* VN247 */ + ILI9881C_COMMAND_INSTR(0xc3, 0x14), /* VN243 */ + ILI9881C_COMMAND_INSTR(0xc4, 0x16), /* VN239 */ + ILI9881C_COMMAND_INSTR(0xc5, 0x29), /* VN231 */ + ILI9881C_COMMAND_INSTR(0xc6, 0x1e), /* VN219 */ + ILI9881C_COMMAND_INSTR(0xc7, 0x1d), /* VN203 */ + ILI9881C_COMMAND_INSTR(0xc8, 0x86), /* VN175 */ + ILI9881C_COMMAND_INSTR(0xc9, 0x1e), /* VN144 */ + ILI9881C_COMMAND_INSTR(0xca, 0x29), /* VN111 */ + ILI9881C_COMMAND_INSTR(0xcb, 0x74), /* VN80 */ + ILI9881C_COMMAND_INSTR(0xcc, 0x19), /* VN52 */ + ILI9881C_COMMAND_INSTR(0xcd, 0x17), /* VN36 */ + ILI9881C_COMMAND_INSTR(0xce, 0x4b), /* VN24 */ + ILI9881C_COMMAND_INSTR(0xcf, 0x20), /* VN16 */ + ILI9881C_COMMAND_INSTR(0xd0, 0x26), /* VN12 */ + ILI9881C_COMMAND_INSTR(0xd1, 0x4c), /* VN8 */ + ILI9881C_COMMAND_INSTR(0xd2, 0x5d), /* VN4 */ + ILI9881C_COMMAND_INSTR(0xd3, 0x3f), /* VN0 */ }; static const struct ili9881c_instr kd050hdfia020_init[] = { @@ -517,7 +517,7 @@ static const struct ili9881c_instr kd050hdfia020_init[] = { ILI9881C_COMMAND_INSTR(0x35, 0x00), ILI9881C_COMMAND_INSTR(0x36, 0x00), ILI9881C_COMMAND_INSTR(0x37, 0x00), - ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x38, 0x3c), ILI9881C_COMMAND_INSTR(0x39, 0x00), ILI9881C_COMMAND_INSTR(0x3a, 0x40), ILI9881C_COMMAND_INSTR(0x3b, 0x40), @@ -549,10 +549,10 @@ static const struct ili9881c_instr kd050hdfia020_init[] = { ILI9881C_COMMAND_INSTR(0x60, 0x00), ILI9881C_COMMAND_INSTR(0x61, 0x15), ILI9881C_COMMAND_INSTR(0x62, 0x14), - ILI9881C_COMMAND_INSTR(0x63, 0x0E), - ILI9881C_COMMAND_INSTR(0x64, 0x0F), - ILI9881C_COMMAND_INSTR(0x65, 0x0C), - ILI9881C_COMMAND_INSTR(0x66, 0x0D), + ILI9881C_COMMAND_INSTR(0x63, 0x0e), + ILI9881C_COMMAND_INSTR(0x64, 0x0f), + ILI9881C_COMMAND_INSTR(0x65, 0x0c), + ILI9881C_COMMAND_INSTR(0x66, 0x0d), ILI9881C_COMMAND_INSTR(0x67, 0x06), ILI9881C_COMMAND_INSTR(0x68, 0x02), ILI9881C_COMMAND_INSTR(0x69, 0x07), @@ -571,10 +571,10 @@ static const struct ili9881c_instr kd050hdfia020_init[] = { ILI9881C_COMMAND_INSTR(0x76, 0x00), ILI9881C_COMMAND_INSTR(0x77, 0x14), ILI9881C_COMMAND_INSTR(0x78, 0x15), - ILI9881C_COMMAND_INSTR(0x79, 0x0E), - ILI9881C_COMMAND_INSTR(0x7a, 0x0F), - ILI9881C_COMMAND_INSTR(0x7b, 0x0C), - ILI9881C_COMMAND_INSTR(0x7c, 0x0D), + ILI9881C_COMMAND_INSTR(0x79, 0x0e), + ILI9881C_COMMAND_INSTR(0x7a, 0x0f), + ILI9881C_COMMAND_INSTR(0x7b, 0x0c), + ILI9881C_COMMAND_INSTR(0x7c, 0x0d), ILI9881C_COMMAND_INSTR(0x7d, 0x06), ILI9881C_COMMAND_INSTR(0x7e, 0x02), ILI9881C_COMMAND_INSTR(0x7f, 0x07), @@ -587,71 +587,71 @@ static const struct ili9881c_instr kd050hdfia020_init[] = { ILI9881C_COMMAND_INSTR(0x87, 0x02), ILI9881C_COMMAND_INSTR(0x88, 0x02), ILI9881C_COMMAND_INSTR(0x89, 0x02), - ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), ILI9881C_SWITCH_PAGE_INSTR(0x4), - ILI9881C_COMMAND_INSTR(0x6C, 0x15), - ILI9881C_COMMAND_INSTR(0x6E, 0x2A), - ILI9881C_COMMAND_INSTR(0x6F, 0x33), - ILI9881C_COMMAND_INSTR(0x3A, 0x94), - ILI9881C_COMMAND_INSTR(0x8D, 0x15), - ILI9881C_COMMAND_INSTR(0x87, 0xBA), + ILI9881C_COMMAND_INSTR(0x6c, 0x15), + ILI9881C_COMMAND_INSTR(0x6e, 0x2a), + ILI9881C_COMMAND_INSTR(0x6f, 0x33), + ILI9881C_COMMAND_INSTR(0x3a, 0x94), + ILI9881C_COMMAND_INSTR(0x8d, 0x15), + ILI9881C_COMMAND_INSTR(0x87, 0xba), ILI9881C_COMMAND_INSTR(0x26, 0x76), - ILI9881C_COMMAND_INSTR(0xB2, 0xD1), - ILI9881C_COMMAND_INSTR(0xB5, 0x06), + ILI9881C_COMMAND_INSTR(0xb2, 0xd1), + ILI9881C_COMMAND_INSTR(0xb5, 0x06), ILI9881C_SWITCH_PAGE_INSTR(0x1), - ILI9881C_COMMAND_INSTR(0x22, 0x0A), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), ILI9881C_COMMAND_INSTR(0x31, 0x00), ILI9881C_COMMAND_INSTR(0x53, 0x90), - ILI9881C_COMMAND_INSTR(0x55, 0xA2), - ILI9881C_COMMAND_INSTR(0x50, 0xB7), - ILI9881C_COMMAND_INSTR(0x51, 0xB7), + ILI9881C_COMMAND_INSTR(0x55, 0xa2), + ILI9881C_COMMAND_INSTR(0x50, 0xb7), + ILI9881C_COMMAND_INSTR(0x51, 0xb7), ILI9881C_COMMAND_INSTR(0x60, 0x22), ILI9881C_COMMAND_INSTR(0x61, 0x00), ILI9881C_COMMAND_INSTR(0x62, 0x19), ILI9881C_COMMAND_INSTR(0x63, 0x10), - ILI9881C_COMMAND_INSTR(0xA0, 0x08), - ILI9881C_COMMAND_INSTR(0xA1, 0x1A), - ILI9881C_COMMAND_INSTR(0xA2, 0x27), - ILI9881C_COMMAND_INSTR(0xA3, 0x15), - ILI9881C_COMMAND_INSTR(0xA4, 0x17), - ILI9881C_COMMAND_INSTR(0xA5, 0x2A), - ILI9881C_COMMAND_INSTR(0xA6, 0x1E), - ILI9881C_COMMAND_INSTR(0xA7, 0x1F), - ILI9881C_COMMAND_INSTR(0xA8, 0x8B), - ILI9881C_COMMAND_INSTR(0xA9, 0x1B), - ILI9881C_COMMAND_INSTR(0xAA, 0x27), - ILI9881C_COMMAND_INSTR(0xAB, 0x78), - ILI9881C_COMMAND_INSTR(0xAC, 0x18), - ILI9881C_COMMAND_INSTR(0xAD, 0x18), - ILI9881C_COMMAND_INSTR(0xAE, 0x4C), - ILI9881C_COMMAND_INSTR(0xAF, 0x21), - ILI9881C_COMMAND_INSTR(0xB0, 0x27), - ILI9881C_COMMAND_INSTR(0xB1, 0x54), - ILI9881C_COMMAND_INSTR(0xB2, 0x67), - ILI9881C_COMMAND_INSTR(0xB3, 0x39), - ILI9881C_COMMAND_INSTR(0xC0, 0x08), - ILI9881C_COMMAND_INSTR(0xC1, 0x1A), - ILI9881C_COMMAND_INSTR(0xC2, 0x27), - ILI9881C_COMMAND_INSTR(0xC3, 0x15), - ILI9881C_COMMAND_INSTR(0xC4, 0x17), - ILI9881C_COMMAND_INSTR(0xC5, 0x2A), - ILI9881C_COMMAND_INSTR(0xC6, 0x1E), - ILI9881C_COMMAND_INSTR(0xC7, 0x1F), - ILI9881C_COMMAND_INSTR(0xC8, 0x8B), - ILI9881C_COMMAND_INSTR(0xC9, 0x1B), - ILI9881C_COMMAND_INSTR(0xCA, 0x27), - ILI9881C_COMMAND_INSTR(0xCB, 0x78), - ILI9881C_COMMAND_INSTR(0xCC, 0x18), - ILI9881C_COMMAND_INSTR(0xCD, 0x18), - ILI9881C_COMMAND_INSTR(0xCE, 0x4C), - ILI9881C_COMMAND_INSTR(0xCF, 0x21), - ILI9881C_COMMAND_INSTR(0xD0, 0x27), - ILI9881C_COMMAND_INSTR(0xD1, 0x54), - ILI9881C_COMMAND_INSTR(0xD2, 0x67), - ILI9881C_COMMAND_INSTR(0xD3, 0x39), + ILI9881C_COMMAND_INSTR(0xa0, 0x08), + ILI9881C_COMMAND_INSTR(0xa1, 0x1a), + ILI9881C_COMMAND_INSTR(0xa2, 0x27), + ILI9881C_COMMAND_INSTR(0xa3, 0x15), + ILI9881C_COMMAND_INSTR(0xa4, 0x17), + ILI9881C_COMMAND_INSTR(0xa5, 0x2a), + ILI9881C_COMMAND_INSTR(0xa6, 0x1e), + ILI9881C_COMMAND_INSTR(0xa7, 0x1f), + ILI9881C_COMMAND_INSTR(0xa8, 0x8b), + ILI9881C_COMMAND_INSTR(0xa9, 0x1b), + ILI9881C_COMMAND_INSTR(0xaa, 0x27), + ILI9881C_COMMAND_INSTR(0xab, 0x78), + ILI9881C_COMMAND_INSTR(0xac, 0x18), + ILI9881C_COMMAND_INSTR(0xad, 0x18), + ILI9881C_COMMAND_INSTR(0xae, 0x4c), + ILI9881C_COMMAND_INSTR(0xaf, 0x21), + ILI9881C_COMMAND_INSTR(0xb0, 0x27), + ILI9881C_COMMAND_INSTR(0xb1, 0x54), + ILI9881C_COMMAND_INSTR(0xb2, 0x67), + ILI9881C_COMMAND_INSTR(0xb3, 0x39), + ILI9881C_COMMAND_INSTR(0xc0, 0x08), + ILI9881C_COMMAND_INSTR(0xc1, 0x1a), + ILI9881C_COMMAND_INSTR(0xc2, 0x27), + ILI9881C_COMMAND_INSTR(0xc3, 0x15), + ILI9881C_COMMAND_INSTR(0xc4, 0x17), + ILI9881C_COMMAND_INSTR(0xc5, 0x2a), + ILI9881C_COMMAND_INSTR(0xc6, 0x1e), + ILI9881C_COMMAND_INSTR(0xc7, 0x1f), + ILI9881C_COMMAND_INSTR(0xc8, 0x8b), + ILI9881C_COMMAND_INSTR(0xc9, 0x1b), + ILI9881C_COMMAND_INSTR(0xca, 0x27), + ILI9881C_COMMAND_INSTR(0xcb, 0x78), + ILI9881C_COMMAND_INSTR(0xcc, 0x18), + ILI9881C_COMMAND_INSTR(0xcd, 0x18), + ILI9881C_COMMAND_INSTR(0xce, 0x4c), + ILI9881C_COMMAND_INSTR(0xcf, 0x21), + ILI9881C_COMMAND_INSTR(0xd0, 0x27), + ILI9881C_COMMAND_INSTR(0xd1, 0x54), + ILI9881C_COMMAND_INSTR(0xd2, 0x67), + ILI9881C_COMMAND_INSTR(0xd3, 0x39), ILI9881C_SWITCH_PAGE_INSTR(0), ILI9881C_COMMAND_INSTR(0x35, 0x00), - ILI9881C_COMMAND_INSTR(0x3A, 0x7), + ILI9881C_COMMAND_INSTR(0x3a, 0x7), }; static const struct ili9881c_instr tl050hdv35_init[] = { @@ -696,7 +696,7 @@ static const struct ili9881c_instr tl050hdv35_init[] = { ILI9881C_COMMAND_INSTR(0x35, 0x00), ILI9881C_COMMAND_INSTR(0x36, 0x00), ILI9881C_COMMAND_INSTR(0x37, 0x00), - ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x38, 0x3c), ILI9881C_COMMAND_INSTR(0x39, 0x00), ILI9881C_COMMAND_INSTR(0x3a, 0x40), ILI9881C_COMMAND_INSTR(0x3b, 0x40), @@ -750,7 +750,7 @@ static const struct ili9881c_instr tl050hdv35_init[] = { ILI9881C_COMMAND_INSTR(0x7f, 0x07), ILI9881C_COMMAND_INSTR(0x88, 0x02), ILI9881C_COMMAND_INSTR(0x89, 0x02), - ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), ILI9881C_SWITCH_PAGE_INSTR(4), ILI9881C_COMMAND_INSTR(0x38, 0x01), ILI9881C_COMMAND_INSTR(0x39, 0x00), @@ -831,12 +831,12 @@ static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_COMMAND_INSTR(0x07, 0x02), ILI9881C_COMMAND_INSTR(0x08, 0x02), ILI9881C_COMMAND_INSTR(0x09, 0x00), - ILI9881C_COMMAND_INSTR(0x0A, 0x00), - ILI9881C_COMMAND_INSTR(0x0B, 0x00), - ILI9881C_COMMAND_INSTR(0x0C, 0x00), - ILI9881C_COMMAND_INSTR(0x0D, 0x00), - ILI9881C_COMMAND_INSTR(0x0E, 0x00), - ILI9881C_COMMAND_INSTR(0x0F, 0x00), + ILI9881C_COMMAND_INSTR(0x0a, 0x00), + ILI9881C_COMMAND_INSTR(0x0b, 0x00), + ILI9881C_COMMAND_INSTR(0x0c, 0x00), + ILI9881C_COMMAND_INSTR(0x0d, 0x00), + ILI9881C_COMMAND_INSTR(0x0e, 0x00), + ILI9881C_COMMAND_INSTR(0x0f, 0x00), ILI9881C_COMMAND_INSTR(0x10, 0x00), ILI9881C_COMMAND_INSTR(0x11, 0x00), @@ -848,12 +848,12 @@ static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_COMMAND_INSTR(0x17, 0x00), ILI9881C_COMMAND_INSTR(0x18, 0x08), ILI9881C_COMMAND_INSTR(0x19, 0x00), - ILI9881C_COMMAND_INSTR(0x1A, 0x00), - ILI9881C_COMMAND_INSTR(0x1B, 0x00), - ILI9881C_COMMAND_INSTR(0x1C, 0x00), - ILI9881C_COMMAND_INSTR(0x1D, 0x00), - ILI9881C_COMMAND_INSTR(0x1E, 0xC0), - ILI9881C_COMMAND_INSTR(0x1F, 0x80), + ILI9881C_COMMAND_INSTR(0x1a, 0x00), + ILI9881C_COMMAND_INSTR(0x1b, 0x00), + ILI9881C_COMMAND_INSTR(0x1c, 0x00), + ILI9881C_COMMAND_INSTR(0x1d, 0x00), + ILI9881C_COMMAND_INSTR(0x1e, 0xc0), + ILI9881C_COMMAND_INSTR(0x1f, 0x80), ILI9881C_COMMAND_INSTR(0x20, 0x02), ILI9881C_COMMAND_INSTR(0x21, 0x09), @@ -865,12 +865,12 @@ static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_COMMAND_INSTR(0x27, 0x00), ILI9881C_COMMAND_INSTR(0x28, 0x55), ILI9881C_COMMAND_INSTR(0x29, 0x03), - ILI9881C_COMMAND_INSTR(0x2A, 0x00), - ILI9881C_COMMAND_INSTR(0x2B, 0x00), - ILI9881C_COMMAND_INSTR(0x2C, 0x00), - ILI9881C_COMMAND_INSTR(0x2D, 0x00), - ILI9881C_COMMAND_INSTR(0x2E, 0x00), - ILI9881C_COMMAND_INSTR(0x2F, 0x00), + ILI9881C_COMMAND_INSTR(0x2a, 0x00), + ILI9881C_COMMAND_INSTR(0x2b, 0x00), + ILI9881C_COMMAND_INSTR(0x2c, 0x00), + ILI9881C_COMMAND_INSTR(0x2d, 0x00), + ILI9881C_COMMAND_INSTR(0x2e, 0x00), + ILI9881C_COMMAND_INSTR(0x2f, 0x00), ILI9881C_COMMAND_INSTR(0x30, 0x00), ILI9881C_COMMAND_INSTR(0x31, 0x00), @@ -880,54 +880,54 @@ static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_COMMAND_INSTR(0x35, 0x05), ILI9881C_COMMAND_INSTR(0x36, 0x05), ILI9881C_COMMAND_INSTR(0x37, 0x00), - ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x38, 0x3c), ILI9881C_COMMAND_INSTR(0x39, 0x35), - ILI9881C_COMMAND_INSTR(0x3A, 0x00), - ILI9881C_COMMAND_INSTR(0x3B, 0x40), - ILI9881C_COMMAND_INSTR(0x3C, 0x00), - ILI9881C_COMMAND_INSTR(0x3D, 0x00), - ILI9881C_COMMAND_INSTR(0x3E, 0x00), - ILI9881C_COMMAND_INSTR(0x3F, 0x00), + ILI9881C_COMMAND_INSTR(0x3a, 0x00), + ILI9881C_COMMAND_INSTR(0x3b, 0x40), + ILI9881C_COMMAND_INSTR(0x3c, 0x00), + ILI9881C_COMMAND_INSTR(0x3d, 0x00), + ILI9881C_COMMAND_INSTR(0x3e, 0x00), + ILI9881C_COMMAND_INSTR(0x3f, 0x00), ILI9881C_COMMAND_INSTR(0x40, 0x00), ILI9881C_COMMAND_INSTR(0x41, 0x88), ILI9881C_COMMAND_INSTR(0x42, 0x00), ILI9881C_COMMAND_INSTR(0x43, 0x00), - ILI9881C_COMMAND_INSTR(0x44, 0x1F), + ILI9881C_COMMAND_INSTR(0x44, 0x1f), ILI9881C_COMMAND_INSTR(0x50, 0x01), ILI9881C_COMMAND_INSTR(0x51, 0x23), ILI9881C_COMMAND_INSTR(0x52, 0x45), ILI9881C_COMMAND_INSTR(0x53, 0x67), ILI9881C_COMMAND_INSTR(0x54, 0x89), - ILI9881C_COMMAND_INSTR(0x55, 0xaB), + ILI9881C_COMMAND_INSTR(0x55, 0xab), ILI9881C_COMMAND_INSTR(0x56, 0x01), ILI9881C_COMMAND_INSTR(0x57, 0x23), ILI9881C_COMMAND_INSTR(0x58, 0x45), ILI9881C_COMMAND_INSTR(0x59, 0x67), - ILI9881C_COMMAND_INSTR(0x5A, 0x89), - ILI9881C_COMMAND_INSTR(0x5B, 0xAB), - ILI9881C_COMMAND_INSTR(0x5C, 0xCD), - ILI9881C_COMMAND_INSTR(0x5D, 0xEF), - ILI9881C_COMMAND_INSTR(0x5E, 0x03), - ILI9881C_COMMAND_INSTR(0x5F, 0x14), + ILI9881C_COMMAND_INSTR(0x5a, 0x89), + ILI9881C_COMMAND_INSTR(0x5b, 0xab), + ILI9881C_COMMAND_INSTR(0x5c, 0xcd), + ILI9881C_COMMAND_INSTR(0x5d, 0xef), + ILI9881C_COMMAND_INSTR(0x5e, 0x03), + ILI9881C_COMMAND_INSTR(0x5f, 0x14), ILI9881C_COMMAND_INSTR(0x60, 0x15), - ILI9881C_COMMAND_INSTR(0x61, 0x0C), - ILI9881C_COMMAND_INSTR(0x62, 0x0D), - ILI9881C_COMMAND_INSTR(0x63, 0x0E), - ILI9881C_COMMAND_INSTR(0x64, 0x0F), + ILI9881C_COMMAND_INSTR(0x61, 0x0c), + ILI9881C_COMMAND_INSTR(0x62, 0x0d), + ILI9881C_COMMAND_INSTR(0x63, 0x0e), + ILI9881C_COMMAND_INSTR(0x64, 0x0f), ILI9881C_COMMAND_INSTR(0x65, 0x10), ILI9881C_COMMAND_INSTR(0x66, 0x11), ILI9881C_COMMAND_INSTR(0x67, 0x08), ILI9881C_COMMAND_INSTR(0x68, 0x02), - ILI9881C_COMMAND_INSTR(0x69, 0x0A), - ILI9881C_COMMAND_INSTR(0x6A, 0x02), - ILI9881C_COMMAND_INSTR(0x6B, 0x02), - ILI9881C_COMMAND_INSTR(0x6C, 0x02), - ILI9881C_COMMAND_INSTR(0x6D, 0x02), - ILI9881C_COMMAND_INSTR(0x6E, 0x02), - ILI9881C_COMMAND_INSTR(0x6F, 0x02), + ILI9881C_COMMAND_INSTR(0x69, 0x0a), + ILI9881C_COMMAND_INSTR(0x6a, 0x02), + ILI9881C_COMMAND_INSTR(0x6b, 0x02), + ILI9881C_COMMAND_INSTR(0x6c, 0x02), + ILI9881C_COMMAND_INSTR(0x6d, 0x02), + ILI9881C_COMMAND_INSTR(0x6e, 0x02), + ILI9881C_COMMAND_INSTR(0x6f, 0x02), ILI9881C_COMMAND_INSTR(0x70, 0x02), ILI9881C_COMMAND_INSTR(0x71, 0x02), @@ -936,15 +936,15 @@ static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_COMMAND_INSTR(0x74, 0x02), ILI9881C_COMMAND_INSTR(0x75, 0x14), ILI9881C_COMMAND_INSTR(0x76, 0x15), - ILI9881C_COMMAND_INSTR(0x77, 0x0F), - ILI9881C_COMMAND_INSTR(0x78, 0x0E), - ILI9881C_COMMAND_INSTR(0x79, 0x0D), - ILI9881C_COMMAND_INSTR(0x7A, 0x0C), - ILI9881C_COMMAND_INSTR(0x7B, 0x11), - ILI9881C_COMMAND_INSTR(0x7C, 0x10), - ILI9881C_COMMAND_INSTR(0x7D, 0x06), - ILI9881C_COMMAND_INSTR(0x7E, 0x02), - ILI9881C_COMMAND_INSTR(0x7F, 0x0A), + ILI9881C_COMMAND_INSTR(0x77, 0x0f), + ILI9881C_COMMAND_INSTR(0x78, 0x0e), + ILI9881C_COMMAND_INSTR(0x79, 0x0d), + ILI9881C_COMMAND_INSTR(0x7a, 0x0c), + ILI9881C_COMMAND_INSTR(0x7b, 0x11), + ILI9881C_COMMAND_INSTR(0x7c, 0x10), + ILI9881C_COMMAND_INSTR(0x7d, 0x06), + ILI9881C_COMMAND_INSTR(0x7e, 0x02), + ILI9881C_COMMAND_INSTR(0x7f, 0x0a), ILI9881C_COMMAND_INSTR(0x80, 0x02), ILI9881C_COMMAND_INSTR(0x81, 0x02), @@ -956,74 +956,74 @@ static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_COMMAND_INSTR(0x87, 0x02), ILI9881C_COMMAND_INSTR(0x88, 0x08), ILI9881C_COMMAND_INSTR(0x89, 0x02), - ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), ILI9881C_SWITCH_PAGE_INSTR(4), ILI9881C_COMMAND_INSTR(0x00, 0x80), ILI9881C_COMMAND_INSTR(0x70, 0x00), ILI9881C_COMMAND_INSTR(0x71, 0x00), - ILI9881C_COMMAND_INSTR(0x66, 0xFE), + ILI9881C_COMMAND_INSTR(0x66, 0xfe), ILI9881C_COMMAND_INSTR(0x82, 0x15), ILI9881C_COMMAND_INSTR(0x84, 0x15), ILI9881C_COMMAND_INSTR(0x85, 0x15), ILI9881C_COMMAND_INSTR(0x3a, 0x24), - ILI9881C_COMMAND_INSTR(0x32, 0xAC), - ILI9881C_COMMAND_INSTR(0x8C, 0x80), - ILI9881C_COMMAND_INSTR(0x3C, 0xF5), + ILI9881C_COMMAND_INSTR(0x32, 0xac), + ILI9881C_COMMAND_INSTR(0x8c, 0x80), + ILI9881C_COMMAND_INSTR(0x3c, 0xf5), ILI9881C_COMMAND_INSTR(0x88, 0x33), ILI9881C_SWITCH_PAGE_INSTR(1), - ILI9881C_COMMAND_INSTR(0x22, 0x0A), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), ILI9881C_COMMAND_INSTR(0x31, 0x00), ILI9881C_COMMAND_INSTR(0x53, 0x78), - ILI9881C_COMMAND_INSTR(0x50, 0x5B), - ILI9881C_COMMAND_INSTR(0x51, 0x5B), + ILI9881C_COMMAND_INSTR(0x50, 0x5b), + ILI9881C_COMMAND_INSTR(0x51, 0x5b), ILI9881C_COMMAND_INSTR(0x60, 0x20), ILI9881C_COMMAND_INSTR(0x61, 0x00), - ILI9881C_COMMAND_INSTR(0x62, 0x0D), + ILI9881C_COMMAND_INSTR(0x62, 0x0d), ILI9881C_COMMAND_INSTR(0x63, 0x00), - ILI9881C_COMMAND_INSTR(0xA0, 0x00), - ILI9881C_COMMAND_INSTR(0xA1, 0x10), - ILI9881C_COMMAND_INSTR(0xA2, 0x1C), - ILI9881C_COMMAND_INSTR(0xA3, 0x13), - ILI9881C_COMMAND_INSTR(0xA4, 0x15), - ILI9881C_COMMAND_INSTR(0xA5, 0x26), - ILI9881C_COMMAND_INSTR(0xA6, 0x1A), - ILI9881C_COMMAND_INSTR(0xA7, 0x1D), - ILI9881C_COMMAND_INSTR(0xA8, 0x67), - ILI9881C_COMMAND_INSTR(0xA9, 0x1C), - ILI9881C_COMMAND_INSTR(0xAA, 0x29), - ILI9881C_COMMAND_INSTR(0xAB, 0x5B), - ILI9881C_COMMAND_INSTR(0xAC, 0x26), - ILI9881C_COMMAND_INSTR(0xAD, 0x28), - ILI9881C_COMMAND_INSTR(0xAE, 0x5C), - ILI9881C_COMMAND_INSTR(0xAF, 0x30), - ILI9881C_COMMAND_INSTR(0xB0, 0x31), - ILI9881C_COMMAND_INSTR(0xB1, 0x2E), - ILI9881C_COMMAND_INSTR(0xB2, 0x32), - ILI9881C_COMMAND_INSTR(0xB3, 0x00), - - ILI9881C_COMMAND_INSTR(0xC0, 0x00), - ILI9881C_COMMAND_INSTR(0xC1, 0x10), - ILI9881C_COMMAND_INSTR(0xC2, 0x1C), - ILI9881C_COMMAND_INSTR(0xC3, 0x13), - ILI9881C_COMMAND_INSTR(0xC4, 0x15), - ILI9881C_COMMAND_INSTR(0xC5, 0x26), - ILI9881C_COMMAND_INSTR(0xC6, 0x1A), - ILI9881C_COMMAND_INSTR(0xC7, 0x1D), - ILI9881C_COMMAND_INSTR(0xC8, 0x67), - ILI9881C_COMMAND_INSTR(0xC9, 0x1C), - ILI9881C_COMMAND_INSTR(0xCA, 0x29), - ILI9881C_COMMAND_INSTR(0xCB, 0x5B), - ILI9881C_COMMAND_INSTR(0xCC, 0x26), - ILI9881C_COMMAND_INSTR(0xCD, 0x28), - ILI9881C_COMMAND_INSTR(0xCE, 0x5C), - ILI9881C_COMMAND_INSTR(0xCF, 0x30), - ILI9881C_COMMAND_INSTR(0xD0, 0x31), - ILI9881C_COMMAND_INSTR(0xD1, 0x2E), - ILI9881C_COMMAND_INSTR(0xD2, 0x32), - ILI9881C_COMMAND_INSTR(0xD3, 0x00), + ILI9881C_COMMAND_INSTR(0xa0, 0x00), + ILI9881C_COMMAND_INSTR(0xa1, 0x10), + ILI9881C_COMMAND_INSTR(0xa2, 0x1c), + ILI9881C_COMMAND_INSTR(0xa3, 0x13), + ILI9881C_COMMAND_INSTR(0xa4, 0x15), + ILI9881C_COMMAND_INSTR(0xa5, 0x26), + ILI9881C_COMMAND_INSTR(0xa6, 0x1a), + ILI9881C_COMMAND_INSTR(0xa7, 0x1d), + ILI9881C_COMMAND_INSTR(0xa8, 0x67), + ILI9881C_COMMAND_INSTR(0xa9, 0x1c), + ILI9881C_COMMAND_INSTR(0xaa, 0x29), + ILI9881C_COMMAND_INSTR(0xab, 0x5b), + ILI9881C_COMMAND_INSTR(0xac, 0x26), + ILI9881C_COMMAND_INSTR(0xad, 0x28), + ILI9881C_COMMAND_INSTR(0xae, 0x5c), + ILI9881C_COMMAND_INSTR(0xaf, 0x30), + ILI9881C_COMMAND_INSTR(0xb0, 0x31), + ILI9881C_COMMAND_INSTR(0xb1, 0x2e), + ILI9881C_COMMAND_INSTR(0xb2, 0x32), + ILI9881C_COMMAND_INSTR(0xb3, 0x00), + + ILI9881C_COMMAND_INSTR(0xc0, 0x00), + ILI9881C_COMMAND_INSTR(0xc1, 0x10), + ILI9881C_COMMAND_INSTR(0xc2, 0x1c), + ILI9881C_COMMAND_INSTR(0xc3, 0x13), + ILI9881C_COMMAND_INSTR(0xc4, 0x15), + ILI9881C_COMMAND_INSTR(0xc5, 0x26), + ILI9881C_COMMAND_INSTR(0xc6, 0x1a), + ILI9881C_COMMAND_INSTR(0xc7, 0x1d), + ILI9881C_COMMAND_INSTR(0xc8, 0x67), + ILI9881C_COMMAND_INSTR(0xc9, 0x1c), + ILI9881C_COMMAND_INSTR(0xca, 0x29), + ILI9881C_COMMAND_INSTR(0xcb, 0x5b), + ILI9881C_COMMAND_INSTR(0xcc, 0x26), + ILI9881C_COMMAND_INSTR(0xcd, 0x28), + ILI9881C_COMMAND_INSTR(0xce, 0x5c), + ILI9881C_COMMAND_INSTR(0xcf, 0x30), + ILI9881C_COMMAND_INSTR(0xd0, 0x31), + ILI9881C_COMMAND_INSTR(0xd1, 0x2e), + ILI9881C_COMMAND_INSTR(0xd2, 0x32), + ILI9881C_COMMAND_INSTR(0xd3, 0x00), ILI9881C_SWITCH_PAGE_INSTR(0), }; @@ -1032,10 +1032,10 @@ static const struct ili9881c_instr am8001280g_init[] = { ILI9881C_COMMAND_INSTR(0x01, 0x00), ILI9881C_COMMAND_INSTR(0x02, 0x00), ILI9881C_COMMAND_INSTR(0x03, 0x73), - ILI9881C_COMMAND_INSTR(0x04, 0xD3), + ILI9881C_COMMAND_INSTR(0x04, 0xd3), ILI9881C_COMMAND_INSTR(0x05, 0x00), - ILI9881C_COMMAND_INSTR(0x06, 0x0A), - ILI9881C_COMMAND_INSTR(0x07, 0x0E), + ILI9881C_COMMAND_INSTR(0x06, 0x0a), + ILI9881C_COMMAND_INSTR(0x07, 0x0e), ILI9881C_COMMAND_INSTR(0x08, 0x00), ILI9881C_COMMAND_INSTR(0x09, 0x01), ILI9881C_COMMAND_INSTR(0x0a, 0x01), @@ -1117,10 +1117,10 @@ static const struct ili9881c_instr am8001280g_init[] = { ILI9881C_COMMAND_INSTR(0x5f, 0x02), ILI9881C_COMMAND_INSTR(0x60, 0x00), ILI9881C_COMMAND_INSTR(0x61, 0x01), - ILI9881C_COMMAND_INSTR(0x62, 0x0D), - ILI9881C_COMMAND_INSTR(0x63, 0x0C), - ILI9881C_COMMAND_INSTR(0x64, 0x0F), - ILI9881C_COMMAND_INSTR(0x65, 0x0E), + ILI9881C_COMMAND_INSTR(0x62, 0x0d), + ILI9881C_COMMAND_INSTR(0x63, 0x0c), + ILI9881C_COMMAND_INSTR(0x64, 0x0f), + ILI9881C_COMMAND_INSTR(0x65, 0x0e), ILI9881C_COMMAND_INSTR(0x66, 0x06), ILI9881C_COMMAND_INSTR(0x67, 0x07), ILI9881C_COMMAND_INSTR(0x68, 0x02), @@ -1139,10 +1139,10 @@ static const struct ili9881c_instr am8001280g_init[] = { ILI9881C_COMMAND_INSTR(0x75, 0x02), ILI9881C_COMMAND_INSTR(0x76, 0x00), ILI9881C_COMMAND_INSTR(0x77, 0x01), - ILI9881C_COMMAND_INSTR(0x78, 0x0D), - ILI9881C_COMMAND_INSTR(0x79, 0x0C), - ILI9881C_COMMAND_INSTR(0x7a, 0x0F), - ILI9881C_COMMAND_INSTR(0x7b, 0x0E), + ILI9881C_COMMAND_INSTR(0x78, 0x0d), + ILI9881C_COMMAND_INSTR(0x79, 0x0c), + ILI9881C_COMMAND_INSTR(0x7a, 0x0f), + ILI9881C_COMMAND_INSTR(0x7b, 0x0e), ILI9881C_COMMAND_INSTR(0x7c, 0x06), ILI9881C_COMMAND_INSTR(0x7d, 0x07), ILI9881C_COMMAND_INSTR(0x7e, 0x02), @@ -1157,7 +1157,7 @@ static const struct ili9881c_instr am8001280g_init[] = { ILI9881C_COMMAND_INSTR(0x87, 0x02), ILI9881C_COMMAND_INSTR(0x88, 0x02), ILI9881C_COMMAND_INSTR(0x89, 0x02), - ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), ILI9881C_SWITCH_PAGE_INSTR(4), ILI9881C_COMMAND_INSTR(0x6c, 0x15), @@ -1170,60 +1170,248 @@ static const struct ili9881c_instr am8001280g_init[] = { ILI9881C_COMMAND_INSTR(0xb2, 0xd1), ILI9881C_SWITCH_PAGE_INSTR(1), - ILI9881C_COMMAND_INSTR(0x22, 0x0A), - ILI9881C_COMMAND_INSTR(0x31, 0x0B), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), + ILI9881C_COMMAND_INSTR(0x31, 0x0b), ILI9881C_COMMAND_INSTR(0x50, 0xa5), ILI9881C_COMMAND_INSTR(0x51, 0xa0), ILI9881C_COMMAND_INSTR(0x53, 0x70), - ILI9881C_COMMAND_INSTR(0x55, 0x7A), + ILI9881C_COMMAND_INSTR(0x55, 0x7a), ILI9881C_COMMAND_INSTR(0x60, 0x14), - ILI9881C_COMMAND_INSTR(0xA0, 0x00), - ILI9881C_COMMAND_INSTR(0xA1, 0x53), - ILI9881C_COMMAND_INSTR(0xA2, 0x50), - ILI9881C_COMMAND_INSTR(0xA3, 0x20), - ILI9881C_COMMAND_INSTR(0xA4, 0x27), - ILI9881C_COMMAND_INSTR(0xA5, 0x33), - ILI9881C_COMMAND_INSTR(0xA6, 0x25), - ILI9881C_COMMAND_INSTR(0xA7, 0x25), - ILI9881C_COMMAND_INSTR(0xA8, 0xD4), - ILI9881C_COMMAND_INSTR(0xA9, 0x1A), - ILI9881C_COMMAND_INSTR(0xAA, 0x2B), - ILI9881C_COMMAND_INSTR(0xAB, 0xB5), - ILI9881C_COMMAND_INSTR(0xAC, 0x19), - ILI9881C_COMMAND_INSTR(0xAD, 0x18), - ILI9881C_COMMAND_INSTR(0xAE, 0x53), - ILI9881C_COMMAND_INSTR(0xAF, 0x1A), - ILI9881C_COMMAND_INSTR(0xB0, 0x25), - ILI9881C_COMMAND_INSTR(0xB1, 0x62), - ILI9881C_COMMAND_INSTR(0xB2, 0x6A), - ILI9881C_COMMAND_INSTR(0xB3, 0x31), - - ILI9881C_COMMAND_INSTR(0xC0, 0x00), - ILI9881C_COMMAND_INSTR(0xC1, 0x53), - ILI9881C_COMMAND_INSTR(0xC2, 0x50), - ILI9881C_COMMAND_INSTR(0xC3, 0x20), - ILI9881C_COMMAND_INSTR(0xC4, 0x27), - ILI9881C_COMMAND_INSTR(0xC5, 0x33), - ILI9881C_COMMAND_INSTR(0xC6, 0x25), - ILI9881C_COMMAND_INSTR(0xC7, 0x25), - ILI9881C_COMMAND_INSTR(0xC8, 0xD4), - ILI9881C_COMMAND_INSTR(0xC9, 0x1A), - ILI9881C_COMMAND_INSTR(0xCA, 0x2B), - ILI9881C_COMMAND_INSTR(0xCB, 0xB5), - ILI9881C_COMMAND_INSTR(0xCC, 0x19), - ILI9881C_COMMAND_INSTR(0xCD, 0x18), - ILI9881C_COMMAND_INSTR(0xCE, 0x53), - ILI9881C_COMMAND_INSTR(0xCF, 0x1A), - ILI9881C_COMMAND_INSTR(0xD0, 0x25), - ILI9881C_COMMAND_INSTR(0xD1, 0x62), - ILI9881C_COMMAND_INSTR(0xD2, 0x6A), - ILI9881C_COMMAND_INSTR(0xD3, 0x31), + ILI9881C_COMMAND_INSTR(0xa0, 0x00), + ILI9881C_COMMAND_INSTR(0xa1, 0x53), + ILI9881C_COMMAND_INSTR(0xa2, 0x50), + ILI9881C_COMMAND_INSTR(0xa3, 0x20), + ILI9881C_COMMAND_INSTR(0xa4, 0x27), + ILI9881C_COMMAND_INSTR(0xa5, 0x33), + ILI9881C_COMMAND_INSTR(0xa6, 0x25), + ILI9881C_COMMAND_INSTR(0xa7, 0x25), + ILI9881C_COMMAND_INSTR(0xa8, 0xd4), + ILI9881C_COMMAND_INSTR(0xa9, 0x1a), + ILI9881C_COMMAND_INSTR(0xaa, 0x2b), + ILI9881C_COMMAND_INSTR(0xab, 0xb5), + ILI9881C_COMMAND_INSTR(0xac, 0x19), + ILI9881C_COMMAND_INSTR(0xad, 0x18), + ILI9881C_COMMAND_INSTR(0xae, 0x53), + ILI9881C_COMMAND_INSTR(0xaf, 0x1a), + ILI9881C_COMMAND_INSTR(0xb0, 0x25), + ILI9881C_COMMAND_INSTR(0xb1, 0x62), + ILI9881C_COMMAND_INSTR(0xb2, 0x6a), + ILI9881C_COMMAND_INSTR(0xb3, 0x31), + + ILI9881C_COMMAND_INSTR(0xc0, 0x00), + ILI9881C_COMMAND_INSTR(0xc1, 0x53), + ILI9881C_COMMAND_INSTR(0xc2, 0x50), + ILI9881C_COMMAND_INSTR(0xc3, 0x20), + ILI9881C_COMMAND_INSTR(0xc4, 0x27), + ILI9881C_COMMAND_INSTR(0xc5, 0x33), + ILI9881C_COMMAND_INSTR(0xc6, 0x25), + ILI9881C_COMMAND_INSTR(0xc7, 0x25), + ILI9881C_COMMAND_INSTR(0xc8, 0xd4), + ILI9881C_COMMAND_INSTR(0xc9, 0x1a), + ILI9881C_COMMAND_INSTR(0xca, 0x2b), + ILI9881C_COMMAND_INSTR(0xcb, 0xb5), + ILI9881C_COMMAND_INSTR(0xcc, 0x19), + ILI9881C_COMMAND_INSTR(0xcd, 0x18), + ILI9881C_COMMAND_INSTR(0xce, 0x53), + ILI9881C_COMMAND_INSTR(0xcf, 0x1a), + ILI9881C_COMMAND_INSTR(0xd0, 0x25), + ILI9881C_COMMAND_INSTR(0xd1, 0x62), + ILI9881C_COMMAND_INSTR(0xd2, 0x6a), + ILI9881C_COMMAND_INSTR(0xd3, 0x31), ILI9881C_SWITCH_PAGE_INSTR(0), ILI9881C_COMMAND_INSTR(MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c), ILI9881C_COMMAND_INSTR(MIPI_DCS_WRITE_POWER_SAVE, 0x00), }; +static const struct ili9881c_instr rpi_5inch_init[] = { + ILI9881C_SWITCH_PAGE_INSTR(3), + ILI9881C_COMMAND_INSTR(0x01, 0x00), + ILI9881C_COMMAND_INSTR(0x02, 0x00), + ILI9881C_COMMAND_INSTR(0x03, 0x73), + ILI9881C_COMMAND_INSTR(0x04, 0x73), + ILI9881C_COMMAND_INSTR(0x05, 0x00), + ILI9881C_COMMAND_INSTR(0x06, 0x06), + ILI9881C_COMMAND_INSTR(0x07, 0x02), + ILI9881C_COMMAND_INSTR(0x08, 0x00), + ILI9881C_COMMAND_INSTR(0x09, 0x01), + ILI9881C_COMMAND_INSTR(0x0a, 0x01), + ILI9881C_COMMAND_INSTR(0x0b, 0x01), + ILI9881C_COMMAND_INSTR(0x0c, 0x01), + ILI9881C_COMMAND_INSTR(0x0d, 0x01), + ILI9881C_COMMAND_INSTR(0x0e, 0x01), + ILI9881C_COMMAND_INSTR(0x0f, 0x01), + ILI9881C_COMMAND_INSTR(0x10, 0x01), + ILI9881C_COMMAND_INSTR(0x11, 0x00), + ILI9881C_COMMAND_INSTR(0x12, 0x00), + ILI9881C_COMMAND_INSTR(0x13, 0x01), + ILI9881C_COMMAND_INSTR(0x14, 0x00), + ILI9881C_COMMAND_INSTR(0x15, 0x00), + ILI9881C_COMMAND_INSTR(0x16, 0x00), + ILI9881C_COMMAND_INSTR(0x17, 0x00), + ILI9881C_COMMAND_INSTR(0x18, 0x00), + ILI9881C_COMMAND_INSTR(0x19, 0x00), + ILI9881C_COMMAND_INSTR(0x1a, 0x00), + ILI9881C_COMMAND_INSTR(0x1b, 0x00), + ILI9881C_COMMAND_INSTR(0x1c, 0x00), + ILI9881C_COMMAND_INSTR(0x1d, 0x00), + ILI9881C_COMMAND_INSTR(0x1e, 0xc0), + ILI9881C_COMMAND_INSTR(0x1f, 0x80), + ILI9881C_COMMAND_INSTR(0x20, 0x04), + ILI9881C_COMMAND_INSTR(0x21, 0x03), + ILI9881C_COMMAND_INSTR(0x22, 0x00), + ILI9881C_COMMAND_INSTR(0x23, 0x00), + ILI9881C_COMMAND_INSTR(0x24, 0x00), + ILI9881C_COMMAND_INSTR(0x25, 0x00), + ILI9881C_COMMAND_INSTR(0x26, 0x00), + ILI9881C_COMMAND_INSTR(0x27, 0x00), + ILI9881C_COMMAND_INSTR(0x28, 0x33), + ILI9881C_COMMAND_INSTR(0x29, 0x03), + ILI9881C_COMMAND_INSTR(0x2a, 0x00), + ILI9881C_COMMAND_INSTR(0x2b, 0x00), + ILI9881C_COMMAND_INSTR(0x2c, 0x00), + ILI9881C_COMMAND_INSTR(0x2d, 0x00), + ILI9881C_COMMAND_INSTR(0x2e, 0x00), + ILI9881C_COMMAND_INSTR(0x2f, 0x00), + ILI9881C_COMMAND_INSTR(0x30, 0x00), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x32, 0x00), + ILI9881C_COMMAND_INSTR(0x33, 0x00), + ILI9881C_COMMAND_INSTR(0x34, 0x03), + ILI9881C_COMMAND_INSTR(0x35, 0x00), + ILI9881C_COMMAND_INSTR(0x36, 0x03), + ILI9881C_COMMAND_INSTR(0x37, 0x00), + ILI9881C_COMMAND_INSTR(0x38, 0x00), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_COMMAND_INSTR(0x3a, 0x00), + ILI9881C_COMMAND_INSTR(0x3b, 0x00), + ILI9881C_COMMAND_INSTR(0x3c, 0x00), + ILI9881C_COMMAND_INSTR(0x3d, 0x00), + ILI9881C_COMMAND_INSTR(0x3e, 0x00), + ILI9881C_COMMAND_INSTR(0x3f, 0x00), + ILI9881C_COMMAND_INSTR(0x40, 0x00), + ILI9881C_COMMAND_INSTR(0x41, 0x00), + ILI9881C_COMMAND_INSTR(0x42, 0x00), + ILI9881C_COMMAND_INSTR(0x43, 0x00), + ILI9881C_COMMAND_INSTR(0x44, 0x00), + ILI9881C_COMMAND_INSTR(0x50, 0x01), + ILI9881C_COMMAND_INSTR(0x51, 0x23), + ILI9881C_COMMAND_INSTR(0x52, 0x45), + ILI9881C_COMMAND_INSTR(0x53, 0x67), + ILI9881C_COMMAND_INSTR(0x54, 0x89), + ILI9881C_COMMAND_INSTR(0x55, 0xab), + ILI9881C_COMMAND_INSTR(0x56, 0x01), + ILI9881C_COMMAND_INSTR(0x57, 0x23), + ILI9881C_COMMAND_INSTR(0x58, 0x45), + ILI9881C_COMMAND_INSTR(0x59, 0x67), + ILI9881C_COMMAND_INSTR(0x5a, 0x89), + ILI9881C_COMMAND_INSTR(0x5b, 0xab), + ILI9881C_COMMAND_INSTR(0x5c, 0xcd), + ILI9881C_COMMAND_INSTR(0x5d, 0xef), + ILI9881C_COMMAND_INSTR(0x5e, 0x10), + ILI9881C_COMMAND_INSTR(0x5f, 0x09), + ILI9881C_COMMAND_INSTR(0x60, 0x08), + ILI9881C_COMMAND_INSTR(0x61, 0x0f), + ILI9881C_COMMAND_INSTR(0x62, 0x0e), + ILI9881C_COMMAND_INSTR(0x63, 0x0d), + ILI9881C_COMMAND_INSTR(0x64, 0x0c), + ILI9881C_COMMAND_INSTR(0x65, 0x02), + ILI9881C_COMMAND_INSTR(0x66, 0x02), + ILI9881C_COMMAND_INSTR(0x67, 0x02), + ILI9881C_COMMAND_INSTR(0x68, 0x02), + ILI9881C_COMMAND_INSTR(0x69, 0x02), + ILI9881C_COMMAND_INSTR(0x6a, 0x02), + ILI9881C_COMMAND_INSTR(0x6b, 0x02), + ILI9881C_COMMAND_INSTR(0x6c, 0x02), + ILI9881C_COMMAND_INSTR(0x6d, 0x02), + ILI9881C_COMMAND_INSTR(0x6e, 0x02), + ILI9881C_COMMAND_INSTR(0x6f, 0x02), + ILI9881C_COMMAND_INSTR(0x70, 0x02), + ILI9881C_COMMAND_INSTR(0x71, 0x06), + ILI9881C_COMMAND_INSTR(0x72, 0x07), + ILI9881C_COMMAND_INSTR(0x73, 0x02), + ILI9881C_COMMAND_INSTR(0x74, 0x02), + ILI9881C_COMMAND_INSTR(0x75, 0x06), + ILI9881C_COMMAND_INSTR(0x76, 0x07), + ILI9881C_COMMAND_INSTR(0x77, 0x0e), + ILI9881C_COMMAND_INSTR(0x78, 0x0f), + ILI9881C_COMMAND_INSTR(0x79, 0x0c), + ILI9881C_COMMAND_INSTR(0x7a, 0x0d), + ILI9881C_COMMAND_INSTR(0x7b, 0x02), + ILI9881C_COMMAND_INSTR(0x7c, 0x02), + ILI9881C_COMMAND_INSTR(0x7d, 0x02), + ILI9881C_COMMAND_INSTR(0x7e, 0x02), + ILI9881C_COMMAND_INSTR(0x7f, 0x02), + ILI9881C_COMMAND_INSTR(0x80, 0x02), + ILI9881C_COMMAND_INSTR(0x81, 0x02), + ILI9881C_COMMAND_INSTR(0x82, 0x02), + ILI9881C_COMMAND_INSTR(0x83, 0x02), + ILI9881C_COMMAND_INSTR(0x84, 0x02), + ILI9881C_COMMAND_INSTR(0x85, 0x02), + ILI9881C_COMMAND_INSTR(0x86, 0x02), + ILI9881C_COMMAND_INSTR(0x87, 0x09), + ILI9881C_COMMAND_INSTR(0x88, 0x08), + ILI9881C_COMMAND_INSTR(0x89, 0x02), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), + ILI9881C_SWITCH_PAGE_INSTR(4), + ILI9881C_COMMAND_INSTR(0x6c, 0x15), + ILI9881C_COMMAND_INSTR(0x6e, 0x2a), + ILI9881C_COMMAND_INSTR(0x6f, 0x57), + ILI9881C_COMMAND_INSTR(0x3a, 0xa4), + ILI9881C_COMMAND_INSTR(0x8d, 0x1a), + ILI9881C_COMMAND_INSTR(0x87, 0xba), + ILI9881C_COMMAND_INSTR(0x26, 0x76), + ILI9881C_COMMAND_INSTR(0xb2, 0xd1), + ILI9881C_SWITCH_PAGE_INSTR(1), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x53, 0x35), + ILI9881C_COMMAND_INSTR(0x55, 0x50), + ILI9881C_COMMAND_INSTR(0x50, 0xaf), + ILI9881C_COMMAND_INSTR(0x51, 0xaf), + ILI9881C_COMMAND_INSTR(0x60, 0x14), + ILI9881C_COMMAND_INSTR(0xa0, 0x08), + ILI9881C_COMMAND_INSTR(0xa1, 0x1d), + ILI9881C_COMMAND_INSTR(0xa2, 0x2c), + ILI9881C_COMMAND_INSTR(0xa3, 0x14), + ILI9881C_COMMAND_INSTR(0xa4, 0x19), + ILI9881C_COMMAND_INSTR(0xa5, 0x2e), + ILI9881C_COMMAND_INSTR(0xa6, 0x22), + ILI9881C_COMMAND_INSTR(0xa7, 0x23), + ILI9881C_COMMAND_INSTR(0xa8, 0x97), + ILI9881C_COMMAND_INSTR(0xa9, 0x1e), + ILI9881C_COMMAND_INSTR(0xaa, 0x29), + ILI9881C_COMMAND_INSTR(0xab, 0x7b), + ILI9881C_COMMAND_INSTR(0xac, 0x18), + ILI9881C_COMMAND_INSTR(0xad, 0x17), + ILI9881C_COMMAND_INSTR(0xae, 0x4b), + ILI9881C_COMMAND_INSTR(0xaf, 0x1f), + ILI9881C_COMMAND_INSTR(0xb0, 0x27), + ILI9881C_COMMAND_INSTR(0xb1, 0x52), + ILI9881C_COMMAND_INSTR(0xb2, 0x63), + ILI9881C_COMMAND_INSTR(0xb3, 0x39), + ILI9881C_COMMAND_INSTR(0xc0, 0x08), + ILI9881C_COMMAND_INSTR(0xc1, 0x1d), + ILI9881C_COMMAND_INSTR(0xc2, 0x2c), + ILI9881C_COMMAND_INSTR(0xc3, 0x14), + ILI9881C_COMMAND_INSTR(0xc4, 0x19), + ILI9881C_COMMAND_INSTR(0xc5, 0x2e), + ILI9881C_COMMAND_INSTR(0xc6, 0x22), + ILI9881C_COMMAND_INSTR(0xc7, 0x23), + ILI9881C_COMMAND_INSTR(0xc8, 0x97), + ILI9881C_COMMAND_INSTR(0xc9, 0x1e), + ILI9881C_COMMAND_INSTR(0xca, 0x29), + ILI9881C_COMMAND_INSTR(0xcb, 0x7b), + ILI9881C_COMMAND_INSTR(0xcc, 0x18), + ILI9881C_COMMAND_INSTR(0xcd, 0x17), + ILI9881C_COMMAND_INSTR(0xce, 0x4b), + ILI9881C_COMMAND_INSTR(0xcf, 0x1f), + ILI9881C_COMMAND_INSTR(0xd0, 0x27), + ILI9881C_COMMAND_INSTR(0xd1, 0x52), + ILI9881C_COMMAND_INSTR(0xd2, 0x63), + ILI9881C_COMMAND_INSTR(0xd3, 0x39), +}; + static const struct ili9881c_instr rpi_7inch_init[] = { ILI9881C_SWITCH_PAGE_INSTR(3), ILI9881C_COMMAND_INSTR(0x01, 0x00), @@ -1352,22 +1540,22 @@ static const struct ili9881c_instr rpi_7inch_init[] = { ILI9881C_COMMAND_INSTR(0x87, 0x02), ILI9881C_COMMAND_INSTR(0x88, 0x02), ILI9881C_COMMAND_INSTR(0x89, 0x02), - ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_COMMAND_INSTR(0x8a, 0x02), ILI9881C_SWITCH_PAGE_INSTR(4), - ILI9881C_COMMAND_INSTR(0x6C, 0x15), - ILI9881C_COMMAND_INSTR(0x6E, 0x2A), - ILI9881C_COMMAND_INSTR(0x6F, 0x33), - ILI9881C_COMMAND_INSTR(0x3B, 0x98), + ILI9881C_COMMAND_INSTR(0x6c, 0x15), + ILI9881C_COMMAND_INSTR(0x6e, 0x2a), + ILI9881C_COMMAND_INSTR(0x6f, 0x33), + ILI9881C_COMMAND_INSTR(0x3b, 0x98), ILI9881C_COMMAND_INSTR(0x3a, 0x94), - ILI9881C_COMMAND_INSTR(0x8D, 0x14), - ILI9881C_COMMAND_INSTR(0x87, 0xBA), + ILI9881C_COMMAND_INSTR(0x8d, 0x14), + ILI9881C_COMMAND_INSTR(0x87, 0xba), ILI9881C_COMMAND_INSTR(0x26, 0x76), - ILI9881C_COMMAND_INSTR(0xB2, 0xD1), - ILI9881C_COMMAND_INSTR(0xB5, 0x06), + ILI9881C_COMMAND_INSTR(0xb2, 0xd1), + ILI9881C_COMMAND_INSTR(0xb5, 0x06), ILI9881C_COMMAND_INSTR(0x38, 0x01), ILI9881C_COMMAND_INSTR(0x39, 0x00), ILI9881C_SWITCH_PAGE_INSTR(1), - ILI9881C_COMMAND_INSTR(0x22, 0x0A), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), ILI9881C_COMMAND_INSTR(0x31, 0x00), ILI9881C_COMMAND_INSTR(0x53, 0x7d), ILI9881C_COMMAND_INSTR(0x55, 0x8f), @@ -1375,46 +1563,46 @@ static const struct ili9881c_instr rpi_7inch_init[] = { ILI9881C_COMMAND_INSTR(0x50, 0x96), ILI9881C_COMMAND_INSTR(0x51, 0x96), ILI9881C_COMMAND_INSTR(0x60, 0x23), - ILI9881C_COMMAND_INSTR(0xA0, 0x08), - ILI9881C_COMMAND_INSTR(0xA1, 0x1d), - ILI9881C_COMMAND_INSTR(0xA2, 0x2a), - ILI9881C_COMMAND_INSTR(0xA3, 0x10), - ILI9881C_COMMAND_INSTR(0xA4, 0x15), - ILI9881C_COMMAND_INSTR(0xA5, 0x28), - ILI9881C_COMMAND_INSTR(0xA6, 0x1c), - ILI9881C_COMMAND_INSTR(0xA7, 0x1d), - ILI9881C_COMMAND_INSTR(0xA8, 0x7e), - ILI9881C_COMMAND_INSTR(0xA9, 0x1d), - ILI9881C_COMMAND_INSTR(0xAA, 0x29), - ILI9881C_COMMAND_INSTR(0xAB, 0x6b), - ILI9881C_COMMAND_INSTR(0xAC, 0x1a), - ILI9881C_COMMAND_INSTR(0xAD, 0x18), - ILI9881C_COMMAND_INSTR(0xAE, 0x4b), - ILI9881C_COMMAND_INSTR(0xAF, 0x20), - ILI9881C_COMMAND_INSTR(0xB0, 0x27), - ILI9881C_COMMAND_INSTR(0xB1, 0x50), - ILI9881C_COMMAND_INSTR(0xB2, 0x64), - ILI9881C_COMMAND_INSTR(0xB3, 0x39), - ILI9881C_COMMAND_INSTR(0xC0, 0x08), - ILI9881C_COMMAND_INSTR(0xC1, 0x1d), - ILI9881C_COMMAND_INSTR(0xC2, 0x2a), - ILI9881C_COMMAND_INSTR(0xC3, 0x10), - ILI9881C_COMMAND_INSTR(0xC4, 0x15), - ILI9881C_COMMAND_INSTR(0xC5, 0x28), - ILI9881C_COMMAND_INSTR(0xC6, 0x1c), - ILI9881C_COMMAND_INSTR(0xC7, 0x1d), - ILI9881C_COMMAND_INSTR(0xC8, 0x7e), - ILI9881C_COMMAND_INSTR(0xC9, 0x1d), - ILI9881C_COMMAND_INSTR(0xCA, 0x29), - ILI9881C_COMMAND_INSTR(0xCB, 0x6b), - ILI9881C_COMMAND_INSTR(0xCC, 0x1a), - ILI9881C_COMMAND_INSTR(0xCD, 0x18), - ILI9881C_COMMAND_INSTR(0xCE, 0x4b), - ILI9881C_COMMAND_INSTR(0xCF, 0x20), - ILI9881C_COMMAND_INSTR(0xD0, 0x27), - ILI9881C_COMMAND_INSTR(0xD1, 0x50), - ILI9881C_COMMAND_INSTR(0xD2, 0x64), - ILI9881C_COMMAND_INSTR(0xD3, 0x39), + ILI9881C_COMMAND_INSTR(0xa0, 0x08), + ILI9881C_COMMAND_INSTR(0xa1, 0x1d), + ILI9881C_COMMAND_INSTR(0xa2, 0x2a), + ILI9881C_COMMAND_INSTR(0xa3, 0x10), + ILI9881C_COMMAND_INSTR(0xa4, 0x15), + ILI9881C_COMMAND_INSTR(0xa5, 0x28), + ILI9881C_COMMAND_INSTR(0xa6, 0x1c), + ILI9881C_COMMAND_INSTR(0xa7, 0x1d), + ILI9881C_COMMAND_INSTR(0xa8, 0x7e), + ILI9881C_COMMAND_INSTR(0xa9, 0x1d), + ILI9881C_COMMAND_INSTR(0xaa, 0x29), + ILI9881C_COMMAND_INSTR(0xab, 0x6b), + ILI9881C_COMMAND_INSTR(0xac, 0x1a), + ILI9881C_COMMAND_INSTR(0xad, 0x18), + ILI9881C_COMMAND_INSTR(0xae, 0x4b), + ILI9881C_COMMAND_INSTR(0xaf, 0x20), + ILI9881C_COMMAND_INSTR(0xb0, 0x27), + ILI9881C_COMMAND_INSTR(0xb1, 0x50), + ILI9881C_COMMAND_INSTR(0xb2, 0x64), + ILI9881C_COMMAND_INSTR(0xb3, 0x39), + ILI9881C_COMMAND_INSTR(0xc0, 0x08), + ILI9881C_COMMAND_INSTR(0xc1, 0x1d), + ILI9881C_COMMAND_INSTR(0xc2, 0x2a), + ILI9881C_COMMAND_INSTR(0xc3, 0x10), + ILI9881C_COMMAND_INSTR(0xc4, 0x15), + ILI9881C_COMMAND_INSTR(0xc5, 0x28), + ILI9881C_COMMAND_INSTR(0xc6, 0x1c), + ILI9881C_COMMAND_INSTR(0xc7, 0x1d), + ILI9881C_COMMAND_INSTR(0xc8, 0x7e), + ILI9881C_COMMAND_INSTR(0xc9, 0x1d), + ILI9881C_COMMAND_INSTR(0xca, 0x29), + ILI9881C_COMMAND_INSTR(0xcb, 0x6b), + ILI9881C_COMMAND_INSTR(0xcc, 0x1a), + ILI9881C_COMMAND_INSTR(0xcd, 0x18), + ILI9881C_COMMAND_INSTR(0xce, 0x4b), + ILI9881C_COMMAND_INSTR(0xcf, 0x20), + ILI9881C_COMMAND_INSTR(0xd0, 0x27), + ILI9881C_COMMAND_INSTR(0xd1, 0x50), + ILI9881C_COMMAND_INSTR(0xd2, 0x64), + ILI9881C_COMMAND_INSTR(0xd3, 0x39), }; static const struct ili9881c_instr bsd1218_a101kl68_init[] = { @@ -1806,6 +1994,23 @@ static const struct drm_display_mode am8001280g_default_mode = { .height_mm = 151, }; +static const struct drm_display_mode rpi_5inch_default_mode = { + .clock = 83333, + + .hdisplay = 720, + .hsync_start = 720 + 110, + .hsync_end = 720 + 110 + 12, + .htotal = 720 + 110 + 12 + 95, + + .vdisplay = 1280, + .vsync_start = 1280 + 100, + .vsync_end = 1280 + 100 + 2, + .vtotal = 1280 + 100 + 2 + 100, + + .width_mm = 62, + .height_mm = 110, +}; + static const struct drm_display_mode rpi_7inch_default_mode = { .clock = 83330, @@ -2000,6 +2205,14 @@ static const struct ili9881c_desc am8001280g_desc = { MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, }; +static const struct ili9881c_desc rpi_5inch_desc = { + .init = rpi_5inch_init, + .init_length = ARRAY_SIZE(rpi_5inch_init), + .mode = &rpi_5inch_default_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, + .lanes = 2, +}; + static const struct ili9881c_desc rpi_7inch_desc = { .init = rpi_7inch_init, .init_length = ARRAY_SIZE(rpi_7inch_init), @@ -2025,6 +2238,7 @@ static const struct of_device_id ili9881c_of_match[] = { { .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc }, { .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc }, { .compatible = "ampire,am8001280g", .data = &am8001280g_desc }, + { .compatible = "raspberrypi,dsi-5inch", &rpi_5inch_desc }, { .compatible = "raspberrypi,dsi-7inch", &rpi_7inch_desc }, { } }; diff --git a/drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c new file mode 100644 index 000000000000..8c00fde1c4a9 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016 XiaoMi, Inc. + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_device.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +static const struct regulator_bulk_data sharp_supplies[] = { + { .supply = "avdd" }, { .supply = "vddio" }, + { .supply = "vsp" }, { .supply = "vsn" }, +}; + +struct sharp_panel { + struct drm_panel panel; + struct mipi_dsi_device *dsi[2]; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; + + const struct drm_display_mode *mode; +}; + +static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel) +{ + return container_of(panel, struct sharp_panel, panel); +} + +static void sharp_panel_reset(struct sharp_panel *sharp) +{ + gpiod_set_value_cansleep(sharp->reset_gpio, 1); + usleep_range(2000, 3000); + gpiod_set_value_cansleep(sharp->reset_gpio, 0); + usleep_range(2000, 3000); +} + +static int sharp_panel_prepare(struct drm_panel *panel) +{ + struct sharp_panel *sharp = to_sharp_panel(panel); + struct device *dev = panel->dev; + struct mipi_dsi_device *dsi0 = sharp->dsi[0]; + struct mipi_dsi_device *dsi1 = sharp->dsi[1]; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL }; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(sharp_supplies), sharp->supplies); + if (ret) { + dev_err(dev, "error enabling regulators (%d)\n", ret); + return ret; + } + + msleep(24); + + if (sharp->reset_gpio) + sharp_panel_reset(sharp); + + msleep(32); + + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_EXIT_SLEEP_MODE); + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, + MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff); + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, + MIPI_DCS_WRITE_POWER_SAVE, 0x01); + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, + MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c); + + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_ON); + + return 0; +} + +static int sharp_panel_unprepare(struct drm_panel *panel) +{ + struct sharp_panel *sharp = to_sharp_panel(panel); + struct mipi_dsi_device *dsi0 = sharp->dsi[0]; + struct mipi_dsi_device *dsi1 = sharp->dsi[1]; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL }; + + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_OFF); + mipi_dsi_msleep(&dsi_ctx, 100); + mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_ENTER_SLEEP_MODE); + mipi_dsi_msleep(&dsi_ctx, 150); + + if (sharp->reset_gpio) + gpiod_set_value_cansleep(sharp->reset_gpio, 1); + + return regulator_bulk_disable(ARRAY_SIZE(sharp_supplies), sharp->supplies); +} + +static const struct drm_display_mode default_mode = { + .clock = (1536 + 136 + 28 + 28) * (2048 + 14 + 8 + 2) * 60 / 1000, + .hdisplay = 1536, + .hsync_start = 1536 + 136, + .hsync_end = 1536 + 136 + 28, + .htotal = 1536 + 136 + 28 + 28, + .vdisplay = 2048, + .vsync_start = 2048 + 14, + .vsync_end = 2048 + 14 + 8, + .vtotal = 2048 + 14 + 8 + 2, + .width_mm = 120, + .height_mm = 160, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static int sharp_panel_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + return drm_connector_helper_get_modes_fixed(connector, &default_mode); +} + +static const struct drm_panel_funcs sharp_panel_funcs = { + .unprepare = sharp_panel_unprepare, + .prepare = sharp_panel_prepare, + .get_modes = sharp_panel_get_modes, +}; + +static int sharp_panel_probe(struct mipi_dsi_device *dsi) +{ + const struct mipi_dsi_device_info info = { "sharp-link1", 0, NULL }; + struct device *dev = &dsi->dev; + struct device_node *dsi_r; + struct mipi_dsi_host *dsi_r_host; + struct sharp_panel *sharp; + int i, ret; + + sharp = devm_drm_panel_alloc(dev, struct sharp_panel, panel, + &sharp_panel_funcs, DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(sharp)) + return PTR_ERR(sharp); + + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(sharp_supplies), + sharp_supplies, &sharp->supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get supplies\n"); + + sharp->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(sharp->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(sharp->reset_gpio), + "failed to get reset GPIO\n"); + + /* Panel is always connected to two DSI hosts, DSI0 is left, DSI1 is right */ + dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi_r) + return dev_err_probe(dev, -ENODEV, "failed to find second DSI host node\n"); + + dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r); + of_node_put(dsi_r); + if (!dsi_r_host) + return dev_err_probe(dev, -EPROBE_DEFER, "cannot get secondary DSI host\n"); + + sharp->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi_r_host, &info); + if (IS_ERR(sharp->dsi[1])) + return dev_err_probe(dev, PTR_ERR(sharp->dsi[1]), + "second link registration failed\n"); + + sharp->dsi[0] = dsi; + mipi_dsi_set_drvdata(dsi, sharp); + + ret = drm_panel_of_backlight(&sharp->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + drm_panel_add(&sharp->panel); + + for (i = 0; i < ARRAY_SIZE(sharp->dsi); i++) { + if (!sharp->dsi[i]) + continue; + + sharp->dsi[i]->lanes = 4; + sharp->dsi[i]->format = MIPI_DSI_FMT_RGB888; + sharp->dsi[i]->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; + + ret = devm_mipi_dsi_attach(dev, sharp->dsi[i]); + if (ret < 0) { + drm_panel_remove(&sharp->panel); + return dev_err_probe(dev, ret, "failed to attach to DSI%d\n", i); + } + } + + return 0; +} + +static void sharp_panel_remove(struct mipi_dsi_device *dsi) +{ + struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi); + + drm_panel_remove(&sharp->panel); +} + +static const struct of_device_id sharp_of_match[] = { + { .compatible = "sharp,lq079l1sx01" }, + { } +}; +MODULE_DEVICE_TABLE(of, sharp_of_match); + +static struct mipi_dsi_driver sharp_panel_driver = { + .driver = { + .name = "panel-sharp-lq079l1sx01", + .of_match_table = sharp_of_match, + }, + .probe = sharp_panel_probe, + .remove = sharp_panel_remove, +}; +module_mipi_dsi_driver(sharp_panel_driver); + +MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>"); +MODULE_DESCRIPTION("Sharp LQ079L1SX01 panel driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 0019de93be1b..da6b71b70a46 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2889,6 +2889,38 @@ static const struct panel_desc innolux_zj070na_01p = { }, }; +static const struct display_timing jutouch_jt101tm023_timing = { + .pixelclock = { 66300000, 72400000, 78900000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 12, 72, 132 }, + .hback_porch = { 88, 88, 88 }, + .hsync_len = { 10, 10, 48 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 1, 15, 49 }, + .vback_porch = { 23, 23, 23 }, + .vsync_len = { 5, 6, 13 }, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc jutouch_jt101tm023 = { + .timings = &jutouch_jt101tm023_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 217, + .height = 136, + }, + .delay = { + .enable = 50, + .disable = 50, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + + static const struct display_timing koe_tx14d24vm1bpa_timing = { .pixelclock = { 5580000, 5850000, 6200000 }, .hactive = { 320, 320, 320 }, @@ -5209,6 +5241,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "innolux,zj070na-01p", .data = &innolux_zj070na_01p, }, { + .compatible = "jutouch,jt101tm023", + .data = &jutouch_jt101tm023, + }, { .compatible = "koe,tx14d24vm1bpa", .data = &koe_tx14d24vm1bpa, }, { diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index 909c280eab1f..e5e688cf98fd 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -3,6 +3,7 @@ * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ +#include <linux/backlight.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/property.h> @@ -20,6 +21,8 @@ struct visionox_rm69299_panel_desc { const struct drm_display_mode *mode; const u8 *init_seq; unsigned int init_seq_len; + int max_brightness; + int initial_brightness; }; struct visionox_rm69299 { @@ -192,7 +195,7 @@ static int visionox_rm69299_unprepare(struct drm_panel *panel) struct visionox_rm69299 *ctx = panel_to_ctx(panel); struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - ctx->dsi->mode_flags = 0; + ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); @@ -247,7 +250,7 @@ static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = { }; static const struct drm_display_mode visionox_rm69299_1080x2160_60hz = { - .clock = 158695, + .clock = (2160 + 8 + 4 + 4) * (1080 + 26 + 2 + 36) * 60 / 1000, .hdisplay = 1080, .hsync_start = 1080 + 26, .hsync_end = 1080 + 26 + 2, @@ -285,6 +288,63 @@ static const struct drm_panel_funcs visionox_rm69299_drm_funcs = { .get_modes = visionox_rm69299_get_modes, }; +static int visionox_rm69299_bl_get_brightness(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return brightness; +} + +static int visionox_rm69299_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return 0; +} + +static const struct backlight_ops visionox_rm69299_bl_ops = { + .update_status = visionox_rm69299_bl_update_status, + .get_brightness = visionox_rm69299_bl_get_brightness, +}; + +static struct backlight_device * +visionox_rm69299_create_backlight(struct visionox_rm69299 *ctx) +{ + struct device *dev = &ctx->dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = ctx->desc->initial_brightness, + .max_brightness = ctx->desc->max_brightness, + }; + + if (!ctx->desc->max_brightness) + return 0; + + return devm_backlight_device_register(dev, dev_name(dev), dev, ctx->dsi, + &visionox_rm69299_bl_ops, + &props); +} + static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) { struct device *dev = &dsi->dev; @@ -316,6 +376,11 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi) return PTR_ERR(ctx->reset_gpio); } + ctx->panel.backlight = visionox_rm69299_create_backlight(ctx); + if (IS_ERR(ctx->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), + "Failed to create backlight\n"); + drm_panel_add(&ctx->panel); dsi->lanes = 4; @@ -353,6 +418,8 @@ const struct visionox_rm69299_panel_desc visionox_rm69299_shift_desc = { .mode = &visionox_rm69299_1080x2160_60hz, .init_seq = (const u8 *)visionox_rm69299_1080x2160_60hz_init_seq, .init_seq_len = ARRAY_SIZE(visionox_rm69299_1080x2160_60hz_init_seq), + .max_brightness = 255, + .initial_brightness = 50, }; static const struct of_device_id visionox_rm69299_of_match[] = { diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index 077525a3ad68..1e73efad02a8 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -10,11 +10,13 @@ #include <linux/pm.h> #include <linux/regulator/consumer.h> #include <linux/spinlock.h> +#include <drm/drm_auth.h> #include <drm/drm_device.h> #include <drm/drm_mm.h> #include <drm/gpu_scheduler.h> #include "panfrost_devfreq.h" +#include "panfrost_job.h" struct panfrost_device; struct panfrost_mmu; @@ -22,7 +24,6 @@ struct panfrost_job_slot; struct panfrost_job; struct panfrost_perfcnt; -#define NUM_JOB_SLOTS 3 #define MAX_PM_DOMAINS 5 enum panfrost_drv_comp_bits { @@ -206,13 +207,19 @@ struct panfrost_engine_usage { struct panfrost_file_priv { struct panfrost_device *pfdev; - struct drm_sched_entity sched_entity[NUM_JOB_SLOTS]; + struct xarray jm_ctxs; struct panfrost_mmu *mmu; struct panfrost_engine_usage engine_usage; }; +static inline bool panfrost_high_prio_allowed(struct drm_file *file) +{ + /* Higher priorities require CAP_SYS_NICE or DRM_MASTER */ + return (capable(CAP_SYS_NICE) || drm_is_current_master(file)); +} + static inline struct panfrost_device *to_panfrost_device(struct drm_device *ddev) { return ddev->dev_private; diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 1ea6c509a5d5..22350ce8a08f 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -109,6 +109,14 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct #endif break; + case DRM_PANFROST_PARAM_ALLOWED_JM_CTX_PRIORITIES: + param->value = BIT(PANFROST_JM_CTX_PRIORITY_LOW) | + BIT(PANFROST_JM_CTX_PRIORITY_MEDIUM); + + if (panfrost_high_prio_allowed(file)) + param->value |= BIT(PANFROST_JM_CTX_PRIORITY_HIGH); + break; + default: return -EINVAL; } @@ -279,9 +287,13 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, struct panfrost_file_priv *file_priv = file->driver_priv; struct drm_panfrost_submit *args = data; struct drm_syncobj *sync_out = NULL; + struct panfrost_jm_ctx *jm_ctx; struct panfrost_job *job; int ret = 0, slot; + if (args->pad) + return -EINVAL; + if (!args->jc) return -EINVAL; @@ -294,10 +306,16 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, return -ENODEV; } + jm_ctx = panfrost_jm_ctx_from_handle(file, args->jm_ctx_handle); + if (!jm_ctx) { + ret = -EINVAL; + goto out_put_syncout; + } + job = kzalloc(sizeof(*job), GFP_KERNEL); if (!job) { ret = -ENOMEM; - goto out_put_syncout; + goto out_put_jm_ctx; } kref_init(&job->refcount); @@ -307,12 +325,13 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, job->requirements = args->requirements; job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev); job->mmu = file_priv->mmu; + job->ctx = panfrost_jm_ctx_get(jm_ctx); job->engine_usage = &file_priv->engine_usage; slot = panfrost_job_get_slot(job); ret = drm_sched_job_init(&job->base, - &file_priv->sched_entity[slot], + &jm_ctx->slot_entity[slot], 1, NULL, file->client_id); if (ret) goto out_put_job; @@ -338,6 +357,8 @@ out_cleanup_job: drm_sched_job_cleanup(&job->base); out_put_job: panfrost_job_put(job); +out_put_jm_ctx: + panfrost_jm_ctx_put(jm_ctx); out_put_syncout: if (sync_out) drm_syncobj_put(sync_out); @@ -536,6 +557,27 @@ err_put_obj: return ret; } +static int panfrost_ioctl_jm_ctx_create(struct drm_device *dev, void *data, + struct drm_file *file) +{ + return panfrost_jm_ctx_create(file, data); +} + +static int panfrost_ioctl_jm_ctx_destroy(struct drm_device *dev, void *data, + struct drm_file *file) +{ + const struct drm_panfrost_jm_ctx_destroy *args = data; + + if (args->pad) + return -EINVAL; + + /* We can't destroy the default context created when the file is opened. */ + if (!args->handle) + return -EINVAL; + + return panfrost_jm_ctx_destroy(file, args->handle); +} + int panfrost_unstable_ioctl_check(void) { if (!unstable_ioctls) @@ -564,7 +606,7 @@ panfrost_open(struct drm_device *dev, struct drm_file *file) goto err_free; } - ret = panfrost_job_open(panfrost_priv); + ret = panfrost_job_open(file); if (ret) goto err_job; @@ -583,7 +625,7 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file) struct panfrost_file_priv *panfrost_priv = file->driver_priv; panfrost_perfcnt_close(file); - panfrost_job_close(panfrost_priv); + panfrost_job_close(file); panfrost_mmu_ctx_put(panfrost_priv->mmu); kfree(panfrost_priv); @@ -603,6 +645,8 @@ static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = { PANFROST_IOCTL(PERFCNT_DUMP, perfcnt_dump, DRM_RENDER_ALLOW), PANFROST_IOCTL(MADVISE, madvise, DRM_RENDER_ALLOW), PANFROST_IOCTL(SET_LABEL_BO, set_label_bo, DRM_RENDER_ALLOW), + PANFROST_IOCTL(JM_CTX_CREATE, jm_ctx_create, DRM_RENDER_ALLOW), + PANFROST_IOCTL(JM_CTX_DESTROY, jm_ctx_destroy, DRM_RENDER_ALLOW), }; static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev, @@ -672,6 +716,47 @@ static int panthor_gems_show(struct seq_file *m, void *data) return 0; } +static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle, + struct seq_file *m) +{ + struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev; + const char *prio = "UNKNOWN"; + + static const char * const prios[] = { + [DRM_SCHED_PRIORITY_HIGH] = "HIGH", + [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL", + [DRM_SCHED_PRIORITY_LOW] = "LOW", + }; + + if (jm_ctx->slot_entity[0].priority != + jm_ctx->slot_entity[1].priority) + drm_warn(ddev, "Slot priorities should be the same in a single context"); + + if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios)) + prio = prios[jm_ctx->slot_entity[0].priority]; + + seq_printf(m, " JM context %u: priority %s\n", handle, prio); +} + +static int show_file_jm_ctxs(struct panfrost_file_priv *pfile, + struct seq_file *m) +{ + struct panfrost_jm_ctx *jm_ctx; + unsigned long i; + + xa_lock(&pfile->jm_ctxs); + xa_for_each(&pfile->jm_ctxs, i, jm_ctx) { + jm_ctx = panfrost_jm_ctx_get(jm_ctx); + xa_unlock(&pfile->jm_ctxs); + show_panfrost_jm_ctx(jm_ctx, i, m); + panfrost_jm_ctx_put(jm_ctx); + xa_lock(&pfile->jm_ctxs); + } + xa_unlock(&pfile->jm_ctxs); + + return 0; +} + static struct drm_info_list panthor_debugfs_list[] = { {"gems", panthor_gems_show, 0, NULL}, }; @@ -685,9 +770,64 @@ static int panthor_gems_debugfs_init(struct drm_minor *minor) return 0; } +static int show_each_file(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + int (*show)(struct panfrost_file_priv *, struct seq_file *) = + node->info_ent->data; + struct drm_file *file; + int ret; + + ret = mutex_lock_interruptible(&ddev->filelist_mutex); + if (ret) + return ret; + + list_for_each_entry(file, &ddev->filelist, lhead) { + struct task_struct *task; + struct panfrost_file_priv *pfile = file->driver_priv; + struct pid *pid; + + /* + * Although we have a valid reference on file->pid, that does + * not guarantee that the task_struct who called get_pid() is + * still alive (e.g. get_pid(current) => fork() => exit()). + * Therefore, we need to protect this ->comm access using RCU. + */ + rcu_read_lock(); + pid = rcu_dereference(file->pid); + task = pid_task(pid, PIDTYPE_TGID); + seq_printf(m, "client_id %8llu pid %8d command %s:\n", + file->client_id, pid_nr(pid), + task ? task->comm : "<unknown>"); + rcu_read_unlock(); + + ret = show(pfile, m); + if (ret < 0) + break; + + seq_puts(m, "\n"); + } + + mutex_unlock(&ddev->filelist_mutex); + return ret; +} + +static struct drm_info_list panfrost_sched_debugfs_list[] = { + { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs }, +}; + +static void panfrost_sched_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(panfrost_sched_debugfs_list, + ARRAY_SIZE(panfrost_sched_debugfs_list), + minor->debugfs_root, minor); +} + static void panfrost_debugfs_init(struct drm_minor *minor) { panthor_gems_debugfs_init(minor); + panfrost_sched_debugfs_init(minor); } #endif @@ -699,6 +839,8 @@ static void panfrost_debugfs_init(struct drm_minor *minor) * - 1.3 - adds JD_REQ_CYCLE_COUNT job requirement for SUBMIT * - adds SYSTEM_TIMESTAMP and SYSTEM_TIMESTAMP_FREQUENCY queries * - 1.4 - adds SET_LABEL_BO + * - 1.5 - adds JM_CTX_{CREATE,DESTROY} ioctls and extend SUBMIT to allow + * context creation with configurable priorities/affinity */ static const struct drm_driver panfrost_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ, @@ -711,7 +853,7 @@ static const struct drm_driver panfrost_drm_driver = { .name = "panfrost", .desc = "panfrost DRM", .major = 1, - .minor = 4, + .minor = 5, .gem_create_object = panfrost_gem_create_object, .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index 82acabb21b27..c47d14eabbae 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -22,6 +22,7 @@ #include "panfrost_mmu.h" #include "panfrost_dump.h" +#define MAX_JM_CTX_PER_FILE 64 #define JOB_TIMEOUT_MS 500 #define job_write(dev, reg, data) writel(data, dev->iomem + (reg)) @@ -359,6 +360,7 @@ static void panfrost_job_cleanup(struct kref *ref) kvfree(job->bos); } + panfrost_jm_ctx_put(job->ctx); kfree(job); } @@ -383,6 +385,9 @@ static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job) int slot = panfrost_job_get_slot(job); struct dma_fence *fence = NULL; + if (job->ctx->destroyed) + return ERR_PTR(-ECANCELED); + if (unlikely(job->base.s_fence->finished.error)) return NULL; @@ -917,39 +922,176 @@ void panfrost_job_fini(struct panfrost_device *pfdev) destroy_workqueue(pfdev->reset.wq); } -int panfrost_job_open(struct panfrost_file_priv *panfrost_priv) +int panfrost_job_open(struct drm_file *file) +{ + struct panfrost_file_priv *panfrost_priv = file->driver_priv; + int ret; + + struct drm_panfrost_jm_ctx_create default_jm_ctx = { + .priority = PANFROST_JM_CTX_PRIORITY_MEDIUM, + }; + + xa_init_flags(&panfrost_priv->jm_ctxs, XA_FLAGS_ALLOC); + + ret = panfrost_jm_ctx_create(file, &default_jm_ctx); + if (ret) + return ret; + + /* We expect the default context to be assigned handle 0. */ + if (WARN_ON(default_jm_ctx.handle)) + return -EINVAL; + + return 0; +} + +void panfrost_job_close(struct drm_file *file) +{ + struct panfrost_file_priv *panfrost_priv = file->driver_priv; + struct panfrost_jm_ctx *jm_ctx; + unsigned long i; + + xa_for_each(&panfrost_priv->jm_ctxs, i, jm_ctx) + panfrost_jm_ctx_destroy(file, i); + + xa_destroy(&panfrost_priv->jm_ctxs); +} + +int panfrost_job_is_idle(struct panfrost_device *pfdev) { - struct panfrost_device *pfdev = panfrost_priv->pfdev; struct panfrost_job_slot *js = pfdev->js; - struct drm_gpu_scheduler *sched; - int ret, i; + int i; for (i = 0; i < NUM_JOB_SLOTS; i++) { - sched = &js->queue[i].sched; - ret = drm_sched_entity_init(&panfrost_priv->sched_entity[i], - DRM_SCHED_PRIORITY_NORMAL, &sched, - 1, NULL); - if (WARN_ON(ret)) - return ret; + /* If there are any jobs in the HW queue, we're not idle */ + if (atomic_read(&js->queue[i].sched.credit_count)) + return false; + } + + return true; +} + +static void panfrost_jm_ctx_release(struct kref *kref) +{ + struct panfrost_jm_ctx *jm_ctx = container_of(kref, struct panfrost_jm_ctx, refcnt); + + WARN_ON(!jm_ctx->destroyed); + + for (u32 i = 0; i < ARRAY_SIZE(jm_ctx->slot_entity); i++) + drm_sched_entity_destroy(&jm_ctx->slot_entity[i]); + + kfree(jm_ctx); +} + +void +panfrost_jm_ctx_put(struct panfrost_jm_ctx *jm_ctx) +{ + if (jm_ctx) + kref_put(&jm_ctx->refcnt, panfrost_jm_ctx_release); +} + +struct panfrost_jm_ctx * +panfrost_jm_ctx_get(struct panfrost_jm_ctx *jm_ctx) +{ + if (jm_ctx) + kref_get(&jm_ctx->refcnt); + + return jm_ctx; +} + +struct panfrost_jm_ctx * +panfrost_jm_ctx_from_handle(struct drm_file *file, u32 handle) +{ + struct panfrost_file_priv *priv = file->driver_priv; + struct panfrost_jm_ctx *jm_ctx; + + xa_lock(&priv->jm_ctxs); + jm_ctx = panfrost_jm_ctx_get(xa_load(&priv->jm_ctxs, handle)); + xa_unlock(&priv->jm_ctxs); + + return jm_ctx; +} + +static int jm_ctx_prio_to_drm_sched_prio(struct drm_file *file, + enum drm_panfrost_jm_ctx_priority in, + enum drm_sched_priority *out) +{ + switch (in) { + case PANFROST_JM_CTX_PRIORITY_LOW: + *out = DRM_SCHED_PRIORITY_LOW; + return 0; + case PANFROST_JM_CTX_PRIORITY_MEDIUM: + *out = DRM_SCHED_PRIORITY_NORMAL; + return 0; + case PANFROST_JM_CTX_PRIORITY_HIGH: + if (!panfrost_high_prio_allowed(file)) + return -EACCES; + + *out = DRM_SCHED_PRIORITY_HIGH; + return 0; + default: + return -EINVAL; } +} + +int panfrost_jm_ctx_create(struct drm_file *file, + struct drm_panfrost_jm_ctx_create *args) +{ + struct panfrost_file_priv *priv = file->driver_priv; + struct panfrost_device *pfdev = priv->pfdev; + enum drm_sched_priority sched_prio; + struct panfrost_jm_ctx *jm_ctx; + int ret; + + jm_ctx = kzalloc(sizeof(*jm_ctx), GFP_KERNEL); + if (!jm_ctx) + return -ENOMEM; + + kref_init(&jm_ctx->refcnt); + + ret = jm_ctx_prio_to_drm_sched_prio(file, args->priority, &sched_prio); + if (ret) + goto err_put_jm_ctx; + + for (u32 i = 0; i < NUM_JOB_SLOTS; i++) { + struct drm_gpu_scheduler *sched = &pfdev->js->queue[i].sched; + + ret = drm_sched_entity_init(&jm_ctx->slot_entity[i], sched_prio, + &sched, 1, NULL); + if (ret) + goto err_put_jm_ctx; + } + + ret = xa_alloc(&priv->jm_ctxs, &args->handle, jm_ctx, + XA_LIMIT(0, MAX_JM_CTX_PER_FILE), GFP_KERNEL); + if (ret) + goto err_put_jm_ctx; + return 0; + +err_put_jm_ctx: + jm_ctx->destroyed = true; + panfrost_jm_ctx_put(jm_ctx); + return ret; } -void panfrost_job_close(struct panfrost_file_priv *panfrost_priv) +int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle) { - struct panfrost_device *pfdev = panfrost_priv->pfdev; - int i; + struct panfrost_file_priv *priv = file->driver_priv; + struct panfrost_device *pfdev = priv->pfdev; + struct panfrost_jm_ctx *jm_ctx; - for (i = 0; i < NUM_JOB_SLOTS; i++) - drm_sched_entity_destroy(&panfrost_priv->sched_entity[i]); + jm_ctx = xa_erase(&priv->jm_ctxs, handle); + if (!jm_ctx) + return -EINVAL; + + jm_ctx->destroyed = true; /* Kill in-flight jobs */ spin_lock(&pfdev->js->job_lock); - for (i = 0; i < NUM_JOB_SLOTS; i++) { - struct drm_sched_entity *entity = &panfrost_priv->sched_entity[i]; - int j; + for (u32 i = 0; i < ARRAY_SIZE(jm_ctx->slot_entity); i++) { + struct drm_sched_entity *entity = &jm_ctx->slot_entity[i]; - for (j = ARRAY_SIZE(pfdev->jobs[0]) - 1; j >= 0; j--) { + for (int j = ARRAY_SIZE(pfdev->jobs[0]) - 1; j >= 0; j--) { struct panfrost_job *job = pfdev->jobs[i][j]; u32 cmd; @@ -980,18 +1122,7 @@ void panfrost_job_close(struct panfrost_file_priv *panfrost_priv) } } spin_unlock(&pfdev->js->job_lock); -} - -int panfrost_job_is_idle(struct panfrost_device *pfdev) -{ - struct panfrost_job_slot *js = pfdev->js; - int i; - for (i = 0; i < NUM_JOB_SLOTS; i++) { - /* If there are any jobs in the HW queue, we're not idle */ - if (atomic_read(&js->queue[i].sched.credit_count)) - return false; - } - - return true; + panfrost_jm_ctx_put(jm_ctx); + return 0; } diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h index ec581b97852b..5a30ff1503c6 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.h +++ b/drivers/gpu/drm/panfrost/panfrost_job.h @@ -18,6 +18,7 @@ struct panfrost_job { struct panfrost_device *pfdev; struct panfrost_mmu *mmu; + struct panfrost_jm_ctx *ctx; /* Fence to be signaled by IRQ handler when the job is complete. */ struct dma_fence *done_fence; @@ -39,10 +40,30 @@ struct panfrost_job { u64 start_cycles; }; +struct panfrost_js_ctx { + struct drm_sched_entity sched_entity; + bool enabled; +}; + +#define NUM_JOB_SLOTS 3 + +struct panfrost_jm_ctx { + struct kref refcnt; + bool destroyed; + struct drm_sched_entity slot_entity[NUM_JOB_SLOTS]; +}; + +int panfrost_jm_ctx_create(struct drm_file *file, + struct drm_panfrost_jm_ctx_create *args); +int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle); +void panfrost_jm_ctx_put(struct panfrost_jm_ctx *jm_ctx); +struct panfrost_jm_ctx *panfrost_jm_ctx_get(struct panfrost_jm_ctx *jm_ctx); +struct panfrost_jm_ctx *panfrost_jm_ctx_from_handle(struct drm_file *file, u32 handle); + int panfrost_job_init(struct panfrost_device *pfdev); void panfrost_job_fini(struct panfrost_device *pfdev); -int panfrost_job_open(struct panfrost_file_priv *panfrost_priv); -void panfrost_job_close(struct panfrost_file_priv *panfrost_priv); +int panfrost_job_open(struct drm_file *file); +void panfrost_job_close(struct drm_file *file); int panfrost_job_get_slot(struct panfrost_job *job); int panfrost_job_push(struct panfrost_job *job); void panfrost_job_put(struct panfrost_job *job); diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 4c202fc5ce05..fdbe89ef7f43 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -1105,7 +1105,7 @@ static int panthor_ioctl_group_create(struct drm_device *ddev, void *data, if (ret) goto out; - ret = panthor_group_create(pfile, args, queue_args); + ret = panthor_group_create(pfile, args, queue_args, file->client_id); if (ret < 0) goto out; args->group_handle = ret; diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 3d1f57e3990f..f5e01cb16cfc 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -360,6 +360,9 @@ struct panthor_queue { /** @entity: DRM scheduling entity used for this queue. */ struct drm_sched_entity entity; + /** @name: DRM scheduler name for this queue. */ + char *name; + /** * @remaining_time: Time remaining before the job timeout expires. * @@ -900,6 +903,8 @@ static void group_free_queue(struct panthor_group *group, struct panthor_queue * if (queue->scheduler.ops) drm_sched_fini(&queue->scheduler); + kfree(queue->name); + panthor_queue_put_syncwait_obj(queue); panthor_kernel_bo_destroy(queue->ringbuf); @@ -1411,7 +1416,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, fault = cs_iface->output->fault; info = cs_iface->output->fault_info; - if (queue && CS_EXCEPTION_TYPE(fault) == DRM_PANTHOR_EXCEPTION_CS_INHERIT_FAULT) { + if (queue) { u64 cs_extract = queue->iface.output->extract; struct panthor_job *job; @@ -3307,9 +3312,10 @@ static u32 calc_profiling_ringbuf_num_slots(struct panthor_device *ptdev, static struct panthor_queue * group_create_queue(struct panthor_group *group, - const struct drm_panthor_queue_create *args) + const struct drm_panthor_queue_create *args, + u64 drm_client_id, u32 gid, u32 qid) { - const struct drm_sched_init_args sched_args = { + struct drm_sched_init_args sched_args = { .ops = &panthor_queue_sched_ops, .submit_wq = group->ptdev->scheduler->wq, .num_rqs = 1, @@ -3322,7 +3328,6 @@ group_create_queue(struct panthor_group *group, .credit_limit = args->ringbuf_size / sizeof(u64), .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), .timeout_wq = group->ptdev->reset.wq, - .name = "panthor-queue", .dev = group->ptdev->base.dev, }; struct drm_gpu_scheduler *drm_sched; @@ -3397,6 +3402,15 @@ group_create_queue(struct panthor_group *group, if (ret) goto err_free_queue; + /* assign a unique name */ + queue->name = kasprintf(GFP_KERNEL, "panthor-queue-%llu-%u-%u", drm_client_id, gid, qid); + if (!queue->name) { + ret = -ENOMEM; + goto err_free_queue; + } + + sched_args.name = queue->name; + ret = drm_sched_init(&queue->scheduler, &sched_args); if (ret) goto err_free_queue; @@ -3446,7 +3460,8 @@ static void add_group_kbo_sizes(struct panthor_device *ptdev, int panthor_group_create(struct panthor_file *pfile, const struct drm_panthor_group_create *group_args, - const struct drm_panthor_queue_create *queue_args) + const struct drm_panthor_queue_create *queue_args, + u64 drm_client_id) { struct panthor_device *ptdev = pfile->ptdev; struct panthor_group_pool *gpool = pfile->groups; @@ -3539,12 +3554,16 @@ int panthor_group_create(struct panthor_file *pfile, memset(group->syncobjs->kmap, 0, group_args->queues.count * sizeof(struct panthor_syncobj_64b)); + ret = xa_alloc(&gpool->xa, &gid, group, XA_LIMIT(1, MAX_GROUPS_PER_POOL), GFP_KERNEL); + if (ret) + goto err_put_group; + for (i = 0; i < group_args->queues.count; i++) { - group->queues[i] = group_create_queue(group, &queue_args[i]); + group->queues[i] = group_create_queue(group, &queue_args[i], drm_client_id, gid, i); if (IS_ERR(group->queues[i])) { ret = PTR_ERR(group->queues[i]); group->queues[i] = NULL; - goto err_put_group; + goto err_erase_gid; } group->queue_count++; @@ -3552,10 +3571,6 @@ int panthor_group_create(struct panthor_file *pfile, group->idle_queues = GENMASK(group->queue_count - 1, 0); - ret = xa_alloc(&gpool->xa, &gid, group, XA_LIMIT(1, MAX_GROUPS_PER_POOL), GFP_KERNEL); - if (ret) - goto err_put_group; - mutex_lock(&sched->reset.lock); if (atomic_read(&sched->reset.in_progress)) { panthor_group_stop(group); @@ -3574,6 +3589,9 @@ int panthor_group_create(struct panthor_file *pfile, return gid; +err_erase_gid: + xa_erase(&gpool->xa, gid); + err_put_group: group_put(group); return ret; diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h index 742b0b4ff3a3..f4a475aa34c0 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.h +++ b/drivers/gpu/drm/panthor/panthor_sched.h @@ -21,7 +21,8 @@ struct panthor_job; int panthor_group_create(struct panthor_file *pfile, const struct drm_panthor_group_create *group_args, - const struct drm_panthor_queue_create *queue_args); + const struct drm_panthor_queue_create *queue_args, + u64 drm_client_id); int panthor_group_destroy(struct panthor_file *pfile, u32 group_handle); int panthor_group_get_state(struct panthor_file *pfile, struct drm_panthor_group_get_state *get_state); diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c index b9fe926a49e8..6d567e5c7c6f 100644 --- a/drivers/gpu/drm/pl111/pl111_display.c +++ b/drivers/gpu/drm/pl111/pl111_display.c @@ -473,12 +473,15 @@ static int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate, return best_div; } -static long pl111_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int pl111_clk_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - int div = pl111_clk_div_choose_div(hw, rate, prate, true); + int div = pl111_clk_div_choose_div(hw, req->rate, + &req->best_parent_rate, true); - return DIV_ROUND_UP_ULL(*prate, div); + req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div); + + return 0; } static unsigned long pl111_clk_div_recalc_rate(struct clk_hw *hw, @@ -528,7 +531,7 @@ static int pl111_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops pl111_clk_div_ops = { .recalc_rate = pl111_clk_div_recalc_rate, - .round_rate = pl111_clk_div_round_rate, + .determine_rate = pl111_clk_div_determine_rate, .set_rate = pl111_clk_div_set_rate, }; diff --git a/drivers/gpu/drm/qxl/qxl_gem.c b/drivers/gpu/drm/qxl/qxl_gem.c index fc5e3763c359..d26043424e95 100644 --- a/drivers/gpu/drm/qxl/qxl_gem.c +++ b/drivers/gpu/drm/qxl/qxl_gem.c @@ -39,7 +39,7 @@ void qxl_gem_object_free(struct drm_gem_object *gobj) qxl_surface_evict(qdev, qobj, false); tbo = &qobj->tbo; - ttm_bo_put(tbo); + ttm_bo_fini(tbo); } int qxl_gem_object_create(struct qxl_device *qdev, int size, diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index f86773f3db20..18ca1bcfd2f9 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -86,7 +86,7 @@ static void radeon_gem_object_free(struct drm_gem_object *gobj) if (robj) { radeon_mn_unregister(robj); - ttm_bo_put(&robj->tbo); + ttm_bo_fini(&robj->tbo); } } diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c index 216219accfd9..6294443f6068 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c @@ -11,6 +11,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_device.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -407,8 +408,8 @@ int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { struct rcar_du_device *rcdu = to_rcar_du_device(dev); - unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); unsigned int align; + int ret; /* * The R8A7779 DU requires a 16 pixels pitch alignment as documented, @@ -419,7 +420,9 @@ int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, else align = 16 * args->bpp / 8; - args->pitch = roundup(min_pitch, align); + ret = drm_mode_size_dumb(dev, args, align, 0); + if (ret) + return ret; return drm_gem_dma_dumb_create_internal(file, dev, args); } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 6330b883efc3..3bd06202e232 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -9,6 +9,7 @@ #include <linux/vmalloc.h> #include <drm/drm.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_fb_helper.h> #include <drm/drm_gem.h> #include <drm/drm_gem_dma_helper.h> @@ -403,13 +404,12 @@ int rockchip_gem_dumb_create(struct drm_file *file_priv, struct drm_mode_create_dumb *args) { struct rockchip_gem_object *rk_obj; - int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + int ret; - /* - * align to 64 bytes since Mali requires it. - */ - args->pitch = ALIGN(min_pitch, 64); - args->size = args->pitch * args->height; + /* 64-byte alignment required by Mali */ + ret = drm_mode_size_dumb(dev, args, SZ_64, 0); + if (ret) + return ret; rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size, &args->handle); diff --git a/drivers/gpu/drm/scheduler/tests/sched_tests.h b/drivers/gpu/drm/scheduler/tests/sched_tests.h index 7f31d35780cc..553d45abd057 100644 --- a/drivers/gpu/drm/scheduler/tests/sched_tests.h +++ b/drivers/gpu/drm/scheduler/tests/sched_tests.h @@ -31,9 +31,8 @@ * * @base: DRM scheduler base class * @test: Backpointer to owning the kunit test case - * @lock: Lock to protect the simulated @hw_timeline, @job_list and @done_list + * @lock: Lock to protect the simulated @hw_timeline and @job_list * @job_list: List of jobs submitted to the mock GPU - * @done_list: List of jobs completed by the mock GPU * @hw_timeline: Simulated hardware timeline has a @context, @next_seqno and * @cur_seqno for implementing a struct dma_fence signaling the * simulated job completion. diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index eec43d1a5595..7e2e69ce890f 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -1016,15 +1016,9 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8); - ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); - if (ret) - return ret; - iosys_map_set_vaddr(&dst, buf); drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); - drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); - ssd130x_update_rect(ssd130x, rect, buf, data_array); return ret; @@ -1048,15 +1042,9 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb, dst_pitch = drm_rect_width(rect); - ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); - if (ret) - return ret; - iosys_map_set_vaddr(&dst, buf); drm_fb_xrgb8888_to_gray8(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); - drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); - ssd132x_update_rect(ssd130x, rect, buf, data_array); return ret; @@ -1078,15 +1066,9 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb, dst_pitch = drm_format_info_min_pitch(fi, 0, drm_rect_width(rect)); - ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); - if (ret) - return ret; - iosys_map_set_vaddr(&dst, data_array); drm_fb_xrgb8888_to_rgb332(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state); - drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); - ssd133x_update_rect(ssd130x, rect, data_array, dst_pitch); return ret; @@ -1232,6 +1214,9 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane, if (!drm_dev_enter(drm, &idx)) return; + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE)) + goto out_drm_dev_exit; + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); drm_atomic_for_each_plane_damage(&iter, &damage) { dst_clip = plane_state->dst; @@ -1245,6 +1230,9 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane, &shadow_plane_state->fmtcnv_state); } + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + +out_drm_dev_exit: drm_dev_exit(idx); } @@ -1267,6 +1255,9 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane, if (!drm_dev_enter(drm, &idx)) return; + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE)) + goto out_drm_dev_exit; + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); drm_atomic_for_each_plane_damage(&iter, &damage) { dst_clip = plane_state->dst; @@ -1280,6 +1271,9 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane, &shadow_plane_state->fmtcnv_state); } + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + +out_drm_dev_exit: drm_dev_exit(idx); } @@ -1301,6 +1295,9 @@ static void ssd133x_primary_plane_atomic_update(struct drm_plane *plane, if (!drm_dev_enter(drm, &idx)) return; + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE)) + goto out_drm_dev_exit; + drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); drm_atomic_for_each_plane_damage(&iter, &damage) { dst_clip = plane_state->dst; @@ -1313,6 +1310,9 @@ static void ssd133x_primary_plane_atomic_update(struct drm_plane *plane, &shadow_plane_state->fmtcnv_state); } + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + +out_drm_dev_exit: drm_dev_exit(idx); } @@ -1393,7 +1393,7 @@ static void ssd130x_primary_plane_reset(struct drm_plane *plane) { struct ssd130x_plane_state *ssd130x_state; - WARN_ON(plane->state); + drm_WARN_ON_ONCE(plane->dev, plane->state); ssd130x_state = kzalloc(sizeof(*ssd130x_state), GFP_KERNEL); if (!ssd130x_state) @@ -1408,7 +1408,7 @@ static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_ struct ssd130x_plane_state *old_ssd130x_state; struct ssd130x_plane_state *ssd130x_state; - if (WARN_ON(!plane->state)) + if (drm_WARN_ON_ONCE(plane->dev, !plane->state)) return NULL; old_ssd130x_state = to_ssd130x_plane_state(plane->state); @@ -1473,15 +1473,7 @@ static enum drm_mode_status ssd130x_crtc_mode_valid(struct drm_crtc *crtc, { struct ssd130x_device *ssd130x = drm_to_ssd130x(crtc->dev); - if (mode->hdisplay != ssd130x->mode.hdisplay && - mode->vdisplay != ssd130x->mode.vdisplay) - return MODE_ONE_SIZE; - else if (mode->hdisplay != ssd130x->mode.hdisplay) - return MODE_ONE_WIDTH; - else if (mode->vdisplay != ssd130x->mode.vdisplay) - return MODE_ONE_HEIGHT; - - return MODE_OK; + return drm_crtc_helper_mode_valid_fixed(crtc, mode, &ssd130x->mode); } static int ssd130x_crtc_atomic_check(struct drm_crtc *crtc, @@ -1498,7 +1490,7 @@ static int ssd130x_crtc_atomic_check(struct drm_crtc *crtc, if (ret) return ret; - ssd130x_state->data_array = kmalloc(ssd130x->width * pages, GFP_KERNEL); + ssd130x_state->data_array = kmalloc_array(ssd130x->width, pages, GFP_KERNEL); if (!ssd130x_state->data_array) return -ENOMEM; @@ -1519,7 +1511,7 @@ static int ssd132x_crtc_atomic_check(struct drm_crtc *crtc, if (ret) return ret; - ssd130x_state->data_array = kmalloc(columns * ssd130x->height, GFP_KERNEL); + ssd130x_state->data_array = kmalloc_array(columns, ssd130x->height, GFP_KERNEL); if (!ssd130x_state->data_array) return -ENOMEM; @@ -1546,7 +1538,7 @@ static int ssd133x_crtc_atomic_check(struct drm_crtc *crtc, pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width); - ssd130x_state->data_array = kmalloc(pitch * ssd130x->height, GFP_KERNEL); + ssd130x_state->data_array = kmalloc_array(pitch, ssd130x->height, GFP_KERNEL); if (!ssd130x_state->data_array) return -ENOMEM; @@ -1558,7 +1550,7 @@ static void ssd130x_crtc_reset(struct drm_crtc *crtc) { struct ssd130x_crtc_state *ssd130x_state; - WARN_ON(crtc->state); + drm_WARN_ON_ONCE(crtc->dev, crtc->state); ssd130x_state = kzalloc(sizeof(*ssd130x_state), GFP_KERNEL); if (!ssd130x_state) @@ -1572,7 +1564,7 @@ static struct drm_crtc_state *ssd130x_crtc_duplicate_state(struct drm_crtc *crtc struct ssd130x_crtc_state *old_ssd130x_state; struct ssd130x_crtc_state *ssd130x_state; - if (WARN_ON(!crtc->state)) + if (drm_WARN_ON_ONCE(crtc->dev, !crtc->state)) return NULL; old_ssd130x_state = to_ssd130x_crtc_state(crtc->state); @@ -1740,20 +1732,8 @@ static const struct drm_encoder_funcs ssd130x_encoder_funcs = { static int ssd130x_connector_get_modes(struct drm_connector *connector) { struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev); - struct drm_display_mode *mode; - struct device *dev = ssd130x->dev; - - mode = drm_mode_duplicate(connector->dev, &ssd130x->mode); - if (!mode) { - dev_err(dev, "Failed to duplicated mode\n"); - return 0; - } - - drm_mode_probed_add(connector, mode); - drm_set_preferred_mode(connector, mode->hdisplay, mode->vdisplay); - /* There is only a single mode */ - return 1; + return drm_connector_helper_get_modes_fixed(connector, &ssd130x->mode); } static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = { @@ -1887,10 +1867,14 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) mode->type = DRM_MODE_TYPE_DRIVER; mode->clock = 1; - mode->hdisplay = mode->htotal = ssd130x->width; - mode->hsync_start = mode->hsync_end = ssd130x->width; - mode->vdisplay = mode->vtotal = ssd130x->height; - mode->vsync_start = mode->vsync_end = ssd130x->height; + mode->hdisplay = ssd130x->width; + mode->htotal = ssd130x->width; + mode->hsync_start = ssd130x->width; + mode->hsync_end = ssd130x->width; + mode->vdisplay = ssd130x->height; + mode->vtotal = ssd130x->height; + mode->vsync_start = ssd130x->height; + mode->vsync_end = ssd130x->height; mode->width_mm = 27; mode->height_mm = 27; diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index 2c7bc064bc66..58eae6804cc8 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -274,8 +274,8 @@ static unsigned long dw_mipi_dsi_clk_recalc_rate(struct clk_hw *hw, return (unsigned long)pll_out_khz * 1000; } -static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int dw_mipi_dsi_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw); unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; @@ -283,14 +283,14 @@ static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate, DRM_DEBUG_DRIVER("\n"); - pll_in_khz = (unsigned int)(*parent_rate / 1000); + pll_in_khz = (unsigned int)(req->best_parent_rate / 1000); /* Compute best pll parameters */ idf = 0; ndiv = 0; odf = 0; - ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000, + ret = dsi_pll_get_params(dsi, pll_in_khz, req->rate / 1000, &idf, &ndiv, &odf); if (ret) DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); @@ -298,7 +298,9 @@ static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate, /* Get the adjusted pll out value */ pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); - return pll_out_khz * 1000; + req->rate = pll_out_khz * 1000; + + return 0; } static int dw_mipi_dsi_clk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -351,7 +353,7 @@ static const struct clk_ops dw_mipi_dsi_stm_clk_ops = { .disable = dw_mipi_dsi_clk_disable, .is_enabled = dw_mipi_dsi_clk_is_enabled, .recalc_rate = dw_mipi_dsi_clk_recalc_rate, - .round_rate = dw_mipi_dsi_clk_round_rate, + .determine_rate = dw_mipi_dsi_clk_determine_rate, .set_rate = dw_mipi_dsi_clk_set_rate, }; diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c index 07788e8d3d83..fe38c0984b2b 100644 --- a/drivers/gpu/drm/stm/lvds.c +++ b/drivers/gpu/drm/stm/lvds.c @@ -682,8 +682,8 @@ static unsigned long lvds_pixel_clk_recalc_rate(struct clk_hw *hw, return (unsigned long)lvds->pixel_clock_rate; } -static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int lvds_pixel_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px); unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0; @@ -703,7 +703,7 @@ static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate, mode = list_first_entry(&connector->modes, struct drm_display_mode, head); - pll_in_khz = (unsigned int)(*parent_rate / 1000); + pll_in_khz = (unsigned int)(req->best_parent_rate / 1000); if (lvds_is_dual_link(lvds->link_type)) multiplier = 2; @@ -719,14 +719,16 @@ static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate, lvds->pixel_clock_rate = (unsigned long)pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv) * 1000 * multiplier / 7; - return lvds->pixel_clock_rate; + req->rate = lvds->pixel_clock_rate; + + return 0; } static const struct clk_ops lvds_pixel_clk_ops = { .enable = lvds_pixel_clk_enable, .disable = lvds_pixel_clk_disable, .recalc_rate = lvds_pixel_clk_recalc_rate, - .round_rate = lvds_pixel_clk_round_rate, + .determine_rate = lvds_pixel_clk_determine_rate, }; static const struct clk_init_data clk_data = { diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c index 12430b9d4e93..b1beadb9bb59 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c @@ -59,13 +59,15 @@ static unsigned long sun4i_ddc_calc_divider(unsigned long rate, return best_rate; } -static long sun4i_ddc_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int sun4i_ddc_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct sun4i_ddc *ddc = hw_to_ddc(hw); - return sun4i_ddc_calc_divider(rate, *prate, ddc->pre_div, - ddc->m_offset, NULL, NULL); + req->rate = sun4i_ddc_calc_divider(req->rate, req->best_parent_rate, + ddc->pre_div, ddc->m_offset, NULL, NULL); + + return 0; } static unsigned long sun4i_ddc_recalc_rate(struct clk_hw *hw, @@ -101,7 +103,7 @@ static int sun4i_ddc_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops sun4i_ddc_ops = { .recalc_rate = sun4i_ddc_recalc_rate, - .round_rate = sun4i_ddc_round_rate, + .determine_rate = sun4i_ddc_determine_rate, .set_rate = sun4i_ddc_set_rate, }; diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c b/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c index 03d7de1911cd..4afb12bd5281 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c @@ -67,8 +67,8 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw, return parent_rate / val; } -static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int sun4i_dclk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct sun4i_dclk *dclk = hw_to_dclk(hw); struct sun4i_tcon *tcon = dclk->tcon; @@ -77,7 +77,7 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, int i; for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) { - u64 ideal = (u64)rate * i; + u64 ideal = (u64)req->rate * i; unsigned long rounded; /* @@ -99,17 +99,19 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, goto out; } - if (abs(rate - rounded / i) < - abs(rate - best_parent / best_div)) { + if (abs(req->rate - rounded / i) < + abs(req->rate - best_parent / best_div)) { best_parent = rounded; best_div = i; } } out: - *parent_rate = best_parent; + req->best_parent_rate = best_parent; - return best_parent / best_div; + req->rate = best_parent / best_div; + + return 0; } static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -155,7 +157,7 @@ static const struct clk_ops sun4i_dclk_ops = { .is_enabled = sun4i_dclk_is_enabled, .recalc_rate = sun4i_dclk_recalc_rate, - .round_rate = sun4i_dclk_round_rate, + .determine_rate = sun4i_dclk_determine_rate, .set_rate = sun4i_dclk_set_rate, .get_phase = sun4i_dclk_get_phase, diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h index 89633e30ca62..da670d7eeb2e 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -10,12 +10,19 @@ #include <drm/drm_crtc.h> #include <drm/drm_device.h> +#include <drm/drm_gem_atomic_helper.h> #include <drm/drm_modes.h> struct drm_format_info; struct drm_scanout_buffer; struct screen_info; +typedef void (*drm_sysfb_blit_func)(struct iosys_map *, const unsigned int *, + const struct iosys_map *, + const struct drm_framebuffer *, + const struct drm_rect *, + struct drm_format_conv_state *); + /* * Input parsing */ @@ -93,10 +100,25 @@ static inline struct drm_sysfb_device *to_drm_sysfb_device(struct drm_device *de * Plane */ +struct drm_sysfb_plane_state { + struct drm_shadow_plane_state base; + + /* transfers framebuffer data to scanout buffer in CRTC format */ + drm_sysfb_blit_func blit_to_crtc; +}; + +static inline struct drm_sysfb_plane_state * +to_drm_sysfb_plane_state(struct drm_plane_state *base) +{ + return container_of(to_drm_shadow_plane_state(base), struct drm_sysfb_plane_state, base); +} + size_t drm_sysfb_build_fourcc_list(struct drm_device *dev, const u32 *native_fourccs, size_t native_nfourccs, u32 *fourccs_out, size_t nfourccs_out); +int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane, + struct drm_plane_state *plane_state); int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, struct drm_atomic_state *new_state); void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, @@ -114,16 +136,24 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, DRM_FORMAT_MOD_INVALID #define DRM_SYSFB_PLANE_HELPER_FUNCS \ - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, \ + .begin_fb_access = drm_sysfb_plane_helper_begin_fb_access, \ + .end_fb_access = drm_gem_end_shadow_fb_access, \ .atomic_check = drm_sysfb_plane_helper_atomic_check, \ .atomic_update = drm_sysfb_plane_helper_atomic_update, \ .atomic_disable = drm_sysfb_plane_helper_atomic_disable, \ .get_scanout_buffer = drm_sysfb_plane_helper_get_scanout_buffer +void drm_sysfb_plane_reset(struct drm_plane *plane); +struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane); +void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *plane_state); + #define DRM_SYSFB_PLANE_FUNCS \ + .reset = drm_sysfb_plane_reset, \ .update_plane = drm_atomic_helper_update_plane, \ .disable_plane = drm_atomic_helper_disable_plane, \ - DRM_GEM_SHADOW_PLANE_FUNCS + .atomic_duplicate_state = drm_sysfb_plane_atomic_duplicate_state, \ + .atomic_destroy_state = drm_sysfb_plane_atomic_destroy_state /* * CRTC diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c index ddb4a7523ee6..8517c490e815 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c @@ -11,7 +11,6 @@ #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> -#include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_panic.h> #include <drm/drm_print.h> @@ -185,6 +184,104 @@ size_t drm_sysfb_build_fourcc_list(struct drm_device *dev, } EXPORT_SYMBOL(drm_sysfb_build_fourcc_list); +static void drm_sysfb_plane_state_destroy(struct drm_sysfb_plane_state *sysfb_plane_state) +{ + __drm_gem_destroy_shadow_plane_state(&sysfb_plane_state->base); + + kfree(sysfb_plane_state); +} + +static void drm_sysfb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch, + const struct iosys_map *src, const struct drm_framebuffer *fb, + const struct drm_rect *clip, struct drm_format_conv_state *state) +{ + drm_fb_memcpy(dst, dst_pitch, src, fb, clip); +} + +static drm_sysfb_blit_func drm_sysfb_get_blit_func(u32 dst_format, u32 src_format) +{ + if (src_format == dst_format) { + return drm_sysfb_memcpy; + } else if (src_format == DRM_FORMAT_XRGB8888) { + switch (dst_format) { + case DRM_FORMAT_RGB565: + return drm_fb_xrgb8888_to_rgb565; + case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: + return drm_fb_xrgb8888_to_rgb565be; + case DRM_FORMAT_XRGB1555: + return drm_fb_xrgb8888_to_xrgb1555; + case DRM_FORMAT_ARGB1555: + return drm_fb_xrgb8888_to_argb1555; + case DRM_FORMAT_RGBA5551: + return drm_fb_xrgb8888_to_rgba5551; + case DRM_FORMAT_RGB888: + return drm_fb_xrgb8888_to_rgb888; + case DRM_FORMAT_BGR888: + return drm_fb_xrgb8888_to_bgr888; + case DRM_FORMAT_ARGB8888: + return drm_fb_xrgb8888_to_argb8888; + case DRM_FORMAT_XBGR8888: + return drm_fb_xrgb8888_to_xbgr8888; + case DRM_FORMAT_ABGR8888: + return drm_fb_xrgb8888_to_abgr8888; + case DRM_FORMAT_XRGB2101010: + return drm_fb_xrgb8888_to_xrgb2101010; + case DRM_FORMAT_ARGB2101010: + return drm_fb_xrgb8888_to_argb2101010; + case DRM_FORMAT_BGRX8888: + return drm_fb_xrgb8888_to_bgrx8888; + case DRM_FORMAT_RGB332: + return drm_fb_xrgb8888_to_rgb332; + } + } + + return NULL; +} + +int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + struct drm_device *dev = plane->dev; + struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_crtc_state *crtc_state; + struct drm_sysfb_crtc_state *sysfb_crtc_state; + drm_sysfb_blit_func blit_to_crtc; + int ret; + + ret = drm_gem_begin_shadow_fb_access(plane, plane_state); + if (ret) + return ret; + + if (!fb) + return 0; + + ret = -EINVAL; + + crtc_state = drm_atomic_get_crtc_state(plane_state->state, plane_state->crtc); + if (drm_WARN_ON_ONCE(dev, !crtc_state)) + goto err_drm_gem_end_shadow_fb_access; + sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); + + if (drm_WARN_ON_ONCE(dev, !sysfb_crtc_state->format)) + goto err_drm_gem_end_shadow_fb_access; + blit_to_crtc = drm_sysfb_get_blit_func(sysfb_crtc_state->format->format, + fb->format->format); + if (!blit_to_crtc) { + drm_warn_once(dev, "No blit helper from %p4cc to %p4cc found.\n", + &fb->format->format, &sysfb_crtc_state->format->format); + goto err_drm_gem_end_shadow_fb_access; + } + sysfb_plane_state->blit_to_crtc = blit_to_crtc; + + return 0; + +err_drm_gem_end_shadow_fb_access: + drm_gem_end_shadow_fb_access(plane, plane_state); + return ret; +} +EXPORT_SYMBOL(drm_sysfb_plane_helper_begin_fb_access); + int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane, struct drm_atomic_state *new_state) { @@ -235,12 +332,14 @@ void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_at struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev); struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); - struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); + struct drm_shadow_plane_state *shadow_plane_state = &sysfb_plane_state->base; struct drm_framebuffer *fb = plane_state->fb; unsigned int dst_pitch = sysfb->fb_pitch; struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); const struct drm_format_info *dst_format = sysfb_crtc_state->format; + drm_sysfb_blit_func blit_to_crtc = sysfb_plane_state->blit_to_crtc; struct drm_atomic_helper_damage_iter iter; struct drm_rect damage; int ret, idx; @@ -261,8 +360,8 @@ void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_at continue; iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip)); - drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb, - &damage, &shadow_plane_state->fmtcnv_state); + blit_to_crtc(&dst, &dst_pitch, shadow_plane_state->data, fb, &damage, + &shadow_plane_state->fmtcnv_state); } drm_dev_exit(idx); @@ -321,6 +420,52 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane, } EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer); +void drm_sysfb_plane_reset(struct drm_plane *plane) +{ + struct drm_sysfb_plane_state *sysfb_plane_state; + + if (plane->state) + drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane->state)); + + sysfb_plane_state = kzalloc(sizeof(*sysfb_plane_state), GFP_KERNEL); + if (sysfb_plane_state) + __drm_gem_reset_shadow_plane(plane, &sysfb_plane_state->base); + else + __drm_gem_reset_shadow_plane(plane, NULL); +} +EXPORT_SYMBOL(drm_sysfb_plane_reset); + +struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_plane_state *plane_state = plane->state; + struct drm_sysfb_plane_state *sysfb_plane_state; + struct drm_sysfb_plane_state *new_sysfb_plane_state; + struct drm_shadow_plane_state *new_shadow_plane_state; + + if (drm_WARN_ON(dev, !plane_state)) + return NULL; + sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); + + new_sysfb_plane_state = kzalloc(sizeof(*new_sysfb_plane_state), GFP_KERNEL); + if (!new_sysfb_plane_state) + return NULL; + new_shadow_plane_state = &new_sysfb_plane_state->base; + + __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); + new_sysfb_plane_state->blit_to_crtc = sysfb_plane_state->blit_to_crtc; + + return &new_shadow_plane_state->base; +} +EXPORT_SYMBOL(drm_sysfb_plane_atomic_duplicate_state); + +void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane_state)); +} +EXPORT_SYMBOL(drm_sysfb_plane_atomic_destroy_state); + /* * CRTC */ diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c index 0358164a623c..9b16d5164ef4 100644 --- a/drivers/gpu/drm/sysfb/simpledrm.c +++ b/drivers/gpu/drm/sysfb/simpledrm.c @@ -2,8 +2,9 @@ #include <linux/aperture.h> #include <linux/clk.h> -#include <linux/of_clk.h> #include <linux/minmax.h> +#include <linux/of_address.h> +#include <linux/of_clk.h> #include <linux/of_reserved_mem.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c index 16a4b52d45c6..c318df0adad5 100644 --- a/drivers/gpu/drm/sysfb/vesadrm.c +++ b/drivers/gpu/drm/sysfb/vesadrm.c @@ -295,7 +295,8 @@ static int vesadrm_primary_plane_helper_atomic_check(struct drm_plane *plane, } static const struct drm_plane_helper_funcs vesadrm_primary_plane_helper_funcs = { - DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, + .begin_fb_access = drm_sysfb_plane_helper_begin_fb_access, + .end_fb_access = drm_gem_end_shadow_fb_access, .atomic_check = vesadrm_primary_plane_helper_atomic_check, .atomic_update = drm_sysfb_plane_helper_atomic_update, .atomic_disable = drm_sysfb_plane_helper_atomic_disable, diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 8ede07fb7a21..6b14f1e919eb 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -16,6 +16,7 @@ #include <linux/vmalloc.h> #include <drm/drm_drv.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_prime.h> #include "drm.h" @@ -542,12 +543,13 @@ void tegra_bo_free_object(struct drm_gem_object *gem) int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, struct drm_mode_create_dumb *args) { - unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); struct tegra_drm *tegra = drm->dev_private; struct tegra_bo *bo; + int ret; - args->pitch = round_up(min_pitch, tegra->pitch_align); - args->size = args->pitch * args->height; + ret = drm_mode_size_dumb(drm, args, tegra->pitch_align, 0); + if (ret) + return ret; bo = tegra_bo_create_with_handle(file, drm, args->size, 0, &args->handle); diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 8cd2969e7d4b..c4820f5e7658 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -658,7 +658,7 @@ static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data, { const u8 *ptr = data; unsigned long offset; - size_t i, j; + size_t i; u32 value; switch (ptr[0]) { @@ -691,7 +691,7 @@ static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data, * - subpack_low: bytes 0 - 3 * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00) */ - for (i = 3, j = 0; i < size; i += 7, j += 8) { + for (i = 3; i < size; i += 7) { size_t rem = size - i, num = min_t(size_t, rem, 4); value = tegra_hdmi_subpack(&ptr[i], num); diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 21f3dfdcc5c9..bc7dd562cf6b 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1864,7 +1864,7 @@ static void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor, { const u8 *ptr = data; unsigned long offset; - size_t i, j; + size_t i; u32 value; switch (ptr[0]) { @@ -1897,7 +1897,7 @@ static void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor, * - subpack_low: bytes 0 - 3 * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00) */ - for (i = 3, j = 0; i < size; i += 7, j += 8) { + for (i = 3; i < size; i += 7) { size_t rem = size - i, num = min_t(size_t, rem, 4); value = tegra_sor_hdmi_subpack(&ptr[i], num); diff --git a/drivers/gpu/drm/tests/.kunitconfig b/drivers/gpu/drm/tests/.kunitconfig index 6ec04b4c979d..5be8e71f45d5 100644 --- a/drivers/gpu/drm/tests/.kunitconfig +++ b/drivers/gpu/drm/tests/.kunitconfig @@ -1,3 +1,5 @@ CONFIG_KUNIT=y CONFIG_DRM=y +CONFIG_DRM_VKMS=y +CONFIG_DRM_FBDEV_EMULATION=y CONFIG_DRM_KUNIT_TEST=y diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c index da89fd01c337..8fcc6a2f9477 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.c +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -94,8 +94,6 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc, struct drm_display_mode *mode; enum drm_mode_status ok; - dev_dbg(ddev->dev, "%s\n", __func__); - if (!crtc_state->enable) return 0; @@ -103,7 +101,7 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc, ok = dispc_vp_mode_valid(dispc, hw_videoport, mode); if (ok != MODE_OK) { - dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n", + drm_dbg(ddev, "%s: bad mode: %ux%u pclk %u kHz\n", __func__, mode->hdisplay, mode->vdisplay, mode->clock); return -EINVAL; } @@ -172,7 +170,7 @@ static void tidss_crtc_atomic_flush(struct drm_crtc *crtc, struct tidss_device *tidss = to_tidss(ddev); unsigned long flags; - dev_dbg(ddev->dev, "%s: %s is %sactive, %s modeset, event %p\n", + drm_dbg(ddev, "%s: %s is %sactive, %s modeset, event %p\n", __func__, crtc->name, crtc->state->active ? "" : "not ", drm_atomic_crtc_needs_modeset(crtc->state) ? "needs" : "doesn't need", crtc->state->event); @@ -328,8 +326,6 @@ static int tidss_crtc_enable_vblank(struct drm_crtc *crtc) struct drm_device *ddev = crtc->dev; struct tidss_device *tidss = to_tidss(ddev); - dev_dbg(ddev->dev, "%s\n", __func__); - tidss_runtime_get(tidss); tidss_irq_enable_vblank(crtc); @@ -342,29 +338,34 @@ static void tidss_crtc_disable_vblank(struct drm_crtc *crtc) struct drm_device *ddev = crtc->dev; struct tidss_device *tidss = to_tidss(ddev); - dev_dbg(ddev->dev, "%s\n", __func__); - tidss_irq_disable_vblank(crtc); tidss_runtime_put(tidss); } +static void tidss_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); + + __drm_atomic_helper_crtc_destroy_state(&tstate->base); + kfree(tstate); +} + static void tidss_crtc_reset(struct drm_crtc *crtc) { - struct tidss_crtc_state *tcrtc; + struct tidss_crtc_state *tstate; if (crtc->state) - __drm_atomic_helper_crtc_destroy_state(crtc->state); + tidss_crtc_destroy_state(crtc, crtc->state); - kfree(crtc->state); - - tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL); - if (!tcrtc) { + tstate = kzalloc(sizeof(*tstate), GFP_KERNEL); + if (!tstate) { crtc->state = NULL; return; } - __drm_atomic_helper_crtc_reset(crtc, &tcrtc->base); + __drm_atomic_helper_crtc_reset(crtc, &tstate->base); } static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc) @@ -404,7 +405,7 @@ static const struct drm_crtc_funcs tidss_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .atomic_duplicate_state = tidss_crtc_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_destroy_state = tidss_crtc_destroy_state, .enable_vblank = tidss_crtc_enable_vblank, .disable_vblank = tidss_crtc_disable_vblank, }; diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index 7c8c15a5c39b..d0b191c470ca 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -1051,20 +1051,22 @@ struct dispc_bus_format *dispc_vp_find_bus_fmt(struct dispc_device *dispc, int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport, const struct drm_crtc_state *state) { + struct tidss_device *tidss = dispc->tidss; + struct drm_device *dev = &tidss->ddev; const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); const struct dispc_bus_format *fmt; fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format, tstate->bus_flags); if (!fmt) { - dev_dbg(dispc->dev, "%s: Unsupported bus format: %u\n", + drm_dbg(dev, "%s: Unsupported bus format: %u\n", __func__, tstate->bus_format); return -EINVAL; } if (dispc->feat->vp_bus_type[hw_videoport] != DISPC_VP_OLDI_AM65X && fmt->is_oldi_fmt) { - dev_dbg(dispc->dev, "%s: %s is not OLDI-port\n", + drm_dbg(dev, "%s: %s is not OLDI-port\n", __func__, dispc->feat->vp_name[hw_videoport]); return -EINVAL; } @@ -2849,8 +2851,6 @@ int dispc_runtime_resume(struct dispc_device *dispc) void dispc_remove(struct tidss_device *tidss) { - dev_dbg(tidss->dev, "%s\n", __func__); - tidss->dispc = NULL; } @@ -2992,8 +2992,6 @@ int dispc_init(struct tidss_device *tidss) unsigned int i, num_fourccs; int r = 0; - dev_dbg(dev, "%s\n", __func__); - feat = tidss->feat; if (feat->subrev != DISPC_K2G) { diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index 27d9a8fd541f..1c8cc18bc53c 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -33,8 +33,6 @@ int tidss_runtime_get(struct tidss_device *tidss) { int r; - dev_dbg(tidss->dev, "%s\n", __func__); - r = pm_runtime_resume_and_get(tidss->dev); WARN_ON(r < 0); return r; @@ -44,8 +42,6 @@ void tidss_runtime_put(struct tidss_device *tidss) { int r; - dev_dbg(tidss->dev, "%s\n", __func__); - pm_runtime_mark_last_busy(tidss->dev); r = pm_runtime_put_autosuspend(tidss->dev); @@ -56,8 +52,6 @@ static int __maybe_unused tidss_pm_runtime_suspend(struct device *dev) { struct tidss_device *tidss = dev_get_drvdata(dev); - dev_dbg(dev, "%s\n", __func__); - return dispc_runtime_suspend(tidss->dispc); } @@ -66,8 +60,6 @@ static int __maybe_unused tidss_pm_runtime_resume(struct device *dev) struct tidss_device *tidss = dev_get_drvdata(dev); int r; - dev_dbg(dev, "%s\n", __func__); - r = dispc_runtime_resume(tidss->dispc); if (r) return r; @@ -79,8 +71,6 @@ static int __maybe_unused tidss_suspend(struct device *dev) { struct tidss_device *tidss = dev_get_drvdata(dev); - dev_dbg(dev, "%s\n", __func__); - return drm_mode_config_helper_suspend(&tidss->ddev); } @@ -88,8 +78,6 @@ static int __maybe_unused tidss_resume(struct device *dev) { struct tidss_device *tidss = dev_get_drvdata(dev); - dev_dbg(dev, "%s\n", __func__); - return drm_mode_config_helper_resume(&tidss->ddev); } @@ -127,8 +115,6 @@ static int tidss_probe(struct platform_device *pdev) int ret; int irq; - dev_dbg(dev, "%s\n", __func__); - tidss = devm_drm_dev_alloc(&pdev->dev, &tidss_driver, struct tidss_device, ddev); if (IS_ERR(tidss)) @@ -228,8 +214,6 @@ static void tidss_remove(struct platform_device *pdev) struct tidss_device *tidss = platform_get_drvdata(pdev); struct drm_device *ddev = &tidss->ddev; - dev_dbg(dev, "%s\n", __func__); - drm_dev_unregister(ddev); drm_atomic_helper_shutdown(ddev); diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index c34eb90cddbe..86eb5d97410b 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -24,8 +24,6 @@ static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state) struct drm_device *ddev = old_state->dev; struct tidss_device *tidss = to_tidss(ddev); - dev_dbg(ddev->dev, "%s\n", __func__); - tidss_runtime_get(tidss); drm_atomic_helper_commit_modeset_disables(ddev, old_state); @@ -245,8 +243,6 @@ int tidss_modeset_init(struct tidss_device *tidss) struct drm_device *ddev = &tidss->ddev; int ret; - dev_dbg(tidss->dev, "%s\n", __func__); - ret = drmm_mode_config_init(ddev); if (ret) return ret; diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c index 142ae81951a0..bd10bc1b9961 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -42,8 +42,6 @@ static int tidss_plane_atomic_check(struct drm_plane *plane, u32 hw_videoport; int ret; - dev_dbg(ddev->dev, "%s\n", __func__); - if (!new_plane_state->crtc) { /* * The visible field is not reset by the DRM core but only @@ -124,8 +122,6 @@ static void tidss_plane_atomic_update(struct drm_plane *plane, plane); u32 hw_videoport; - dev_dbg(ddev->dev, "%s\n", __func__); - if (!new_state->visible) { dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); return; @@ -143,8 +139,6 @@ static void tidss_plane_atomic_enable(struct drm_plane *plane, struct tidss_device *tidss = to_tidss(ddev); struct tidss_plane *tplane = to_tidss_plane(plane); - dev_dbg(ddev->dev, "%s\n", __func__); - dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, true); } @@ -155,8 +149,6 @@ static void tidss_plane_atomic_disable(struct drm_plane *plane, struct tidss_device *tidss = to_tidss(ddev); struct tidss_plane *tplane = to_tidss_plane(plane); - dev_dbg(ddev->dev, "%s\n", __func__); - dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false); } diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_test.c index 6c77550c51af..5426b435f702 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_bo_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_test.c @@ -379,7 +379,7 @@ static void ttm_bo_unreserve_bulk(struct kunit *test) dma_resv_fini(resv); } -static void ttm_bo_put_basic(struct kunit *test) +static void ttm_bo_fini_basic(struct kunit *test) { struct ttm_test_devices *priv = test->priv; struct ttm_buffer_object *bo; @@ -410,7 +410,7 @@ static void ttm_bo_put_basic(struct kunit *test) dma_resv_unlock(bo->base.resv); KUNIT_EXPECT_EQ(test, err, 0); - ttm_bo_put(bo); + ttm_bo_fini(bo); } static const char *mock_name(struct dma_fence *f) @@ -423,7 +423,7 @@ static const struct dma_fence_ops mock_fence_ops = { .get_timeline_name = mock_name, }; -static void ttm_bo_put_shared_resv(struct kunit *test) +static void ttm_bo_fini_shared_resv(struct kunit *test) { struct ttm_test_devices *priv = test->priv; struct ttm_buffer_object *bo; @@ -463,7 +463,7 @@ static void ttm_bo_put_shared_resv(struct kunit *test) bo->type = ttm_bo_type_device; bo->base.resv = external_resv; - ttm_bo_put(bo); + ttm_bo_fini(bo); } static void ttm_bo_pin_basic(struct kunit *test) @@ -616,8 +616,8 @@ static struct kunit_case ttm_bo_test_cases[] = { KUNIT_CASE(ttm_bo_unreserve_basic), KUNIT_CASE(ttm_bo_unreserve_pinned), KUNIT_CASE(ttm_bo_unreserve_bulk), - KUNIT_CASE(ttm_bo_put_basic), - KUNIT_CASE(ttm_bo_put_shared_resv), + KUNIT_CASE(ttm_bo_fini_basic), + KUNIT_CASE(ttm_bo_fini_shared_resv), KUNIT_CASE(ttm_bo_pin_basic), KUNIT_CASE(ttm_bo_pin_unpin_resource), KUNIT_CASE(ttm_bo_multiple_pin_one_unpin), diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c index 1bcc67977f48..3a1eef83190c 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c @@ -144,7 +144,7 @@ static void ttm_bo_init_reserved_sys_man(struct kunit *test) drm_mm_node_allocated(&bo->base.vma_node.vm_node)); ttm_resource_free(bo, &bo->resource); - ttm_bo_put(bo); + ttm_bo_fini(bo); } static void ttm_bo_init_reserved_mock_man(struct kunit *test) @@ -186,7 +186,7 @@ static void ttm_bo_init_reserved_mock_man(struct kunit *test) drm_mm_node_allocated(&bo->base.vma_node.vm_node)); ttm_resource_free(bo, &bo->resource); - ttm_bo_put(bo); + ttm_bo_fini(bo); ttm_mock_manager_fini(priv->ttm_dev, mem_type); } @@ -221,7 +221,7 @@ static void ttm_bo_init_reserved_resv(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, bo->base.resv, &resv); ttm_resource_free(bo, &bo->resource); - ttm_bo_put(bo); + ttm_bo_fini(bo); } static void ttm_bo_validate_basic(struct kunit *test) @@ -265,7 +265,7 @@ static void ttm_bo_validate_basic(struct kunit *test) KUNIT_EXPECT_EQ(test, bo->resource->placement, DRM_BUDDY_TOPDOWN_ALLOCATION); - ttm_bo_put(bo); + ttm_bo_fini(bo); ttm_mock_manager_fini(priv->ttm_dev, snd_mem); } @@ -292,7 +292,7 @@ static void ttm_bo_validate_invalid_placement(struct kunit *test) KUNIT_EXPECT_EQ(test, err, -ENOMEM); - ttm_bo_put(bo); + ttm_bo_fini(bo); } static void ttm_bo_validate_failed_alloc(struct kunit *test) @@ -321,7 +321,7 @@ static void ttm_bo_validate_failed_alloc(struct kunit *test) KUNIT_EXPECT_EQ(test, err, -ENOMEM); - ttm_bo_put(bo); + ttm_bo_fini(bo); ttm_bad_manager_fini(priv->ttm_dev, mem_type); } @@ -353,7 +353,7 @@ static void ttm_bo_validate_pinned(struct kunit *test) ttm_bo_unpin(bo); dma_resv_unlock(bo->base.resv); - ttm_bo_put(bo); + ttm_bo_fini(bo); } static const struct ttm_bo_validate_test_case ttm_mem_type_cases[] = { @@ -403,7 +403,7 @@ static void ttm_bo_validate_same_placement(struct kunit *test) KUNIT_EXPECT_EQ(test, err, 0); KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, 0); - ttm_bo_put(bo); + ttm_bo_fini(bo); if (params->mem_type != TTM_PL_SYSTEM) ttm_mock_manager_fini(priv->ttm_dev, params->mem_type); @@ -452,7 +452,7 @@ static void ttm_bo_validate_busy_placement(struct kunit *test) KUNIT_EXPECT_EQ(test, bo->resource->mem_type, snd_mem); KUNIT_ASSERT_TRUE(test, list_is_singular(&man->lru[bo->priority])); - ttm_bo_put(bo); + ttm_bo_fini(bo); ttm_bad_manager_fini(priv->ttm_dev, fst_mem); ttm_mock_manager_fini(priv->ttm_dev, snd_mem); } @@ -495,7 +495,7 @@ static void ttm_bo_validate_multihop(struct kunit *test) KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, size * 2); KUNIT_EXPECT_EQ(test, bo->resource->mem_type, final_mem); - ttm_bo_put(bo); + ttm_bo_fini(bo); ttm_mock_manager_fini(priv->ttm_dev, fst_mem); ttm_mock_manager_fini(priv->ttm_dev, tmp_mem); @@ -567,7 +567,7 @@ static void ttm_bo_validate_no_placement_signaled(struct kunit *test) KUNIT_ASSERT_TRUE(test, flags & TTM_TT_FLAG_ZERO_ALLOC); } - ttm_bo_put(bo); + ttm_bo_fini(bo); } static int threaded_dma_resv_signal(void *arg) @@ -635,7 +635,7 @@ static void ttm_bo_validate_no_placement_not_signaled(struct kunit *test) /* Make sure we have an idle object at this point */ dma_resv_wait_timeout(bo->base.resv, usage, false, MAX_SCHEDULE_TIMEOUT); - ttm_bo_put(bo); + ttm_bo_fini(bo); } static void ttm_bo_validate_move_fence_signaled(struct kunit *test) @@ -668,7 +668,7 @@ static void ttm_bo_validate_move_fence_signaled(struct kunit *test) KUNIT_EXPECT_EQ(test, bo->resource->mem_type, mem_type); KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size); - ttm_bo_put(bo); + ttm_bo_fini(bo); dma_fence_put(man->move); } @@ -753,7 +753,7 @@ static void ttm_bo_validate_move_fence_not_signaled(struct kunit *test) else KUNIT_EXPECT_EQ(test, bo->resource->mem_type, fst_mem); - ttm_bo_put(bo); + ttm_bo_fini(bo); ttm_mock_manager_fini(priv->ttm_dev, fst_mem); ttm_mock_manager_fini(priv->ttm_dev, snd_mem); } @@ -807,8 +807,8 @@ static void ttm_bo_validate_happy_evict(struct kunit *test) KUNIT_EXPECT_EQ(test, bos[1].resource->mem_type, mem_type); for (i = 0; i < bo_no; i++) - ttm_bo_put(&bos[i]); - ttm_bo_put(bo_val); + ttm_bo_fini(&bos[i]); + ttm_bo_fini(bo_val); ttm_mock_manager_fini(priv->ttm_dev, mem_type); ttm_mock_manager_fini(priv->ttm_dev, mem_multihop); @@ -852,12 +852,12 @@ static void ttm_bo_validate_all_pinned_evict(struct kunit *test) KUNIT_EXPECT_EQ(test, err, -ENOMEM); - ttm_bo_put(bo_small); + ttm_bo_fini(bo_small); ttm_bo_reserve(bo_big, false, false, NULL); ttm_bo_unpin(bo_big); dma_resv_unlock(bo_big->base.resv); - ttm_bo_put(bo_big); + ttm_bo_fini(bo_big); ttm_mock_manager_fini(priv->ttm_dev, mem_type); ttm_mock_manager_fini(priv->ttm_dev, mem_multihop); @@ -916,13 +916,13 @@ static void ttm_bo_validate_allowed_only_evict(struct kunit *test) KUNIT_EXPECT_EQ(test, bo_evictable->resource->mem_type, mem_type_evict); KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, size * 2 + BO_SIZE); - ttm_bo_put(bo); - ttm_bo_put(bo_evictable); + ttm_bo_fini(bo); + ttm_bo_fini(bo_evictable); ttm_bo_reserve(bo_pinned, false, false, NULL); ttm_bo_unpin(bo_pinned); dma_resv_unlock(bo_pinned->base.resv); - ttm_bo_put(bo_pinned); + ttm_bo_fini(bo_pinned); ttm_mock_manager_fini(priv->ttm_dev, mem_type); ttm_mock_manager_fini(priv->ttm_dev, mem_multihop); @@ -973,8 +973,8 @@ static void ttm_bo_validate_deleted_evict(struct kunit *test) KUNIT_EXPECT_NULL(test, bo_big->ttm); KUNIT_EXPECT_NULL(test, bo_big->resource); - ttm_bo_put(bo_small); - ttm_bo_put(bo_big); + ttm_bo_fini(bo_small); + ttm_bo_fini(bo_big); ttm_mock_manager_fini(priv->ttm_dev, mem_type); } @@ -1025,8 +1025,8 @@ static void ttm_bo_validate_busy_domain_evict(struct kunit *test) KUNIT_EXPECT_EQ(test, bo_init->resource->mem_type, mem_type); KUNIT_EXPECT_NULL(test, bo_val->resource); - ttm_bo_put(bo_init); - ttm_bo_put(bo_val); + ttm_bo_fini(bo_init); + ttm_bo_fini(bo_val); ttm_mock_manager_fini(priv->ttm_dev, mem_type); ttm_bad_manager_fini(priv->ttm_dev, mem_type_evict); @@ -1070,8 +1070,8 @@ static void ttm_bo_validate_evict_gutting(struct kunit *test) KUNIT_ASSERT_NULL(test, bo_evict->resource); KUNIT_ASSERT_TRUE(test, bo_evict->ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC); - ttm_bo_put(bo_evict); - ttm_bo_put(bo); + ttm_bo_fini(bo_evict); + ttm_bo_fini(bo); ttm_mock_manager_fini(priv->ttm_dev, mem_type); } @@ -1128,9 +1128,9 @@ static void ttm_bo_validate_recrusive_evict(struct kunit *test) ttm_mock_manager_fini(priv->ttm_dev, mem_type); ttm_mock_manager_fini(priv->ttm_dev, mem_type_evict); - ttm_bo_put(bo_val); - ttm_bo_put(bo_tt); - ttm_bo_put(bo_mock); + ttm_bo_fini(bo_val); + ttm_bo_fini(bo_tt); + ttm_bo_fini(bo_mock); } static struct kunit_case ttm_bo_validate_test_cases[] = { diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 29423ceeec5c..fba2a68a556e 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -318,18 +318,17 @@ static void ttm_bo_release(struct kref *kref) bo->destroy(bo); } -/** - * ttm_bo_put - * - * @bo: The buffer object. - * - * Unreference a buffer object. - */ +/* TODO: remove! */ void ttm_bo_put(struct ttm_buffer_object *bo) { kref_put(&bo->kref, ttm_bo_release); } -EXPORT_SYMBOL(ttm_bo_put); + +void ttm_bo_fini(struct ttm_buffer_object *bo) +{ + ttm_bo_put(bo); +} +EXPORT_SYMBOL(ttm_bo_fini); static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h b/drivers/gpu/drm/ttm/ttm_bo_internal.h index 9d8b747a34db..e0d48eac74b0 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_internal.h +++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h @@ -55,4 +55,6 @@ ttm_bo_get_unless_zero(struct ttm_buffer_object *bo) return bo; } +void ttm_bo_put(struct ttm_buffer_object *bo); + #endif diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index 123ab0ce1781..bb8c40be3250 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -35,6 +35,7 @@ config DRM_VC4_HDMI_CEC bool "Broadcom VC4 HDMI CEC Support" depends on DRM_VC4 select CEC_CORE + select DRM_DISPLAY_HDMI_CEC_HELPER help Choose this option if you have a Broadcom VC4 GPU and want to use CEC. diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 07c91b450f93..049c92dd5d27 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -32,6 +32,7 @@ */ #include <drm/display/drm_hdmi_audio_helper.h> +#include <drm/display/drm_hdmi_cec_helper.h> #include <drm/display/drm_hdmi_helper.h> #include <drm/display/drm_hdmi_state_helper.h> #include <drm/display/drm_scdc_helper.h> @@ -375,14 +376,6 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi, drm_atomic_helper_connector_hdmi_hotplug(connector, status); - if (status == connector_status_disconnected) { - cec_phys_addr_invalidate(vc4_hdmi->cec_adap); - return; - } - - cec_s_phys_addr(vc4_hdmi->cec_adap, - connector->display_info.source_physical_address, false); - if (status != connector_status_connected) return; @@ -2384,8 +2377,8 @@ static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv) struct vc4_hdmi *vc4_hdmi = priv; if (vc4_hdmi->cec_rx_msg.len) - cec_received_msg(vc4_hdmi->cec_adap, - &vc4_hdmi->cec_rx_msg); + drm_connector_hdmi_cec_received_msg(&vc4_hdmi->connector, + &vc4_hdmi->cec_rx_msg); return IRQ_HANDLED; } @@ -2395,15 +2388,17 @@ static irqreturn_t vc4_cec_irq_handler_tx_thread(int irq, void *priv) struct vc4_hdmi *vc4_hdmi = priv; if (vc4_hdmi->cec_tx_ok) { - cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK, - 0, 0, 0, 0); + drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector, + CEC_TX_STATUS_OK, + 0, 0, 0, 0); } else { /* * This CEC implementation makes 1 retry, so if we * get a NACK, then that means it made 2 attempts. */ - cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK, - 0, 2, 0, 0); + drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector, + CEC_TX_STATUS_NACK, + 0, 2, 0, 0); } return IRQ_HANDLED; } @@ -2560,9 +2555,9 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) return ret; } -static int vc4_hdmi_cec_enable(struct cec_adapter *adap) +static int vc4_hdmi_cec_enable(struct drm_connector *connector) { - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); struct drm_device *drm = vc4_hdmi->connector.dev; /* clock period in microseconds */ const u32 usecs = 1000000 / CEC_CLOCK_FREQ; @@ -2627,9 +2622,9 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap) return 0; } -static int vc4_hdmi_cec_disable(struct cec_adapter *adap) +static int vc4_hdmi_cec_disable(struct drm_connector *connector) { - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; int idx; @@ -2663,17 +2658,17 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap) return 0; } -static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) +static int vc4_hdmi_cec_adap_enable(struct drm_connector *connector, bool enable) { if (enable) - return vc4_hdmi_cec_enable(adap); + return vc4_hdmi_cec_enable(connector); else - return vc4_hdmi_cec_disable(adap); + return vc4_hdmi_cec_disable(connector); } -static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) +static int vc4_hdmi_cec_adap_log_addr(struct drm_connector *connector, u8 log_addr) { - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; int idx; @@ -2699,10 +2694,10 @@ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) return 0; } -static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, +static int vc4_hdmi_cec_adap_transmit(struct drm_connector *connector, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { - struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); struct drm_device *dev = vc4_hdmi->connector.dev; unsigned long flags; u32 val; @@ -2745,84 +2740,65 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, return 0; } -static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = { - .adap_enable = vc4_hdmi_cec_adap_enable, - .adap_log_addr = vc4_hdmi_cec_adap_log_addr, - .adap_transmit = vc4_hdmi_cec_adap_transmit, -}; - -static void vc4_hdmi_cec_release(void *ptr) -{ - struct vc4_hdmi *vc4_hdmi = ptr; - - cec_unregister_adapter(vc4_hdmi->cec_adap); - vc4_hdmi->cec_adap = NULL; -} - -static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) +static int vc4_hdmi_cec_init(struct drm_connector *connector) { - struct cec_connector_info conn_info; + struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); struct platform_device *pdev = vc4_hdmi->pdev; struct device *dev = &pdev->dev; int ret; - if (!of_property_present(dev->of_node, "interrupts")) { - dev_warn(dev, "'interrupts' DT property is missing, no CEC\n"); - return 0; - } - - vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops, - vc4_hdmi, - vc4_hdmi->variant->card_name, - CEC_CAP_DEFAULTS | - CEC_CAP_CONNECTOR_INFO, 1); - ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap); - if (ret < 0) - return ret; - - cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector); - cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); - if (vc4_hdmi->variant->external_irq_controller) { ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-rx"), vc4_cec_irq_handler_rx_bare, vc4_cec_irq_handler_rx_thread, 0, "vc4 hdmi cec rx", vc4_hdmi); if (ret) - goto err_delete_cec_adap; + return ret; ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-tx"), vc4_cec_irq_handler_tx_bare, vc4_cec_irq_handler_tx_thread, 0, "vc4 hdmi cec tx", vc4_hdmi); if (ret) - goto err_delete_cec_adap; + return ret; } else { ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), vc4_cec_irq_handler, vc4_cec_irq_handler_thread, 0, "vc4 hdmi cec", vc4_hdmi); if (ret) - goto err_delete_cec_adap; + return ret; } - ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev); - if (ret < 0) - goto err_delete_cec_adap; + return 0; +} + +static const struct drm_connector_hdmi_cec_funcs vc4_hdmi_cec_funcs = { + .init = vc4_hdmi_cec_init, + .enable = vc4_hdmi_cec_adap_enable, + .log_addr = vc4_hdmi_cec_adap_log_addr, + .transmit = vc4_hdmi_cec_adap_transmit, +}; + +static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi) +{ + struct platform_device *pdev = vc4_hdmi->pdev; + struct device *dev = &pdev->dev; + + if (!of_property_present(dev->of_node, "interrupts")) { + dev_warn(dev, "'interrupts' DT property is missing, no CEC\n"); + return 0; + } /* - * NOTE: Strictly speaking, we should probably use a DRM-managed - * registration there to avoid removing the CEC adapter by the - * time the DRM driver doesn't have any user anymore. + * NOTE: the CEC adapter will be unregistered by drmm cleanup from + * drm_managed_release(), which is called from drm_dev_release() + * during device unbind. * * However, the CEC framework already cleans up the CEC adapter * only when the last user has closed its file descriptor, so we * don't need to handle it in DRM. * - * By the time the device-managed hook is executed, we will give - * up our reference to the CEC adapter and therefore don't - * really care when it's actually freed. - * * There's still a problematic sequence: if we unregister our * CEC adapter, but the userspace keeps a handle on the CEC * adapter but not the DRM device for some reason. In such a @@ -2833,19 +2809,14 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) * the CEC framework already handles this too, by calling * cec_is_registered() in cec_ioctl() and cec_poll(). */ - ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi); - if (ret) - return ret; - - return 0; - -err_delete_cec_adap: - cec_delete_adapter(vc4_hdmi->cec_adap); - - return ret; + return drmm_connector_hdmi_cec_register(&vc4_hdmi->connector, + &vc4_hdmi_cec_funcs, + vc4_hdmi->variant->card_name, + 1, + &pdev->dev); } #else -static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) +static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi) { return 0; } @@ -3250,7 +3221,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (ret) goto err_put_runtime_pm; - ret = vc4_hdmi_cec_init(vc4_hdmi); + ret = vc4_hdmi_cec_register(vc4_hdmi); if (ret) goto err_put_runtime_pm; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index a31157c99bee..8d069718df00 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -147,7 +147,6 @@ struct vc4_hdmi { */ bool disable_wifi_frequencies; - struct cec_adapter *cec_adap; struct cec_msg cec_rx_msg; bool cec_tx_ok; bool cec_irq_was_rx; diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index e60573e0f3e9..bd79f24686dc 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -7,25 +7,18 @@ #include <drm/drm_managed.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> +#include <drm/drm_vblank_helper.h> #include "vkms_drv.h" -static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) +static bool vkms_crtc_handle_vblank_timeout(struct drm_crtc *crtc) { - struct vkms_output *output = container_of(timer, struct vkms_output, - vblank_hrtimer); - struct drm_crtc *crtc = &output->crtc; + struct vkms_output *output = drm_crtc_to_vkms_output(crtc); struct vkms_crtc_state *state; - u64 ret_overrun; bool ret, fence_cookie; fence_cookie = dma_fence_begin_signalling(); - ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer, - output->period_ns); - if (ret_overrun != 1) - pr_warn("%s: vblank timer overrun\n", __func__); - spin_lock(&output->lock); ret = drm_crtc_handle_vblank(crtc); if (!ret) @@ -57,55 +50,6 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) dma_fence_end_signalling(fence_cookie); - return HRTIMER_RESTART; -} - -static int vkms_enable_vblank(struct drm_crtc *crtc) -{ - struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); - struct vkms_output *out = drm_crtc_to_vkms_output(crtc); - - hrtimer_setup(&out->vblank_hrtimer, &vkms_vblank_simulate, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - out->period_ns = ktime_set(0, vblank->framedur_ns); - hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL); - - return 0; -} - -static void vkms_disable_vblank(struct drm_crtc *crtc) -{ - struct vkms_output *out = drm_crtc_to_vkms_output(crtc); - - hrtimer_cancel(&out->vblank_hrtimer); -} - -static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc, - int *max_error, ktime_t *vblank_time, - bool in_vblank_irq) -{ - struct vkms_output *output = drm_crtc_to_vkms_output(crtc); - struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); - - if (!READ_ONCE(vblank->enabled)) { - *vblank_time = ktime_get(); - return true; - } - - *vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires); - - if (WARN_ON(*vblank_time == vblank->time)) - return true; - - /* - * To prevent races we roll the hrtimer forward before we do any - * interrupt processing - this is how real hw works (the interrupt is - * only generated after all the vblank registers are updated) and what - * the vblank core expects. Therefore we need to always correct the - * timestampe by one frame. - */ - *vblank_time -= output->period_ns; - return true; } @@ -159,9 +103,7 @@ static const struct drm_crtc_funcs vkms_crtc_funcs = { .reset = vkms_atomic_crtc_reset, .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state, .atomic_destroy_state = vkms_atomic_crtc_destroy_state, - .enable_vblank = vkms_enable_vblank, - .disable_vblank = vkms_disable_vblank, - .get_vblank_timestamp = vkms_get_vblank_timestamp, + DRM_CRTC_VBLANK_TIMER_FUNCS, .get_crc_sources = vkms_get_crc_sources, .set_crc_source = vkms_set_crc_source, .verify_crc_source = vkms_verify_crc_source, @@ -213,18 +155,6 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -static void vkms_crtc_atomic_enable(struct drm_crtc *crtc, - struct drm_atomic_state *state) -{ - drm_crtc_vblank_on(crtc); -} - -static void vkms_crtc_atomic_disable(struct drm_crtc *crtc, - struct drm_atomic_state *state) -{ - drm_crtc_vblank_off(crtc); -} - static void vkms_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state) __acquires(&vkms_output->lock) @@ -265,8 +195,9 @@ static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { .atomic_check = vkms_crtc_atomic_check, .atomic_begin = vkms_crtc_atomic_begin, .atomic_flush = vkms_crtc_atomic_flush, - .atomic_enable = vkms_crtc_atomic_enable, - .atomic_disable = vkms_crtc_atomic_disable, + .atomic_enable = drm_crtc_vblank_atomic_enable, + .atomic_disable = drm_crtc_vblank_atomic_disable, + .handle_vblank_timeout = vkms_crtc_handle_vblank_timeout, }; struct vkms_output *vkms_crtc_init(struct drm_device *dev, struct drm_plane *primary, diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 8013c31efe3b..fb9711e1c6fb 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -215,8 +215,6 @@ struct vkms_output { struct drm_crtc crtc; struct drm_writeback_connector wb_connector; struct drm_encoder wb_encoder; - struct hrtimer vblank_hrtimer; - ktime_t period_ns; struct workqueue_struct *composer_workq; spinlock_t lock; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c index eedf1fe60be7..39f8c46550c2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c @@ -37,7 +37,7 @@ static void vmw_gem_object_free(struct drm_gem_object *gobj) { struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(gobj); if (bo) - ttm_bo_put(bo); + ttm_bo_fini(bo); } static int vmw_gem_object_open(struct drm_gem_object *obj, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index 7e281c3c6bc5..c4ac9b47e23a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -15,6 +15,7 @@ #include "vmw_surface_cache.h" #include "device_include/svga3d_surfacedefs.h" +#include <drm/drm_dumb_buffers.h> #include <drm/ttm/ttm_placement.h> #define SVGA3D_FLAGS_64(upper32, lower32) (((uint64_t)upper32 << 32) | lower32) @@ -2267,23 +2268,9 @@ int vmw_dumb_create(struct drm_file *file_priv, * contents is going to be rendered guest side. */ if (!dev_priv->has_mob || !vmw_supports_3d(dev_priv)) { - int cpp = DIV_ROUND_UP(args->bpp, 8); - - switch (cpp) { - case 1: /* DRM_FORMAT_C8 */ - case 2: /* DRM_FORMAT_RGB565 */ - case 4: /* DRM_FORMAT_XRGB8888 */ - break; - default: - /* - * Dumb buffers don't allow anything else. - * This is tested via IGT's dumb_buffers - */ - return -EINVAL; - } - - args->pitch = args->width * cpp; - args->size = ALIGN(args->pitch * args->height, PAGE_SIZE); + ret = drm_mode_size_dumb(dev, args, 0, 0); + if (ret) + return ret; ret = vmw_gem_object_create_with_handle(dev_priv, file_priv, args->size, &args->handle, diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index a1302afb31b9..7b6502081873 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -9,6 +9,7 @@ #include <linux/nospec.h> #include <drm/drm_drv.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_gem_ttm_helper.h> #include <drm/drm_managed.h> #include <drm/ttm/ttm_backup.h> @@ -1737,7 +1738,7 @@ static void xe_gem_object_free(struct drm_gem_object *obj) * refcount directly if needed. */ __xe_bo_vunmap(gem_to_xe_bo(obj)); - ttm_bo_put(container_of(obj, struct ttm_buffer_object, base)); + ttm_bo_fini(container_of(obj, struct ttm_buffer_object, base)); } static void xe_gem_object_close(struct drm_gem_object *obj, @@ -3603,14 +3604,13 @@ int xe_bo_dumb_create(struct drm_file *file_priv, struct xe_device *xe = to_xe_device(dev); struct xe_bo *bo; uint32_t handle; - int cpp = DIV_ROUND_UP(args->bpp, 8); int err; u32 page_size = max_t(u32, PAGE_SIZE, xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K); - args->pitch = ALIGN(args->width * cpp, 64); - args->size = ALIGN(mul_u32_u32(args->pitch, args->height), - page_size); + err = drm_mode_size_dumb(dev, args, SZ_64, page_size); + if (err) + return err; bo = xe_bo_create_user(xe, NULL, args->size, DRM_XE_GEM_CPU_CACHING_WC, diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c index 2bee0a2275ed..02f3a7d78cf8 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -19,6 +19,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> +#include <drm/drm_dumb_buffers.h> #include <drm/drm_encoder.h> #include <drm/drm_fbdev_dma.h> #include <drm/drm_fourcc.h> @@ -363,10 +364,12 @@ static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv, struct drm_mode_create_dumb *args) { struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm); - unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + int ret; /* Enforce the alignment constraints of the DMA engine. */ - args->pitch = ALIGN(pitch, dpsub->dma_align); + ret = drm_mode_size_dumb(drm, args, dpsub->dma_align, 0); + if (ret) + return ret; return drm_gem_dma_dumb_create_internal(file_priv, drm, args); } diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 344cc9e741c1..723a80895cd4 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -471,6 +471,18 @@ static int host1x_device_add(struct host1x *host1x, mutex_unlock(&clients_lock); + /* + * Add device even if there are no subdevs to ensure syncpoint functionality + * is available regardless of whether any engine subdevices are present + */ + if (list_empty(&device->subdevs)) { + err = device_add(&device->dev); + if (err < 0) + dev_err(&device->dev, "failed to add device: %d\n", err); + else + device->registered = true; + } + return 0; } diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 1f93e5e276c0..e365df6af353 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -585,14 +585,8 @@ static int host1x_probe(struct platform_device *pdev) } host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - err = PTR_ERR(host->clk); - - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get clock: %d\n", err); - - return err; - } + if (IS_ERR(host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), "failed to get clock\n"); err = host1x_get_resets(host); if (err) @@ -821,6 +815,7 @@ u64 host1x_get_dma_mask(struct host1x *host1x) } EXPORT_SYMBOL(host1x_get_dma_mask); +MODULE_SOFTDEP("post: tegra-drm"); MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); MODULE_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>"); MODULE_DESCRIPTION("Host1x driver for Tegra products"); diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c index d44b8de890be..2df6a16d484e 100644 --- a/drivers/gpu/host1x/hw/channel_hw.c +++ b/drivers/gpu/host1x/hw/channel_hw.c @@ -47,24 +47,11 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo, } } -static void submit_wait(struct host1x_job *job, u32 id, u32 threshold, - u32 next_class) +static void submit_wait(struct host1x_job *job, u32 id, u32 threshold) { struct host1x_cdma *cdma = &job->channel->cdma; -#if HOST1X_HW >= 6 - u32 stream_id; - - /* - * If a memory context has been set, use it. Otherwise - * (if context isolation is disabled) use the engine's - * firmware stream ID. - */ - if (job->memory_context) - stream_id = job->memory_context->stream_id; - else - stream_id = job->engine_fallback_streamid; - +#if HOST1X_HW >= 2 host1x_cdma_push_wide(cdma, host1x_opcode_setclass( HOST1X_CLASS_HOST1X, @@ -76,23 +63,6 @@ static void submit_wait(struct host1x_job *job, u32 id, u32 threshold, id, HOST1X_OPCODE_NOP ); - host1x_cdma_push_wide(&job->channel->cdma, - host1x_opcode_setclass(job->class, 0, 0), - host1x_opcode_setpayload(stream_id), - host1x_opcode_setstreamid(job->engine_streamid_offset / 4), - HOST1X_OPCODE_NOP); -#elif HOST1X_HW >= 2 - host1x_cdma_push_wide(cdma, - host1x_opcode_setclass( - HOST1X_CLASS_HOST1X, - HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32, - /* WAIT_SYNCPT_32 is at SYNCPT_PAYLOAD_32+2 */ - BIT(0) | BIT(2) - ), - threshold, - id, - host1x_opcode_setclass(next_class, 0, 0) - ); #else /* TODO add waitchk or use waitbases or other mitigation */ host1x_cdma_push(cdma, @@ -103,6 +73,32 @@ static void submit_wait(struct host1x_job *job, u32 id, u32 threshold, ), host1x_class_host_wait_syncpt(id, threshold) ); +#endif +} + +static void submit_setclass(struct host1x_job *job, u32 next_class) +{ + struct host1x_cdma *cdma = &job->channel->cdma; + +#if HOST1X_HW >= 6 + u32 stream_id; + + /* + * If a memory context has been set, use it. Otherwise + * (if context isolation is disabled) use the engine's + * firmware stream ID. + */ + if (job->memory_context) + stream_id = job->memory_context->stream_id; + else + stream_id = job->engine_fallback_streamid; + + host1x_cdma_push_wide(cdma, + host1x_opcode_setclass(next_class, 0, 0), + host1x_opcode_setpayload(stream_id), + host1x_opcode_setstreamid(job->engine_streamid_offset / 4), + HOST1X_OPCODE_NOP); +#else host1x_cdma_push(cdma, host1x_opcode_setclass(next_class, 0, 0), HOST1X_OPCODE_NOP @@ -110,7 +106,8 @@ static void submit_wait(struct host1x_job *job, u32 id, u32 threshold, #endif } -static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base) +static void submit_gathers(struct host1x_job *job, struct host1x_job_cmd *cmds, u32 num_cmds, + u32 job_syncpt_base) { struct host1x_cdma *cdma = &job->channel->cdma; #if HOST1X_HW < 6 @@ -119,8 +116,8 @@ static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base) unsigned int i; u32 threshold; - for (i = 0; i < job->num_cmds; i++) { - struct host1x_job_cmd *cmd = &job->cmds[i]; + for (i = 0; i < num_cmds; i++) { + struct host1x_job_cmd *cmd = &cmds[i]; if (cmd->is_wait) { if (cmd->wait.relative) @@ -128,7 +125,8 @@ static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base) else threshold = cmd->wait.threshold; - submit_wait(job, cmd->wait.id, threshold, cmd->wait.next_class); + submit_wait(job, cmd->wait.id, threshold); + submit_setclass(job, cmd->wait.next_class); } else { struct host1x_job_gather *g = &cmd->gather; @@ -216,7 +214,34 @@ static void channel_program_cdma(struct host1x_job *job) #if HOST1X_HW >= 6 u32 fence; + int i = 0; + + if (job->num_cmds == 0) + goto prefences_done; + if (!job->cmds[0].is_wait || job->cmds[0].wait.relative) + goto prefences_done; + + /* Enter host1x class with invalid stream ID for prefence waits. */ + host1x_cdma_push_wide(cdma, + host1x_opcode_acquire_mlock(1), + host1x_opcode_setclass(1, 0, 0), + host1x_opcode_setpayload(0), + host1x_opcode_setstreamid(0x1fffff)); + + for (i = 0; i < job->num_cmds; i++) { + struct host1x_job_cmd *cmd = &job->cmds[i]; + + if (!cmd->is_wait || cmd->wait.relative) + break; + + submit_wait(job, cmd->wait.id, cmd->wait.threshold); + } + + host1x_cdma_push(cdma, + HOST1X_OPCODE_NOP, + host1x_opcode_release_mlock(1)); +prefences_done: /* Enter engine class with invalid stream ID. */ host1x_cdma_push_wide(cdma, host1x_opcode_acquire_mlock(job->class), @@ -230,11 +255,12 @@ static void channel_program_cdma(struct host1x_job *job) host1x_opcode_nonincr(HOST1X_UCLASS_INCR_SYNCPT, 1), HOST1X_UCLASS_INCR_SYNCPT_INDX_F(job->syncpt->id) | HOST1X_UCLASS_INCR_SYNCPT_COND_F(4)); - submit_wait(job, job->syncpt->id, fence, job->class); + submit_wait(job, job->syncpt->id, fence); + submit_setclass(job, job->class); /* Submit work. */ job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs); - submit_gathers(job, job->syncpt_end - job->syncpt_incrs); + submit_gathers(job, job->cmds + i, job->num_cmds - i, job->syncpt_end - job->syncpt_incrs); /* Before releasing MLOCK, ensure engine is idle again. */ fence = host1x_syncpt_incr_max(sp, 1); @@ -242,7 +268,7 @@ static void channel_program_cdma(struct host1x_job *job) host1x_opcode_nonincr(HOST1X_UCLASS_INCR_SYNCPT, 1), HOST1X_UCLASS_INCR_SYNCPT_INDX_F(job->syncpt->id) | HOST1X_UCLASS_INCR_SYNCPT_COND_F(4)); - submit_wait(job, job->syncpt->id, fence, job->class); + submit_wait(job, job->syncpt->id, fence); /* Release MLOCK. */ host1x_cdma_push(cdma, @@ -272,7 +298,7 @@ static void channel_program_cdma(struct host1x_job *job) job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs); - submit_gathers(job, job->syncpt_end - job->syncpt_incrs); + submit_gathers(job, job->cmds, job->num_cmds, job->syncpt_end - job->syncpt_incrs); #endif } diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index f63d14a57a1d..acc7d82e0585 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -345,8 +345,6 @@ static void syncpt_release(struct kref *ref) sp->locked = false; - mutex_lock(&sp->host->syncpt_mutex); - host1x_syncpt_base_free(sp->base); kfree(sp->name); sp->base = NULL; @@ -369,7 +367,7 @@ void host1x_syncpt_put(struct host1x_syncpt *sp) if (!sp) return; - kref_put(&sp->ref, syncpt_release); + kref_put_mutex(&sp->ref, syncpt_release, &sp->host->syncpt_mutex); } EXPORT_SYMBOL(host1x_syncpt_put); |