diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,dsi.yaml b/Documentation/devicetree/bindings/display/bridge/renesas,dsi.yaml index c20625b8425e..3832a553871b 100644 --- a/Documentation/devicetree/bindings/display/bridge/renesas,dsi.yaml +++ b/Documentation/devicetree/bindings/display/bridge/renesas,dsi.yaml @@ -28,6 +28,7 @@ properties: - const: renesas,r9a09g057-mipi-dsi - enum: + - renesas,r9a08g046-mipi-dsi # RZ/G3L - renesas,r9a09g057-mipi-dsi # RZ/V2H(P) reg: diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml b/Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml new file mode 100644 index 000000000000..4cd7b688fbf7 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/renesas,r9a08g046-lvds.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/renesas,r9a08g046-lvds.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas RZ/G3L LVDS Encoder + +maintainers: + - Biju Das + - Tommaso Merciai + +description: | + This binding describes the LVDS encoder embedded in the Renesas RZ/G3L + SoC. The encoder can operate in LVDS Single-link mode with 4 lanes + (Data) + 1 lane (Clock). + +properties: + compatible: + const: renesas,r9a08g046-lvds + + reg: + maxItems: 1 + + clocks: + items: + - description: Peripheral clock + - description: PHY clock + - description: Dot clock + + clock-names: + items: + - const: pclk + - const: phyclk + - const: dotclk + + resets: + items: + - description: LVDS_RESET_N + - description: MIPI_DSI_PRESET_N + - description: MIPI_DSI_CMN_RSTB + - description: MIPI_DSI_ARESET_N + + reset-names: + items: + - const: lvdrst + - const: prst + - const: rst + - const: arst + + power-domains: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Input channel, directly connected to the Display Unit. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: | + Output channel, directly connected to the LVDS panel or bridge. + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - power-domains + - ports + +additionalProperties: false + +examples: + - | + #include + + lvds@108a0000 { + compatible = "renesas,r9a08g046-lvds"; + reg = <0x108a0000 0x10000>; + clocks = <&cpg CPG_MOD R9A08G046_MIPI_DSI_PCLK>, + <&cpg CPG_MOD R9A08G046_LVDS_PLLCLK>, + <&cpg CPG_MOD R9A08G046_LVDS_CLK_DOT0>; + clock-names = "pclk", "phyclk", "dotclk"; + resets = <&cpg R9A08G046_LVDS_RESET_N>, + <&cpg R9A08G046_MIPI_DSI_PRESET_N>, + <&cpg R9A08G046_MIPI_DSI_CMN_RSTB>, + <&cpg R9A08G046_MIPI_DSI_ARESET_N>; + reset-names = "lvdrst", "prst", "rst", "arst"; + power-domains = <&cpg>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + lvds0_in: endpoint { + remote-endpoint = <&du_out_lvds0>; + }; + }; + + port@1 { + reg = <1>; + lvds0_out: endpoint { + remote-endpoint = <&panel_in>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml index 4eae802de9fd..9e20e2faf82f 100644 --- a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml +++ b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml @@ -19,6 +19,7 @@ properties: - anbernic,rg-ds-display-bottom - anbernic,rg-ds-display-top - chongzhou,cz101b4001 + - dcltek,dt300250 - kingdisplay,kd101ne3-40ti - melfas,lmfbx101117480 - radxa,display-10hd-ad001 diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 21453f0d7ce2..3d1cc11ba3aa 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -37,6 +37,8 @@ properties: - ampire,am-480272h3tmqw-t01h # Ampire AM-800480L1TMQW-T00H 5" WVGA TFT LCD panel - ampire,am-800480l1tmqw-t00h + # Ampire AM-800480N3TZQW-00H 5" WVGA TFT LCD panel + - ampire,am-800480n3tzqw-00h # Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel - ampire,am800480r3tmqwa1h # Ampire AM-800600P5TMQW-TB8H 8.0" SVGA TFT LCD panel diff --git a/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml b/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml index ebfc825b8346..91e06d6a6819 100644 --- a/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml +++ b/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml @@ -22,6 +22,8 @@ properties: enum: # Anberic RG353V-V2 5.0" 640x480 TFT LCD panel - anbernic,rg353v-panel-v2 + # AYANEO Pocket DS lower 5.0" 1024x768 IPS LCD panel + - ayaneo,pocket-ds-lower-panel # GameForce Chi 3.5" 640x480 TFT LCD panel - gameforce,chi-panel # Powkiddy RGB10MAX3 5.0" 720x1280 TFT LCD panel diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml index 7c84a9ecc7a7..5c9b15a09dee 100644 --- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml +++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml @@ -20,6 +20,7 @@ properties: - enum: - renesas,r9a07g043u-du # RZ/G2UL - renesas,r9a07g044-du # RZ/G2{L,LC} + - renesas,r9a08g046-du # RZ/G3L - renesas,r9a09g057-du # RZ/V2H(P) - renesas,r9a09g077-du # RZ/T2H - items: @@ -65,7 +66,7 @@ properties: model-dependent. Each port shall have a single endpoint. patternProperties: - "^port@[0-1]$": + "^port@[0-2]$": $ref: /schemas/graph.yaml#/properties/port unevaluatedProperties: false @@ -108,6 +109,7 @@ allOf: port@0: description: DPI port@1: false + port@2: false required: - port@0 @@ -124,10 +126,31 @@ allOf: description: DSI port@1: description: DPI + port@2: false required: - port@0 - port@1 + - if: + properties: + compatible: + contains: + const: renesas,r9a08g046-du + then: + properties: + ports: + properties: + port@0: + description: DSI + port@1: + description: DPI + port@2: + description: LVDS + + required: + - port@0 + - port@1 + - port@2 - if: properties: compatible: @@ -140,6 +163,7 @@ allOf: port@0: description: DSI port@1: false + port@2: false required: - port@0 diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index b1af9deac2e9..1e54877628c7 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -409,6 +409,8 @@ patternProperties: description: DataImage, Inc. "^davicom,.*": description: DAVICOM Semiconductor, Inc. + "^dcltek,.*": + description: DCL Technologies Inc. "^deepcomputing,.*": description: DeepComputing (HK) Limited "^dell,.*": @@ -1253,6 +1255,8 @@ patternProperties: description: OpenWrt "^option,.*": description: Option NV + "^optologic,.*": + description: Opto Logic S.A. "^oranth,.*": description: Shenzhen Oranth Technology Co., Ltd. "^ORCL,.*": diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index bced421c0d65..639e93704924 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -20,9 +20,9 @@ static int list_limit = 1024; module_param(list_limit, int, 0644); MODULE_PARM_DESC(list_limit, "udmabuf_create_list->count limit. Default is 1024."); -static int size_limit_mb = 64; +static int size_limit_mb = INT_MAX; module_param(size_limit_mb, int, 0644); -MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is 64."); +MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is INT_MAX."); struct udmabuf { pgoff_t pagecount; diff --git a/drivers/gpu/Kconfig b/drivers/gpu/Kconfig index ebb2ad4b7ea0..3bcf08260d73 100644 --- a/drivers/gpu/Kconfig +++ b/drivers/gpu/Kconfig @@ -7,7 +7,8 @@ config GPU_BUDDY config GPU_BUDDY_KUNIT_TEST tristate "KUnit tests for GPU buddy allocator" if !KUNIT_ALL_TESTS - depends on GPU_BUDDY && KUNIT + depends on KUNIT + select GPU_BUDDY default KUNIT_ALL_TESTS help KUnit tests for the GPU buddy allocator. diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index dc81fe0301ce..4d5ac375a538 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -630,6 +630,59 @@ void gpu_buddy_free_block(struct gpu_buddy *mm, } EXPORT_SYMBOL(gpu_buddy_free_block); +/** + * gpu_buddy_allocated_addr_to_block - given relative address find the allocated block + * + * @mm: GPU buddy manager + * @addr: Relative address + * + * Returns: + * gpu_buddy_block on success, NULL or error code on failure + */ +struct gpu_buddy_block *gpu_buddy_allocated_addr_to_block(struct gpu_buddy *mm, u64 addr) +{ + struct gpu_buddy_block *block; + LIST_HEAD(dfs); + u64 end; + int i; + + gpu_buddy_driver_lock_held(mm); + + end = addr + mm->chunk_size - 1; + for (i = 0; i < mm->n_roots; ++i) + list_add_tail(&mm->roots[i]->tmp_link, &dfs); + + do { + u64 block_start; + u64 block_end; + + block = list_first_entry_or_null(&dfs, + struct gpu_buddy_block, + tmp_link); + if (!block) + break; + + list_del(&block->tmp_link); + + block_start = gpu_buddy_block_offset(block); + block_end = block_start + gpu_buddy_block_size(mm, block) - 1; + + if (!overlaps(addr, end, block_start, block_end)) + continue; + + if (gpu_buddy_block_is_allocated(block)) + return block; + else if (gpu_buddy_block_is_free(block)) + return NULL; + + list_add(&block->right->tmp_link, &dfs); + list_add(&block->left->tmp_link, &dfs); + } while (1); + + return ERR_PTR(-ENXIO); +} +EXPORT_SYMBOL(gpu_buddy_allocated_addr_to_block); + static void __gpu_buddy_free_list(struct gpu_buddy *mm, struct list_head *objects, bool mark_clear, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 5bcb6a966941..74c1521b8efc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1526,7 +1526,7 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, dma_resv_for_each_fence(&resv_cursor, bo->base.resv, DMA_RESV_USAGE_BOOKKEEP, f) { if (amdkfd_fence_check_mm(f, current->mm) && - !(place->flags & TTM_PL_FLAG_CONTIGUOUS)) + !(place && (place->flags & TTM_PL_FLAG_CONTIGUOUS))) return false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 2a241a5b12c4..249f07f30951 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -23,6 +23,8 @@ */ #include +#include +#include #include #include #include @@ -905,6 +907,38 @@ static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = { .debug = amdgpu_vram_mgr_debug }; +static const struct dmem_cgroup_ops amdgpu_vram_mgr_dmem_ops; + +static int amdgpu_vram_mgr_dmem_reclaim(struct dmem_cgroup_pool_state *pool, + u64 target_bytes, void *priv) +{ + struct ttm_resource_manager *man = priv; + struct amdgpu_device *adev = amdgpu_ttm_adev(man->bdev); + int ret, idx; + + if (!drm_dev_enter(adev_to_drm(adev), &idx)) + return -ENODEV; + + ret = pm_runtime_get_sync(adev_to_drm(adev)->dev); + if (ret < 0) { + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); + goto out; + } + + ret = ttm_resource_manager_dmem_reclaim(pool, target_bytes, priv); + + pm_runtime_mark_last_busy(adev_to_drm(adev)->dev); + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); + +out: + drm_dev_exit(idx); + return ret; +} + +static const struct dmem_cgroup_ops amdgpu_vram_mgr_dmem_ops = { + .reclaim = amdgpu_vram_mgr_dmem_reclaim, +}; + /** * amdgpu_vram_mgr_init - init VRAM manager and DRM MM * @@ -916,11 +950,9 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev) { struct amdgpu_vram_mgr *mgr = &adev->mman.vram_mgr; struct ttm_resource_manager *man = &mgr->manager; + struct dmem_cgroup_region *cg; int err; - man->cg = drmm_cgroup_register_region(adev_to_drm(adev), "vram", adev->gmc.real_vram_size); - if (IS_ERR(man->cg)) - return PTR_ERR(man->cg); ttm_resource_manager_init(man, &adev->mman.bdev, adev->gmc.real_vram_size); @@ -935,6 +967,20 @@ int amdgpu_vram_mgr_init(struct amdgpu_device *adev) if (err) return err; + cg = dmem_cgroup_register_region(&(struct dmem_cgroup_init){ + .size = adev->gmc.real_vram_size, + .ops = &amdgpu_vram_mgr_dmem_ops, + .reclaim_priv = man, + }, + "drm/%s/vram", adev_to_drm(adev)->unique); + if (IS_ERR(cg)) { + gpu_buddy_fini(&mgr->mm); + return PTR_ERR(cg); + } + + mgr->cg_region = cg; + ttm_resource_manager_set_dmem_region(man, cg); + ttm_set_driver_manager(&adev->mman.bdev, TTM_PL_VRAM, &mgr->manager); ttm_resource_manager_set_used(man, true); return 0; @@ -958,6 +1004,19 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev) ttm_resource_manager_set_used(man, false); ret = ttm_resource_manager_evict_all(&adev->mman.bdev, man); + + /* + * Unregister the dmem cgroup region regardless of the evict_all() + * result and before any further teardown. This drains in-flight + * reclaim callbacks and blocks new ones, so no reclaim can reference + * the manager once we start freeing it. It must run after evict_all() + * so that ttm_resource_free() can still uncharge via man->cg during + * eviction. Clear man->cg afterwards. + */ + dmem_cgroup_unregister_region(mgr->cg_region); + mgr->cg_region = NULL; + ttm_resource_manager_set_dmem_region(man, NULL); + if (ret) return; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h index 429a21a2e9b2..e581cbf52fcb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h @@ -26,6 +26,8 @@ #include +struct dmem_cgroup_region; + struct amdgpu_vram_mgr { struct ttm_resource_manager manager; struct gpu_buddy mm; @@ -36,6 +38,7 @@ struct amdgpu_vram_mgr { atomic64_t vis_usage; u64 default_page_size; struct list_head allocated_vres_list; + struct dmem_cgroup_region *cg_region; }; struct amdgpu_vres_task { diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index db99e252aeeb..038b852405b5 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1471,11 +1471,11 @@ static const struct adv7511_chip_info adv7535_chip_info = { }; static const struct i2c_device_id adv7511_i2c_ids[] = { - { "adv7511", (kernel_ulong_t)&adv7511_chip_info }, - { "adv7511w", (kernel_ulong_t)&adv7511_chip_info }, - { "adv7513", (kernel_ulong_t)&adv7511_chip_info }, - { "adv7533", (kernel_ulong_t)&adv7533_chip_info }, - { "adv7535", (kernel_ulong_t)&adv7535_chip_info }, + { .name = "adv7511", .driver_data = (kernel_ulong_t)&adv7511_chip_info }, + { .name = "adv7511w", .driver_data = (kernel_ulong_t)&adv7511_chip_info }, + { .name = "adv7513", .driver_data = (kernel_ulong_t)&adv7511_chip_info }, + { .name = "adv7533", .driver_data = (kernel_ulong_t)&adv7533_chip_info }, + { .name = "adv7535", .driver_data = (kernel_ulong_t)&adv7535_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, adv7511_i2c_ids); diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index d152c02419f5..407834a03b63 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -783,7 +783,7 @@ static void anx6345_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id anx6345_id[] = { - { "anx6345" }, + { .name = "anx6345" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, anx6345_id); diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index ebd34657908e..c3af88e14f8b 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2993,8 +2993,8 @@ static void anx7625_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id anx7625_id[] = { - { "anx7625" }, - {} + { .name = "anx7625" }, + { } }; MODULE_DEVICE_TABLE(i2c, anx7625_id); diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index eb628a0ce6ec..0f6173c61502 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -784,8 +784,8 @@ static struct mipi_dsi_driver chipone_dsi_driver = { }; static const struct i2c_device_id chipone_i2c_id[] = { - { "chipone,icn6211" }, - {}, + { .name = "chipone,icn6211" }, + { } }; MODULE_DEVICE_TABLE(i2c, chipone_i2c_id); diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c index 92d464727e41..4bc64b2885f7 100644 --- a/drivers/gpu/drm/bridge/chrontel-ch7033.c +++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c @@ -601,7 +601,7 @@ static const struct of_device_id ch7033_dt_ids[] = { MODULE_DEVICE_TABLE(of, ch7033_dt_ids); static const struct i2c_device_id ch7033_ids[] = { - { "ch7033" }, + { .name = "ch7033" }, { } }; MODULE_DEVICE_TABLE(i2c, ch7033_ids); diff --git a/drivers/gpu/drm/bridge/imx/Kconfig b/drivers/gpu/drm/bridge/imx/Kconfig index 005a745e5c47..49fcecfee38e 100644 --- a/drivers/gpu/drm/bridge/imx/Kconfig +++ b/drivers/gpu/drm/bridge/imx/Kconfig @@ -107,4 +107,15 @@ config DRM_IMX93_MIPI_DSI Choose this to enable MIPI DSI controller found in Freescale i.MX93 processor. +config DRM_IMX93_PARALLEL_DISP_FMT_CONVERTER + tristate "NXP i.MX91/i.MX93 parallel display format converter" + depends on OF + select DRM_KMS_HELPER + help + On i.MX93 and i.MX91 SoCs the parallel display format output is + controlled via the MEDIAMIX BLK-CTRL DISPLAY_MUX. + + Say 'Y' or 'M' if you use the parallel display output path on a + i.MX93 or i.MX91 SoC. + endif # ARCH_MXC || COMPILE_TEST diff --git a/drivers/gpu/drm/bridge/imx/Makefile b/drivers/gpu/drm/bridge/imx/Makefile index 94ac8c40ebe9..99f28cd013ae 100644 --- a/drivers/gpu/drm/bridge/imx/Makefile +++ b/drivers/gpu/drm/bridge/imx/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_COMBINER) += imx8qxp-pixel-combiner.o obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK) += imx8qxp-pixel-link.o obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI) += imx8qxp-pxl2dpi.o obj-$(CONFIG_DRM_IMX93_MIPI_DSI) += imx93-mipi-dsi.o +obj-$(CONFIG_DRM_IMX93_PARALLEL_DISP_FMT_CONVERTER) += imx93-pdfc.o diff --git a/drivers/gpu/drm/bridge/imx/imx93-pdfc.c b/drivers/gpu/drm/bridge/imx/imx93-pdfc.c new file mode 100644 index 000000000000..2d6ca95ed554 --- /dev/null +++ b/drivers/gpu/drm/bridge/imx/imx93-pdfc.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2022-2025 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define IMX93_DISPLAY_MUX_REG 0x60 +#define PARALLEL_DISP_FORMAT GENMASK(10, 8) +#define FORMAT_RGB888_TO_RGB888 FIELD_PREP(PARALLEL_DISP_FORMAT, 0) +#define FORMAT_RGB888_TO_RGB666 FIELD_PREP(PARALLEL_DISP_FORMAT, 1) +#define FORMAT_RGB565_TO_RGB565 FIELD_PREP(PARALLEL_DISP_FORMAT, 2) + +struct imx93_pdfc { + struct drm_bridge bridge; + struct device *dev; + struct regmap *regmap; + u32 phy_bus_width; +}; + +static struct imx93_pdfc *bridge_to_imx93_pdfc(struct drm_bridge *bridge) +{ + return container_of(bridge, struct imx93_pdfc, bridge); +} + +static int +imx93_pdfc_bridge_attach(struct drm_bridge *bridge, struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + return drm_bridge_attach(bridge->encoder, bridge->next_bridge, bridge, flags); +} + +static void imx93_pdfc_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_commit *state) +{ + struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge); + const struct drm_bridge_state *bridge_state; + unsigned int mask = PARALLEL_DISP_FORMAT; + unsigned int val; + + bridge_state = drm_atomic_get_new_bridge_state(state, bridge); + + switch (bridge_state->output_bus_cfg.format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_FIXED: + val = FORMAT_RGB888_TO_RGB888; + if (pdfc->phy_bus_width == 18) { + /* + * Can be valid if physical bus limitation exists, + * therefore use dev_dbg(). + */ + dev_dbg(pdfc->dev, "Truncate two LSBs from each color\n"); + val = FORMAT_RGB888_TO_RGB666; + } + break; + case MEDIA_BUS_FMT_RGB666_1X18: + val = FORMAT_RGB888_TO_RGB666; + break; + case MEDIA_BUS_FMT_RGB565_1X16: + val = FORMAT_RGB565_TO_RGB565; + break; + } + + regmap_update_bits(pdfc->regmap, IMX93_DISPLAY_MUX_REG, mask, val); +} + +/* TODO: Add YUV formats */ +static const u32 imx93_pdfc_bus_output_fmts[] = { + MEDIA_BUS_FMT_FIXED, + MEDIA_BUS_FMT_RGB888_1X24, + MEDIA_BUS_FMT_RGB666_1X18, + MEDIA_BUS_FMT_RGB565_1X16, +}; + +static bool imx93_pdfc_bus_output_fmt_supported(u32 fmt) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(imx93_pdfc_bus_output_fmts); i++) { + if (imx93_pdfc_bus_output_fmts[i] == fmt) + return true; + } + + return false; +} + +static u32 * +imx93_pdfc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + u32 *input_fmts; + + *num_input_fmts = 0; + + if (!imx93_pdfc_bus_output_fmt_supported(output_fmt)) + return NULL; + + input_fmts = kmalloc_obj(*input_fmts); + if (!input_fmts) + return NULL; + + *num_input_fmts = 1; + + switch (output_fmt) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB565_1X16: + input_fmts[0] = output_fmt; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + case MEDIA_BUS_FMT_FIXED: + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + break; + } + + return input_fmts; +} + +static int imx93_pdfc_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge); + u32 format = bridge_state->output_bus_cfg.format; + + if (imx93_pdfc_bus_output_fmt_supported(format)) + return 0; + + dev_warn(pdfc->dev, "Unsupported output bus format: 0x%x\n", format); + + return -EINVAL; +} + +static const struct drm_bridge_funcs funcs = { + .attach = imx93_pdfc_bridge_attach, + .atomic_enable = imx93_pdfc_bridge_atomic_enable, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = imx93_pdfc_bridge_atomic_get_input_bus_fmts, + .atomic_check = imx93_pdfc_bridge_atomic_check, + .atomic_create_state = drm_atomic_helper_bridge_create_state, +}; + +static int imx93_pdfc_bridge_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct drm_bridge *next_bridge; + struct imx93_pdfc *pdfc; + struct device_node *ep; + int err; + + pdfc = devm_drm_bridge_alloc(dev, struct imx93_pdfc, bridge, &funcs); + if (IS_ERR(pdfc)) + return PTR_ERR(pdfc); + + pdfc->regmap = syscon_node_to_regmap(dev->of_node->parent); + if (IS_ERR(pdfc->regmap)) + return dev_err_probe(dev, PTR_ERR(pdfc->regmap), + "failed to get regmap\n"); + + /* No limits per default */ + pdfc->phy_bus_width = 24; + + /* Get output ep (port1/endpoint) */ + ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); + if (ep) { + err = of_property_read_u32(ep, "bus-width", &pdfc->phy_bus_width); + of_node_put(ep); + + /* bus-width is optional but it must have valid data if present */ + if (err && err != -EINVAL) + return dev_err_probe(dev, err, + "failed to query bus-width\n"); + } + + next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); + if (IS_ERR(next_bridge)) + return dev_err_probe(dev, PTR_ERR(next_bridge), + "failed to get next bridge\n"); + pdfc->dev = dev; + pdfc->bridge.of_node = dev->of_node; + pdfc->bridge.type = DRM_MODE_CONNECTOR_DPI; + pdfc->bridge.next_bridge = drm_bridge_get(next_bridge); + + return devm_drm_bridge_add(dev, &pdfc->bridge); +} + +static const struct of_device_id imx93_pdfc_dt_ids[] = { + { .compatible = "nxp,imx91-pdfc", }, + { .compatible = "nxp,imx93-pdfc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx93_pdfc_dt_ids); + +static struct platform_driver imx93_pdfc_bridge_driver = { + .probe = imx93_pdfc_bridge_probe, + .driver = { + .of_match_table = imx93_pdfc_dt_ids, + .name = "imx93_pdfc", + }, +}; +module_platform_driver(imx93_pdfc_bridge_driver); + +MODULE_DESCRIPTION("NXP i.MX93 parallel display format configuration driver"); +MODULE_AUTHOR("Liu Ying "); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c index 4c0b670806cc..21ac8e58b9d2 100644 --- a/drivers/gpu/drm/bridge/ite-it6263.c +++ b/drivers/gpu/drm/bridge/ite-it6263.c @@ -910,7 +910,7 @@ static const struct of_device_id it6263_of_match[] = { MODULE_DEVICE_TABLE(of, it6263_of_match); static const struct i2c_device_id it6263_i2c_ids[] = { - { "it6263" }, + { .name = "it6263" }, { } }; MODULE_DEVICE_TABLE(i2c, it6263_i2c_ids); diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 8ecb43611dba..025a19c1e530 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -3652,7 +3652,7 @@ static void it6505_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id it6505_id[] = { - { "it6505" }, + { .name = "it6505" }, { } }; diff --git a/drivers/gpu/drm/bridge/lontium-lt8713sx.c b/drivers/gpu/drm/bridge/lontium-lt8713sx.c index 4a6420b876da..bfff28883280 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8713sx.c +++ b/drivers/gpu/drm/bridge/lontium-lt8713sx.c @@ -572,7 +572,7 @@ static void lt8713sx_remove(struct i2c_client *client) } static struct i2c_device_id lt8713sx_id[] = { - { "lontium,lt8713sx", 0 }, + { .name = "lontium,lt8713sx" }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index df5d07e7644c..e347d04b7b2d 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -677,7 +677,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(lt8912_bridge_pm_ops, lt8912_bridge_suspend, lt8 static int lt8912_get_regulators(struct lt8912 *lt) { unsigned int i; - const char * const supply_names[] = { + static const char * const supply_names[] = { "vdd", "vccmipirx", "vccsysclk", "vcclvdstx", "vcchdmitx", "vcclvdspll", "vcchdmipll" }; @@ -820,8 +820,8 @@ static const struct of_device_id lt8912_dt_match[] = { MODULE_DEVICE_TABLE(of, lt8912_dt_match); static const struct i2c_device_id lt8912_id[] = { - { "lt8912" }, - {} + { .name = "lt8912" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt8912_id); diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index 6d97ae5640c4..23682384daf4 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -773,8 +773,8 @@ static void lt9211_remove(struct i2c_client *client) } static const struct i2c_device_id lt9211_id[] = { - { "lontium,lt9211" }, - {}, + { .name = "lontium,lt9211" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt9211_id); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index b6a368f71285..fb34f661ee0a 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -1229,8 +1229,8 @@ static void lt9611_remove(struct i2c_client *client) } static const struct i2c_device_id lt9611_id[] = { - { "lontium,lt9611" }, - {} + { .name = "lontium,lt9611" }, + { } }; MODULE_DEVICE_TABLE(i2c, lt9611_id); diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index 6625ba23761c..42a6503cd0f9 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -899,7 +899,7 @@ static void lt9611uxc_remove(struct i2c_client *client) } static const struct i2c_device_id lt9611uxc_id[] = { - { "lontium,lt9611uxc" }, + { .name = "lontium,lt9611uxc" }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 25ff0a0acc93..4d244e6d4ed9 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -317,8 +317,8 @@ static void stdp4028_ge_b850v3_fw_remove(struct i2c_client *stdp4028_i2c) } static const struct i2c_device_id stdp4028_ge_b850v3_fw_i2c_table[] = { - { "stdp4028_ge_fw" }, - {} + { .name = "stdp4028_ge_fw" }, + { } }; MODULE_DEVICE_TABLE(i2c, stdp4028_ge_b850v3_fw_i2c_table); @@ -364,8 +364,8 @@ static void stdp2690_ge_b850v3_fw_remove(struct i2c_client *stdp2690_i2c) } static const struct i2c_device_id stdp2690_ge_b850v3_fw_i2c_table[] = { - { "stdp2690_ge_fw" }, - {} + { .name = "stdp2690_ge_fw" }, + { } }; MODULE_DEVICE_TABLE(i2c, stdp2690_ge_b850v3_fw_i2c_table); diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index a9b4ac2defc9..f9eff8bfceb7 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -323,8 +323,8 @@ static void ptn3460_remove(struct i2c_client *client) } static const struct i2c_device_id ptn3460_i2c_table[] = { - { "ptn3460" }, - {} + { .name = "ptn3460" }, + { } }; MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table); diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 33844db753aa..3a2ec482037d 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -534,10 +534,10 @@ static void ps8622_remove(struct i2c_client *client) } static const struct i2c_device_id ps8622_i2c_table[] = { - /* Device type, max_lane_count */ - {"ps8622", 1}, - {"ps8625", 2}, - {}, + /* Device type, driver_data holds the maximal lane_count */ + { .name = "ps8622", .driver_data = 1 }, + { .name = "ps8625", .driver_data = 2 }, + { } }; MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table); diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index f575b2dbe32b..9aac44ed5b67 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -1235,7 +1235,7 @@ static const struct of_device_id sii902x_dt_ids[] = { MODULE_DEVICE_TABLE(of, sii902x_dt_ids); static const struct i2c_device_id sii902x_i2c_ids[] = { - { "sii9022" }, + { .name = "sii9022" }, { } }; MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids); diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c index be67642ab7d3..9fef332db248 100644 --- a/drivers/gpu/drm/bridge/sii9234.c +++ b/drivers/gpu/drm/bridge/sii9234.c @@ -944,7 +944,7 @@ static const struct of_device_id sii9234_dt_match[] = { MODULE_DEVICE_TABLE(of, sii9234_dt_match); static const struct i2c_device_id sii9234_id[] = { - { "SII9234" }, + { .name = "SII9234" }, { } }; MODULE_DEVICE_TABLE(i2c, sii9234_id); diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 22c81b38b41e..b452748ad9db 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2374,7 +2374,7 @@ static const struct of_device_id sii8620_dt_match[] = { MODULE_DEVICE_TABLE(of, sii8620_dt_match); static const struct i2c_device_id sii8620_id[] = { - { "sii8620" }, + { .name = "sii8620" }, { } }; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c index 3445c82e6f50..8b8529e1723c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c @@ -1949,7 +1949,6 @@ static const struct regmap_config dw_dp_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .fast_io = true, .max_register = DW_DP_MAX_REGISTER, .rd_table = &dw_dp_readable_table, }; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index a408dbd414bf..34c5f583e910 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3389,7 +3389,8 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, break; default: dev_err(dev, "reg-io-width must be 1 or 4\n"); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto err_res; } iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c index 5f06cb03b4c0..7fd2f1e60654 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c @@ -907,7 +907,6 @@ static const struct regmap_config dw_mipi_dsi2_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .fast_io = true, }; static struct dw_mipi_dsi2 * diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index 6124d3123f57..76564d5e4f69 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -292,25 +292,17 @@ static int tc358762_probe(struct mipi_dsi_device *dsi) ctx->bridge.of_node = dev->of_node; ctx->bridge.pre_enable_prev_first = true; - drm_bridge_add(&ctx->bridge); + ret = devm_drm_bridge_add(dev, &ctx->bridge); + if (ret < 0) + return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - drm_bridge_remove(&ctx->bridge); + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) dev_err(dev, "failed to attach dsi\n"); - } return ret; } -static void tc358762_remove(struct mipi_dsi_device *dsi) -{ - struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi); - - mipi_dsi_detach(dsi); - drm_bridge_remove(&ctx->bridge); -} - static const struct of_device_id tc358762_of_match[] = { { .compatible = "toshiba,tc358762" }, { } @@ -319,7 +311,6 @@ MODULE_DEVICE_TABLE(of, tc358762_of_match); static struct mipi_dsi_driver tc358762_driver = { .probe = tc358762_probe, - .remove = tc358762_remove, .driver = { .name = "tc358762", .of_match_table = tc358762_of_match, diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index f55a56cbd88f..049215e65988 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -376,25 +376,17 @@ static int tc358764_probe(struct mipi_dsi_device *dsi) ctx->bridge.of_node = dev->of_node; ctx->bridge.pre_enable_prev_first = true; - drm_bridge_add(&ctx->bridge); + ret = devm_drm_bridge_add(dev, &ctx->bridge); + if (ret < 0) + return ret; - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - drm_bridge_remove(&ctx->bridge); + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) dev_err(dev, "failed to attach dsi\n"); - } return ret; } -static void tc358764_remove(struct mipi_dsi_device *dsi) -{ - struct tc358764 *ctx = mipi_dsi_get_drvdata(dsi); - - mipi_dsi_detach(dsi); - drm_bridge_remove(&ctx->bridge); -} - static const struct of_device_id tc358764_of_match[] = { { .compatible = "toshiba,tc358764" }, { } @@ -403,7 +395,6 @@ MODULE_DEVICE_TABLE(of, tc358764_of_match); static struct mipi_dsi_driver tc358764_driver = { .probe = tc358764_probe, - .remove = tc358764_remove, .driver = { .name = "tc358764", .of_match_table = tc358764_of_match, diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 8f1648f799a7..e3a57f8228da 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -2613,7 +2613,7 @@ static void tc_remove(struct i2c_client *client) } static const struct i2c_device_id tc358767_i2c_ids[] = { - { "tc358767" }, + { .name = "tc358767" }, { } }; MODULE_DEVICE_TABLE(i2c, tc358767_i2c_ids); diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 112a421f99b5..d1fc6af37cc5 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -1362,8 +1362,8 @@ static const struct regmap_config tc358768_regmap_config = { }; static const struct i2c_device_id tc358768_i2c_ids[] = { - { "tc358768" }, - { "tc358778" }, + { .name = "tc358768" }, + { .name = "tc358778" }, { } }; MODULE_DEVICE_TABLE(i2c, tc358768_i2c_ids); diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 728938f9dcb4..203dc738ecef 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -727,8 +727,8 @@ static void tc_remove(struct i2c_client *client) } static const struct i2c_device_id tc358775_i2c_ids[] = { - { "tc358765", TC358765, }, - { "tc358775", TC358775, }, + { .name = "tc358765", .driver_data = TC358765 }, + { .name = "tc358775", .driver_data = TC358775 }, { } }; MODULE_DEVICE_TABLE(i2c, tc358775_i2c_ids); diff --git a/drivers/gpu/drm/bridge/tda998x_drv.c b/drivers/gpu/drm/bridge/tda998x_drv.c index 16c745591175..0b966c9844e7 100644 --- a/drivers/gpu/drm/bridge/tda998x_drv.c +++ b/drivers/gpu/drm/bridge/tda998x_drv.c @@ -2064,7 +2064,7 @@ MODULE_DEVICE_TABLE(of, tda998x_dt_ids); #endif static const struct i2c_device_id tda998x_ids[] = { - { "tda998x" }, + { .name = "tda998x" }, { } }; MODULE_DEVICE_TABLE(i2c, tda998x_ids); diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c index c16eb3a05cfa..c9f789e30687 100644 --- a/drivers/gpu/drm/bridge/ti-dlpc3433.c +++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c @@ -389,7 +389,7 @@ static void dlpc3433_remove(struct i2c_client *client) } static const struct i2c_device_id dlpc3433_id[] = { - { "ti,dlpc3433" }, + { .name = "ti,dlpc3433" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, dlpc3433_id); diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index e6dbe51d0dba..f9fdbf48c6b3 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -1072,9 +1072,9 @@ static void sn65dsi83_remove(struct i2c_client *client) } static const struct i2c_device_id sn65dsi83_id[] = { - { "ti,sn65dsi83", MODEL_SN65DSI83 }, - { "ti,sn65dsi84", MODEL_SN65DSI84 }, - {}, + { .name = "ti,sn65dsi83", .driver_data = MODEL_SN65DSI83 }, + { .name = "ti,sn65dsi84", .driver_data = MODEL_SN65DSI84 }, + { } }; MODULE_DEVICE_TABLE(i2c, sn65dsi83_id); diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 1da242f17413..48b83df9aed6 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -2080,7 +2080,7 @@ static int ti_sn65dsi86_probe(struct i2c_client *client) dev_name(pdata->dev), pdata); if (ret) - return dev_err_probe(dev, ret, "failed to request interrupt\n"); + return ret; } /* @@ -2116,8 +2116,8 @@ static int ti_sn65dsi86_probe(struct i2c_client *client) } static const struct i2c_device_id ti_sn65dsi86_id[] = { - { "ti,sn65dsi86" }, - {} + { .name = "ti,sn65dsi86" }, + { } }; MODULE_DEVICE_TABLE(i2c, ti_sn65dsi86_id); diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index f0a14f52aaac..629ff7095e87 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -437,7 +437,7 @@ static void tfp410_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id tfp410_i2c_ids[] = { - { "tfp410" }, + { .name = "tfp410" }, { } }; MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 1ff0bf7cba6a..3c570f9393b9 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -960,17 +960,19 @@ static void drmm_cg_unregister_region(struct drm_device *dev, void *arg) * drmm_cgroup_register_region - Register a region of a DRM device to cgroups * @dev: device for region * @region_name: Region name for registering - * @size: Size of region in bytes + * @init: Initialization parameters for the region. * * This decreases the ref-count of @dev by one. The device is destroyed if the * ref-count drops to zero. */ -struct dmem_cgroup_region *drmm_cgroup_register_region(struct drm_device *dev, const char *region_name, u64 size) +struct dmem_cgroup_region * +drmm_cgroup_register_region(struct drm_device *dev, const char *region_name, + const struct dmem_cgroup_init *init) { struct dmem_cgroup_region *region; int ret; - region = dmem_cgroup_register_region(size, "drm/%s/%s", dev->unique, region_name); + region = dmem_cgroup_register_region(init, "drm/%s/%s", dev->unique, region_name); if (IS_ERR_OR_NULL(region)) return region; diff --git a/drivers/gpu/drm/imagination/pvr_ccb.c b/drivers/gpu/drm/imagination/pvr_ccb.c index 4accf18e2341..b702d122d791 100644 --- a/drivers/gpu/drm/imagination/pvr_ccb.c +++ b/drivers/gpu/drm/imagination/pvr_ccb.c @@ -255,7 +255,7 @@ pvr_kccb_used_slot_count_locked(struct pvr_device *pvr_dev) * pvr_kccb_send_cmd_reserved_powered() - Send command to the KCCB, with the PM ref * held and a slot pre-reserved * @pvr_dev: Device pointer. - * @cmd: Command to sent. + * @cmd: Command to send. * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. */ void @@ -361,7 +361,7 @@ static int pvr_kccb_reserve_slot_sync(struct pvr_device *pvr_dev) /** * pvr_kccb_send_cmd_powered() - Send command to the KCCB, with a PM ref held * @pvr_dev: Device pointer. - * @cmd: Command to sent. + * @cmd: Command to send. * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. * * Returns: @@ -385,7 +385,7 @@ pvr_kccb_send_cmd_powered(struct pvr_device *pvr_dev, struct rogue_fwif_kccb_cmd /** * pvr_kccb_send_cmd() - Send command to the KCCB * @pvr_dev: Device pointer. - * @cmd: Command to sent. + * @cmd: Command to send. * @kccb_slot: Address to store the KCCB slot for this command. May be %NULL. * * Returns: diff --git a/drivers/gpu/drm/imagination/pvr_check.h b/drivers/gpu/drm/imagination/pvr_check.h new file mode 100644 index 000000000000..94764ce9bbd1 --- /dev/null +++ b/drivers/gpu/drm/imagination/pvr_check.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* Copyright (c) 2026 Imagination Technologies Ltd. */ + +#ifndef PVR_CHECK_H +#define PVR_CHECK_H + +#include +#include +#include + +#define OFFSET_CHECK(type, member, offset) \ + static_assert(offsetof(type, member) == (offset), \ + "offsetof(" #type ", " #member ") incorrect") + +#define SIZE_CHECK(type, size) \ + static_assert(sizeof(type) == (size), #type " is incorrect size") + +#define ALIGN_CHECK(type, align) \ + static_assert(__alignof__(type) <= (align), #type " has incorrect alignment") + +/* + * Where the last member of a struct is a flexible array member, using + * SIZE_CHECK() is pointless. If the structure is not already padded to + * alignment without the flexible array member, sizeof() will not match the + * offset of the flexible array member and the "correct" sizeof() value is + * completely meaningless. + * + * In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final + * field is a flexible array member and that it behaves as expected. + */ +#define FLEX_ARRAY_CHECK(type, member) \ + static_assert(flex_array_size((type *)NULL, member, 1) == \ + sizeof_field(type, member[0]), \ + #type "->" #member " is incorrect size") + +#endif /* PVR_CHECK_H */ diff --git a/drivers/gpu/drm/imagination/pvr_device_info.h b/drivers/gpu/drm/imagination/pvr_device_info.h index f61fb988b553..f6c2845530da 100644 --- a/drivers/gpu/drm/imagination/pvr_device_info.h +++ b/drivers/gpu/drm/imagination/pvr_device_info.h @@ -22,12 +22,12 @@ struct pvr_device_features { bool has_coreid_per_os; bool has_dynamic_dust_power; bool has_ecc_rams; - bool has_fb_cdc_v4; - bool has_fbc_max_default_descriptors; - bool has_fbc_max_large_descriptors; bool has_fbcdc; bool has_fbcdc_algorithm; bool has_fbcdc_architecture; + bool has_fbc_max_default_descriptors; + bool has_fbc_max_large_descriptors; + bool has_fb_cdc_v4; bool has_gpu_multicore_support; bool has_gpu_virtualisation; bool has_gs_rta_support; diff --git a/drivers/gpu/drm/imagination/pvr_gem.h b/drivers/gpu/drm/imagination/pvr_gem.h index c99f30cc6208..4286e047ae24 100644 --- a/drivers/gpu/drm/imagination/pvr_gem.h +++ b/drivers/gpu/drm/imagination/pvr_gem.h @@ -84,8 +84,10 @@ struct pvr_gem_object { /** * @base: The underlying &struct drm_gem_shmem_object. * - * Do not access this member directly, instead call - * shem_gem_from_pvr_gem(). + * .. note:: + * + * This member should not be accessed directly, but instead by + * calling shmem_gem_from_pvr_gem(). */ struct drm_gem_shmem_object base; @@ -97,12 +99,6 @@ struct pvr_gem_object { * changed after creation. * * Must be a combination of DRM_PVR_BO_* and/or PVR_BO_* flags. - * - * .. note:: - * - * This member is declared const to indicate that none of these - * options may change or be changed throughout the object's - * lifetime. */ u64 flags; diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 54e88b4208d7..09993e858df8 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -17,6 +17,7 @@ #include "pvr_rogue_fwif_client.h" #define MAX_DEADLINE_MS 30000 +#define SCHED_TIMEOUT_PERIOD (2 * MAX_DEADLINE_MS) #define CTX_COMPUTE_CCCB_SIZE_LOG2 15 #define CTX_FRAG_CCCB_SIZE_LOG2 15 @@ -1283,7 +1284,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx, .submit_wq = pvr_dev->sched_wq, .credit_limit = 64 * 1024, .hang_limit = 1, - .timeout = msecs_to_jiffies(500), + .timeout = msecs_to_jiffies(SCHED_TIMEOUT_PERIOD), .timeout_wq = pvr_dev->sched_wq, .name = "pvr-queue", .dev = pvr_dev->base.dev, diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h index e72f4064af18..5048866359b9 100644 --- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_check.h @@ -4,31 +4,7 @@ #ifndef PVR_ROGUE_FWIF_CHECK_H #define PVR_ROGUE_FWIF_CHECK_H -#include -#include -#include - -#define OFFSET_CHECK(type, member, offset) \ - static_assert(offsetof(type, member) == (offset), \ - "offsetof(" #type ", " #member ") incorrect") - -#define SIZE_CHECK(type, size) \ - static_assert(sizeof(type) == (size), #type " is incorrect size") - -/* - * Where the last member of a struct is a flexible array member, using - * SIZE_CHECK() is pointless. If the structure is not already padded to - * alignment without the flexible array member, sizeof() will not match the - * offset of the flexible array member and the "correct" sizeof() value is - * completely meaningless. - * - * In those instances, use FLEX_ARRAY_CHECK() instead to assert that the final - * field is a flexible array member and that it behaves as expected. - */ -#define FLEX_ARRAY_CHECK(type, member) \ - static_assert(flex_array_size((type *)NULL, member, 1) == \ - sizeof_field(type, member[0]), \ - #type "->" #member " is incorrect size") +#include "pvr_check.h" OFFSET_CHECK(struct rogue_fwif_file_info_buf, path, 0); OFFSET_CHECK(struct rogue_fwif_file_info_buf, info, 200); diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h index 54aa4474163e..467d1d2073a1 100644 --- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_client_check.h @@ -4,14 +4,7 @@ #ifndef PVR_ROGUE_FWIF_CLIENT_CHECK_H #define PVR_ROGUE_FWIF_CLIENT_CHECK_H -#include - -#define OFFSET_CHECK(type, member, offset) \ - static_assert(offsetof(type, member) == (offset), \ - "offsetof(" #type ", " #member ") incorrect") - -#define SIZE_CHECK(type, size) \ - static_assert(sizeof(type) == (size), #type " is incorrect size") +#include "pvr_check.h" OFFSET_CHECK(struct rogue_fwif_geom_regs, vdm_ctrl_stream_base, 0); OFFSET_CHECK(struct rogue_fwif_geom_regs, tpu_border_colour_table, 8); diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h index 597ed54bbd3a..59ef0a9aa395 100644 --- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h +++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared_check.h @@ -4,14 +4,7 @@ #ifndef PVR_ROGUE_FWIF_SHARED_CHECK_H #define PVR_ROGUE_FWIF_SHARED_CHECK_H -#include - -#define OFFSET_CHECK(type, member, offset) \ - static_assert(offsetof(type, member) == (offset), \ - "offsetof(" #type ", " #member ") incorrect") - -#define SIZE_CHECK(type, size) \ - static_assert(sizeof(type) == (size), #type " is incorrect size") +#include "pvr_check.h" OFFSET_CHECK(struct rogue_fwif_dma_addr, dev_addr, 0); OFFSET_CHECK(struct rogue_fwif_dma_addr, fw_addr, 8); diff --git a/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h b/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h index 684766006703..5c33b342dda5 100644 --- a/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h +++ b/drivers/gpu/drm/imagination/pvr_rogue_heap_config.h @@ -33,12 +33,12 @@ /* 0 MiB to 4 MiB, size of 4 MiB : RESERVED */ /* 0x00_0040_0000 - 0x7F_FFC0_0000 **/ -/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED **/ +/* 4 MiB to 512 GiB, size of 512 GiB less 4 MiB : RESERVED */ /* 0x80_0000_0000 ************************************************************/ /* 0x80_0000_0000 - 0x9F_FFFF_FFFF **/ -/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP **/ +/* 512 GiB to 640 GiB, size of 128 GiB : GENERAL_HEAP */ #define ROGUE_GENERAL_HEAP_BASE 0x8000000000ull #define ROGUE_GENERAL_HEAP_SIZE SZ_128G @@ -62,7 +62,7 @@ #define ROGUE_PDSCODEDATA_HEAP_SIZE SZ_4G /* 0xDB_0000_0000 - 0xDB_FFFF_FFFF */ -/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN **/ +/* 876 GiB to 880 GiB, size of 256 MiB (reserved 4GiB) : BRN */ /* * The BRN63142 quirk workaround requires Region Header memory to be at the top * of a 16GiB aligned range. This is so when masked with 0x03FFFFFFFF the @@ -82,7 +82,7 @@ /* 0xE1_0000_0000 - 0xE1_BFFF_FFFF */ /* 900 GiB to 903 GiB, size of 3 GiB : RESERVED */ -/* 0xE1_C000_000 - 0xE1_FFFF_FFFF */ +/* 0xE1_C000_0000 - 0xE1_FFFF_FFFF */ /* 903 GiB to 904 GiB, reserved 1 GiB, : FIRMWARE_HEAP */ #define ROGUE_FW_HEAP_BASE 0xE1C0000000ull @@ -90,7 +90,7 @@ /* 904 GiB to 912 GiB, size of 8 GiB : FREE */ /* 0xE4_0000_0000 - 0xE7_FFFF_FFFF */ -/* 912 GiB to 968 GiB, size of 16 GiB : TRANSFER_FRAG */ +/* 912 GiB to 928 GiB, size of 16 GiB : TRANSFER_FRAG */ #define ROGUE_TRANSFER_FRAG_HEAP_BASE 0xE400000000ull #define ROGUE_TRANSFER_FRAG_HEAP_SIZE SZ_16G diff --git a/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h b/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h index 824b4bf33ac1..aebf231ce90f 100644 --- a/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h +++ b/drivers/gpu/drm/imagination/pvr_rogue_mips_check.h @@ -4,55 +4,32 @@ #ifndef PVR_ROGUE_MIPS_CHECK_H #define PVR_ROGUE_MIPS_CHECK_H -#include +#include "pvr_check.h" -static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) == 0, - "offsetof(struct rogue_mips_tlb_entry, tlb_page_mask) incorrect"); -static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_hi) == 4, - "offsetof(struct rogue_mips_tlb_entry, tlb_hi) incorrect"); -static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo0) == 8, - "offsetof(struct rogue_mips_tlb_entry, tlb_lo0) incorrect"); -static_assert(offsetof(struct rogue_mips_tlb_entry, tlb_lo1) == 12, - "offsetof(struct rogue_mips_tlb_entry, tlb_lo1) incorrect"); -static_assert(sizeof(struct rogue_mips_tlb_entry) == 16, - "struct rogue_mips_tlb_entry is incorrect size"); +OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_page_mask, 0); +OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_hi, 4); +OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_lo0, 8); +OFFSET_CHECK(struct rogue_mips_tlb_entry, tlb_lo1, 12); +SIZE_CHECK(struct rogue_mips_tlb_entry, 16); -static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_in) == 0, - "offsetof(struct rogue_mips_remap_entry, remap_addr_in) incorrect"); -static_assert(offsetof(struct rogue_mips_remap_entry, remap_addr_out) == 4, - "offsetof(struct rogue_mips_remap_entry, remap_addr_out) incorrect"); -static_assert(offsetof(struct rogue_mips_remap_entry, remap_region_size) == 8, - "offsetof(struct rogue_mips_remap_entry, remap_region_size) incorrect"); -static_assert(sizeof(struct rogue_mips_remap_entry) == 12, - "struct rogue_mips_remap_entry is incorrect size"); +OFFSET_CHECK(struct rogue_mips_remap_entry, remap_addr_in, 0); +OFFSET_CHECK(struct rogue_mips_remap_entry, remap_addr_out, 4); +OFFSET_CHECK(struct rogue_mips_remap_entry, remap_region_size, 8); +SIZE_CHECK(struct rogue_mips_remap_entry, 12); -static_assert(offsetof(struct rogue_mips_state, error_state) == 0, - "offsetof(struct rogue_mips_state, error_state) incorrect"); -static_assert(offsetof(struct rogue_mips_state, error_epc) == 4, - "offsetof(struct rogue_mips_state, error_epc) incorrect"); -static_assert(offsetof(struct rogue_mips_state, status_register) == 8, - "offsetof(struct rogue_mips_state, status_register) incorrect"); -static_assert(offsetof(struct rogue_mips_state, cause_register) == 12, - "offsetof(struct rogue_mips_state, cause_register) incorrect"); -static_assert(offsetof(struct rogue_mips_state, bad_register) == 16, - "offsetof(struct rogue_mips_state, bad_register) incorrect"); -static_assert(offsetof(struct rogue_mips_state, epc) == 20, - "offsetof(struct rogue_mips_state, epc) incorrect"); -static_assert(offsetof(struct rogue_mips_state, sp) == 24, - "offsetof(struct rogue_mips_state, sp) incorrect"); -static_assert(offsetof(struct rogue_mips_state, debug) == 28, - "offsetof(struct rogue_mips_state, debug) incorrect"); -static_assert(offsetof(struct rogue_mips_state, depc) == 32, - "offsetof(struct rogue_mips_state, depc) incorrect"); -static_assert(offsetof(struct rogue_mips_state, bad_instr) == 36, - "offsetof(struct rogue_mips_state, bad_instr) incorrect"); -static_assert(offsetof(struct rogue_mips_state, unmapped_address) == 40, - "offsetof(struct rogue_mips_state, unmapped_address) incorrect"); -static_assert(offsetof(struct rogue_mips_state, tlb) == 44, - "offsetof(struct rogue_mips_state, tlb) incorrect"); -static_assert(offsetof(struct rogue_mips_state, remap) == 300, - "offsetof(struct rogue_mips_state, remap) incorrect"); -static_assert(sizeof(struct rogue_mips_state) == 684, - "struct rogue_mips_state is incorrect size"); +OFFSET_CHECK(struct rogue_mips_state, error_state, 0); +OFFSET_CHECK(struct rogue_mips_state, error_epc, 4); +OFFSET_CHECK(struct rogue_mips_state, status_register, 8); +OFFSET_CHECK(struct rogue_mips_state, cause_register, 12); +OFFSET_CHECK(struct rogue_mips_state, bad_register, 16); +OFFSET_CHECK(struct rogue_mips_state, epc, 20); +OFFSET_CHECK(struct rogue_mips_state, sp, 24); +OFFSET_CHECK(struct rogue_mips_state, debug, 28); +OFFSET_CHECK(struct rogue_mips_state, depc, 32); +OFFSET_CHECK(struct rogue_mips_state, bad_instr, 36); +OFFSET_CHECK(struct rogue_mips_state, unmapped_address, 40); +OFFSET_CHECK(struct rogue_mips_state, tlb, 44); +OFFSET_CHECK(struct rogue_mips_state, remap, 300); +SIZE_CHECK(struct rogue_mips_state, 684); #endif /* PVR_ROGUE_MIPS_CHECK_H */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/base.h b/drivers/gpu/drm/nouveau/dispnv50/base.h index 085bd3aeb40b..fbfd62d8fc79 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/base.h +++ b/drivers/gpu/drm/nouveau/dispnv50/base.h @@ -1,5 +1,7 @@ #ifndef __NV50_KMS_BASE_H__ #define __NV50_KMS_BASE_H__ +#include + #include "wndw.h" int base507c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **); @@ -24,4 +26,8 @@ extern const struct nv50_wndw_func base907c; int base917c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **); int nv50_base_new(struct nouveau_drm *, int head, struct nv50_wndw **); + +/* The only blend mode supported by base channels */ +#define BASE_BLEND_MODES BIT(DRM_MODE_BLEND_PIXEL_NONE) + #endif diff --git a/drivers/gpu/drm/nouveau/dispnv50/base507c.c b/drivers/gpu/drm/nouveau/dispnv50/base507c.c index a431f6c5f6fa..41b648ab3c5e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/base507c.c +++ b/drivers/gpu/drm/nouveau/dispnv50/base507c.c @@ -297,6 +297,7 @@ base507c = { .image_set = base507c_image_set, .image_clr = base507c_image_clr, .update = base507c_update, + .blend_modes = BASE_BLEND_MODES, }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/base827c.c b/drivers/gpu/drm/nouveau/dispnv50/base827c.c index 4545cc5f3a14..6c290324b4f3 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/base827c.c +++ b/drivers/gpu/drm/nouveau/dispnv50/base827c.c @@ -93,6 +93,7 @@ base827c = { .image_set = base827c_image_set, .image_clr = base507c_image_clr, .update = base507c_update, + .blend_modes = BASE_BLEND_MODES, }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/base907c.c b/drivers/gpu/drm/nouveau/dispnv50/base907c.c index 4a2d5a259e15..a94d8350224b 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/base907c.c +++ b/drivers/gpu/drm/nouveau/dispnv50/base907c.c @@ -205,6 +205,7 @@ base907c = { .image_set = base907c_image_set, .image_clr = base507c_image_clr, .update = base507c_update, + .blend_modes = BASE_BLEND_MODES, }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c index 1a889139cb05..5f2ff40388e7 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/curs507a.c +++ b/drivers/gpu/drm/nouveau/dispnv50/curs507a.c @@ -29,6 +29,7 @@ #include #include +#include #include bool @@ -164,6 +165,8 @@ curs507a_wndw = { .acquire = curs507a_acquire, .release = curs507a_release, .prepare = curs507a_prepare, + // TODO: Cursors also support premulti, but we haven't hooked it up anywhere yet. + .blend_modes = BIT(DRM_MODE_BLEND_COVERAGE), }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 364227f5456f..2c66e480b511 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -2768,6 +2768,7 @@ nv50_display_read_hw_state(struct nouveau_drm *drm) { struct drm_device *dev = drm->dev; struct drm_encoder *encoder; + struct drm_plane *plane; struct drm_modeset_acquire_ctx ctx; struct nv50_disp *disp = nv50_disp(dev); int ret; @@ -2781,6 +2782,9 @@ nv50_display_read_hw_state(struct nouveau_drm *drm) nv50_display_read_hw_or_state(dev, disp, nouveau_encoder(encoder)); } + drm_for_each_plane(plane, dev) + nv50_wndw_default_state(nv50_wndw(plane)); + DRM_MODESET_LOCK_ALL_END(dev, ctx, ret); } diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index 2635458d52ac..15a322422f4e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -848,6 +848,28 @@ static const u64 nv50_cursor_format_modifiers[] = { DRM_FORMAT_MOD_INVALID, }; +/* + * Setup defaults for the atomic wndw state + */ +void +nv50_wndw_default_state(struct nv50_wndw *wndw) +{ + struct nv50_wndw_atom *armw = nv50_wndw_atom(wndw->plane.state); + const unsigned int blend_modes = wndw->func->blend_modes; + + drm_modeset_lock_assert_held(&wndw->plane.mutex); + + /* Ensure the plane's atomic state didn't default to a pixel_blend_mode we don't support */ + if (blend_modes && (!(BIT(armw->state.pixel_blend_mode) & blend_modes))) { + if (blend_modes & BIT(DRM_MODE_BLEND_COVERAGE)) + armw->state.pixel_blend_mode = DRM_MODE_BLEND_COVERAGE; + else if (blend_modes & BIT(DRM_MODE_BLEND_PREMULTI)) + armw->state.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; + else if (blend_modes & BIT(DRM_MODE_BLEND_PIXEL_NONE)) + armw->state.pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE; + } +} + int nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev, enum drm_plane_type type, const char *name, int index, @@ -908,13 +930,6 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev, ret = drm_plane_create_alpha_property(&wndw->plane); if (ret) return ret; - - ret = drm_plane_create_blend_mode_property(&wndw->plane, - BIT(DRM_MODE_BLEND_PIXEL_NONE) | - BIT(DRM_MODE_BLEND_PREMULTI) | - BIT(DRM_MODE_BLEND_COVERAGE)); - if (ret) - return ret; } else { ret = drm_plane_create_zpos_immutable_property(&wndw->plane, nv50_wndw_zpos_default(&wndw->plane)); @@ -922,6 +937,17 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev, return ret; } + /* + * DRM requires that we have a blend mode property for any type of plane that exposes color + * formats with an alpha channel. So do this, even if we don't actually have control for the + * blend property hooked up with blend_set. + */ + if (func->blend_modes) { + ret = drm_plane_create_blend_mode_property(&wndw->plane, func->blend_modes); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.h b/drivers/gpu/drm/nouveau/dispnv50/wndw.h index 90d100514bef..7bd8bcc199db 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.h +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.h @@ -1,6 +1,8 @@ #ifndef __NV50_KMS_WNDW_H__ #define __NV50_KMS_WNDW_H__ #define nv50_wndw(p) container_of((p), struct nv50_wndw, plane) +#include + #include "disp.h" #include "atom.h" #include "lut.h" @@ -44,6 +46,7 @@ void nv50_wndw_flush_clr(struct nv50_wndw *, u32 *interlock, bool flush, struct nv50_wndw_atom *); void nv50_wndw_ntfy_enable(struct nv50_wndw *, struct nv50_wndw_atom *); int nv50_wndw_wait_armed(struct nv50_wndw *, struct nv50_wndw_atom *); +void nv50_wndw_default_state(struct nv50_wndw *wndw); struct nv50_wndw_func { int (*acquire)(struct nv50_wndw *, struct nv50_wndw_atom *asyw, @@ -74,6 +77,7 @@ struct nv50_wndw_func { int (*image_clr)(struct nv50_wndw *); int (*scale_set)(struct nv50_wndw *, struct nv50_wndw_atom *); int (*blend_set)(struct nv50_wndw *, struct nv50_wndw_atom *); + unsigned int blend_modes; int (*update)(struct nv50_wndw *, u32 *interlock); }; @@ -139,4 +143,11 @@ int wndwca7e_new(struct nouveau_drm *, enum drm_plane_type, int, s32, int nv50_wndw_new(struct nouveau_drm *, enum drm_plane_type, int index, struct nv50_wndw **); + +/* A set of blend modes supported by all wndws */ +#define WNDW_BLEND_MODES \ + ( BIT(DRM_MODE_BLEND_COVERAGE) \ + | BIT(DRM_MODE_BLEND_PREMULTI) \ + | BIT(DRM_MODE_BLEND_PIXEL_NONE)) + #endif diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c index 554c4f91f8be..7ab4d532118e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c @@ -343,6 +343,7 @@ wndwc37e = { .image_set = wndwc37e_image_set, .image_clr = wndwc37e_image_clr, .blend_set = wndwc37e_blend_set, + .blend_modes = WNDW_BLEND_MODES, .update = wndwc37e_update, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c index d1ca51aae58c..e220f43b5bca 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c @@ -231,6 +231,7 @@ wndwc57e = { .image_set = wndwc57e_image_set, .image_clr = wndwc37e_image_clr, .blend_set = wndwc37e_blend_set, + .blend_modes = WNDW_BLEND_MODES, .update = wndwc37e_update, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c index 52af293c98f4..580cf7abd819 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c @@ -95,6 +95,7 @@ wndwc67e = { .image_set = wndwc67e_image_set, .image_clr = wndwc37e_image_clr, .blend_set = wndwc37e_blend_set, + .blend_modes = WNDW_BLEND_MODES, .update = wndwc37e_update, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c index 2cec8cfbd546..d65ec438b434 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c @@ -231,6 +231,7 @@ wndwca7e = { .image_set = wndwca7e_image_set, .image_clr = wndwca7e_image_clr, .blend_set = wndwc37e_blend_set, + .blend_modes = WNDW_BLEND_MODES, .update = wndwc37e_update, }; diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 291203121f0c..4542d5f4ded8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -408,6 +408,9 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) case NOUVEAU_FIFO_ENGINE_CE: engine = NV_DEVICE_HOST_RUNLIST_ENGINES_CE; break; + case NOUVEAU_FIFO_ENGINE_NVDEC: + engine = NV_DEVICE_HOST_RUNLIST_ENGINES_NVDEC; + break; default: return nouveau_abi16_put(abi16, -ENOSYS); } @@ -485,10 +488,13 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) goto done; break; case NV_DEVICE_INFO_V0_TURING: - ret = nvif_object_ctor(&chan->chan->user, "abi16CeWar", 0, TURING_DMA_COPY_A, - NULL, 0, &chan->ce); - if (ret) - goto done; + if (engine != NV_DEVICE_HOST_RUNLIST_ENGINES_NVDEC) { + ret = nvif_object_ctor(&chan->chan->user, "abi16CeWar", + 0, TURING_DMA_COPY_A, NULL, 0, + &chan->ce); + if (ret) + goto done; + } break; default: break; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 1c2523e2f92e..5fc75dc750ed 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -10,7 +10,7 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 4 -#define DRIVER_PATCHLEVEL 2 +#define DRIVER_PATCHLEVEL 3 /* * 1.1.1: @@ -39,6 +39,8 @@ * - add variable page sizes and compression for Turing+ * 1.4.2: * - tell userspace LPTE/SPTE races are fixed. + * 1.4.3: + * - VDEC contexts can be created. */ #include diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index ad01f922aa86..cfc0868e2dd3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -23,6 +23,7 @@ * USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include #include #include @@ -188,6 +189,13 @@ nouveau_ttm_init_vram(struct nouveau_drm *drm) man->func = &nouveau_vram_manager; + man->cg = drmm_cgroup_register_region(drm->dev, "vram", + &(struct dmem_cgroup_init){ + .size = drm->gem.vram_available + }); + if (IS_ERR(man->cg)) + return PTR_ERR(man->cg); + ttm_resource_manager_init(man, &drm->ttm.bdev, drm->gem.vram_available >> PAGE_SHIFT); ttm_set_driver_manager(&drm->ttm.bdev, TTM_PL_VRAM, man); diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 7aaee94f86e4..6da720e8732d 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -456,15 +456,10 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) timer_delete(&dsi->te_timer); #endif - /* make a copy and unlock, so that isrs can unregister - * themselves */ - memcpy(&dsi->isr_tables_copy, &dsi->isr_tables, - sizeof(dsi->isr_tables)); + dsi_handle_isrs(&dsi->isr_tables, irqstatus, vcstatus, ciostatus); spin_unlock(&dsi->irq_lock); - dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus); - dsi_handle_irq_errors(dsi, irqstatus, vcstatus, ciostatus); dsi_collect_irq_stats(dsi, irqstatus, vcstatus, ciostatus); diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.h b/drivers/gpu/drm/omapdrm/dss/dsi.h index 601707c0ecc4..2b25247ea893 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.h +++ b/drivers/gpu/drm/omapdrm/dss/dsi.h @@ -379,8 +379,6 @@ struct dsi_data { spinlock_t irq_lock; struct dsi_isr_tables isr_tables; - /* space for a copy used by the interrupt handler */ - struct dsi_isr_tables isr_tables_copy; int update_vc; #ifdef DSI_PERF_MEASURE diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index df7286679446..2b455369ed70 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -144,6 +144,16 @@ struct panel_delay { */ unsigned int disable; + /** + * @pre_unprepare: Time for the end of video data to power off. + * + * The time (in milliseconds) that it needs to have passed between + * the end of valid video data from source and start powering off. + * + * This is T10-min on eDP timing diagrams. It is not common to set. + */ + unsigned int pre_unprepare; + /** * @unprepare: Time to power down completely. * @@ -413,8 +423,12 @@ static int panel_edp_suspend(struct device *dev) static int panel_edp_unprepare(struct drm_panel *panel) { + struct panel_edp *p = to_panel_edp(panel); int ret; + if (p->desc->delay.pre_unprepare) + msleep(p->desc->delay.pre_unprepare); + ret = pm_runtime_put_sync_suspend(panel->dev); if (ret < 0) return ret; @@ -832,6 +846,13 @@ static int generic_edp_panel_probe(struct device *dev, struct panel_edp *panel) return 0; } +static void panel_edp_put_adapter(void *_adap) +{ + struct i2c_adapter *adap = _adap; + + i2c_put_adapter(adap); +} + static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, struct drm_dp_aux *aux) { @@ -874,11 +895,16 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0); if (ddc) { - panel->ddc = of_find_i2c_adapter_by_node(ddc); + panel->ddc = of_get_i2c_adapter_by_node(ddc); of_node_put(ddc); if (!panel->ddc) return -EPROBE_DEFER; + + err = devm_add_action_or_reset(dev, panel_edp_put_adapter, + panel->ddc); + if (err) + return err; } else if (aux) { panel->ddc = &aux->ddc; } @@ -890,7 +916,7 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, err = drm_panel_of_backlight(&panel->base); if (err) - goto err_finished_ddc_init; + return err; /* * We use runtime PM for prepare / unprepare since those power the panel @@ -937,9 +963,6 @@ static int panel_edp_probe(struct device *dev, const struct panel_desc *desc, err_finished_pm_runtime: pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); -err_finished_ddc_init: - if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc)) - put_device(&panel->ddc->dev); return err; } @@ -983,8 +1006,6 @@ static void panel_edp_remove(struct device *dev) pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); - if (panel->ddc && (!panel->aux || panel->ddc != &panel->aux->ddc)) - put_device(&panel->ddc->dev); drm_edid_free(panel->drm_edid); panel->drm_edid = NULL; @@ -1803,6 +1824,13 @@ static const struct panel_delay delay_80_500_e80_p2e200 = { .prepare_to_enable = 200, }; +static const struct panel_delay delay_200_500_e80_pu100 = { + .hpd_absent = 200, + .unprepare = 500, + .enable = 80, + .pre_unprepare = 100, +}; + static const struct panel_delay delay_100_500_e200 = { .hpd_absent = 100, .unprepare = 500, @@ -2029,6 +2057,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a84, &delay_200_500_e50, "NV133WUM-T01"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ac5, &delay_200_500_e50, "NV116WHM-N4C"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ae8, &delay_200_500_e50_p2e80, "NV140WUM-N41"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0af2, &delay_200_500_e50_p2e80, "NE140QDM-NX2"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b09, &delay_200_500_e50_po2e200, "NV140FHM-NZ"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b1e, &delay_200_500_e80, "NE140QDM-N6A"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b34, &delay_200_500_e80_d50, "NV122WUM-N41"), @@ -2076,6 +2105,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'M', 'N', 0x1161, &delay_200_500_e80, "N116BCP-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1163, &delay_200_500_e80_d50, "N116BCJ-EAK"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1169, &delay_200_500_e80_d50, "N116BCN-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x116c, &delay_200_500_e80_d50, "N116BCP-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x116d, &delay_200_500_e80_d50, "N116BCP-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x117a, &delay_200_500_e80_d50, "N116BCL-EAK"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"), @@ -2165,6 +2195,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x158f, &delay_200_500_p2e100, "LQ134Z1"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x1593, &delay_200_500_p2e100, "LQ134N1"), + EDP_PANEL_ENTRY('S', 'H', 'P', 0x15a7, &delay_200_500_e200, "LQ120P1JX51"), EDP_PANEL_ENTRY('S', 'T', 'A', 0x0004, &delay_200_500_e200, "116KHD024006"), EDP_PANEL_ENTRY('S', 'T', 'A', 0x0009, &delay_200_500_e250, "116QHD024002"), @@ -2172,7 +2203,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('T', 'M', 'A', 0x0811, &delay_200_500_e80_d50, "TM140VDXP01-04"), EDP_PANEL_ENTRY('T', 'M', 'A', 0x2094, &delay_200_500_e50_d100, "TL140VDMS03-01"), - EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e50_d100, "TM156VDXP25"), + EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e80_pu100, "TM156VDXP25"), { /* sentinal */ } }; diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c index e6c483851f1f..5610a225f77b 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9805.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9805.c @@ -25,22 +25,8 @@ #define ILI9805_SETEXTC_PARAMETER2 (0x98) #define ILI9805_SETEXTC_PARAMETER3 (0x05) -#define ILI9805_INSTR(_delay, ...) { \ - .delay = (_delay), \ - .len = sizeof((u8[]) {__VA_ARGS__}), \ - .data = (u8[]){__VA_ARGS__} \ - } - -struct ili9805_instr { - size_t len; - const u8 *data; - u32 delay; -}; - struct ili9805_desc { - const char *name; - const struct ili9805_instr *init; - const size_t init_length; + void (*init)(struct mipi_dsi_multi_context *ctx); const struct drm_display_mode *mode; u32 width_mm; u32 height_mm; @@ -56,65 +42,80 @@ struct ili9805 { struct gpio_desc *reset_gpio; }; -static const struct ili9805_instr gpm1780a0_init[] = { - ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1, - ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3), - ILI9805_INSTR(100, 0xFD, 0x0F, 0x10, 0x44, 0x00), - ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00, - 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00), - ILI9805_INSTR(0, 0xB8, 0x62), - ILI9805_INSTR(0, 0xF1, 0x00), - ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40), - ILI9805_INSTR(0, 0xF3, 0x60, 0x83, 0x04), - ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01), - ILI9805_INSTR(0, 0xEB, 0x08, 0x0F), - ILI9805_INSTR(0, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04, - 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a), - ILI9805_INSTR(0, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, 0x04, - 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a), - ILI9805_INSTR(10, 0xc1, 0x13, 0x39, 0x19, 0x06), - ILI9805_INSTR(10, 0xc7, 0xe5), - ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14), - ILI9805_INSTR(10, 0xB4, 0x02), - ILI9805_INSTR(0, 0xBB, 0x14, 0x55), - ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x08), - ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77), - ILI9805_INSTR(0, 0x20), - ILI9805_INSTR(0, 0xB0, 0x01), - ILI9805_INSTR(0, 0xB6, 0x31, 0x00, 0xef), - ILI9805_INSTR(0, 0xDF, 0x23), - ILI9805_INSTR(0, 0xB9, 0x02, 0x00), -}; +static void gpm1780a0_init(struct mipi_dsi_multi_context *ctx) +{ + mipi_dsi_dcs_write_seq_multi(ctx, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, + ILI9805_SETEXTC_PARAMETER1, ILI9805_SETEXTC_PARAMETER2, + ILI9805_SETEXTC_PARAMETER3); + mipi_dsi_msleep(ctx, 100); + mipi_dsi_dcs_write_seq_multi(ctx, 0xfd, 0x0f, 0x10, 0x44, 0x00); + mipi_dsi_msleep(ctx, 100); + mipi_dsi_dcs_write_seq_multi(ctx, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb8, 0x62); + mipi_dsi_dcs_write_seq_multi(ctx, 0xf1, 0x00); + mipi_dsi_dcs_write_seq_multi(ctx, 0xf2, 0x00, 0x58, 0x40); + mipi_dsi_dcs_write_seq_multi(ctx, 0xf3, 0x60, 0x83, 0x04); + mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x04, 0x0f, 0x01); + mipi_dsi_dcs_write_seq_multi(ctx, 0xeb, 0x08, 0x0f); + mipi_dsi_dcs_write_seq_multi(ctx, 0xe0, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, + 0x04, 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a); + mipi_dsi_dcs_write_seq_multi(ctx, 0xe1, 0x00, 0x08, 0x0d, 0x0e, 0x0e, 0x0d, 0x0a, 0x08, + 0x04, 0x08, 0x0d, 0x0f, 0x0b, 0x1c, 0x14, 0x0a); + mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x13, 0x39, 0x19, 0x06); + mipi_dsi_msleep(ctx, 10); + mipi_dsi_dcs_write_seq_multi(ctx, 0xc7, 0xe5); + mipi_dsi_msleep(ctx, 10); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb1, 0x00, 0x12, 0x14); + mipi_dsi_msleep(ctx, 10); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb4, 0x02); + mipi_dsi_msleep(ctx, 10); + mipi_dsi_dcs_write_seq_multi(ctx, 0xbb, 0x14, 0x55); + mipi_dsi_dcs_write_seq_multi(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x08); + mipi_dsi_dcs_set_pixel_format_multi(ctx, 0x77); + mipi_dsi_dcs_write_seq_multi(ctx, 0x20); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb0, 0x01); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb6, 0x31, 0x00, 0xef); + mipi_dsi_dcs_write_seq_multi(ctx, 0xdf, 0x23); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb9, 0x02, 0x00); +} -static const struct ili9805_instr tm041xdhg01_init[] = { - ILI9805_INSTR(100, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, ILI9805_SETEXTC_PARAMETER1, - ILI9805_SETEXTC_PARAMETER2, ILI9805_SETEXTC_PARAMETER3), - ILI9805_INSTR(100, 0xFD, 0x0F, 0x13, 0x44, 0x00), - ILI9805_INSTR(0, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01, - 0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01), - ILI9805_INSTR(0, 0xB8, 0x74), - ILI9805_INSTR(0, 0xF1, 0x00), - ILI9805_INSTR(0, 0xF2, 0x00, 0x58, 0x40), - ILI9805_INSTR(0, 0xFC, 0x04, 0x0F, 0x01), - ILI9805_INSTR(0, 0xEB, 0x08, 0x0F), - ILI9805_INSTR(0, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04, - 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08), - ILI9805_INSTR(0, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, 0x04, - 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08), - ILI9805_INSTR(10, 0xc1, 0x15, 0x03, 0x03, 0x31), - ILI9805_INSTR(10, 0xB1, 0x00, 0x12, 0x14), - ILI9805_INSTR(10, 0xB4, 0x02), - ILI9805_INSTR(0, 0xBB, 0x14, 0x55), - ILI9805_INSTR(0, MIPI_DCS_SET_ADDRESS_MODE, 0x0a), - ILI9805_INSTR(0, MIPI_DCS_SET_PIXEL_FORMAT, 0x77), - ILI9805_INSTR(0, 0x20), - ILI9805_INSTR(0, 0xB0, 0x00), - ILI9805_INSTR(0, 0xB6, 0x01), - ILI9805_INSTR(0, 0xc2, 0x11), - ILI9805_INSTR(0, 0x51, 0xFF), - ILI9805_INSTR(0, 0x53, 0x24), - ILI9805_INSTR(0, 0x55, 0x00), -}; +static void tm041xdhg01_init(struct mipi_dsi_multi_context *ctx) +{ + mipi_dsi_dcs_write_seq_multi(ctx, ILI9805_EXTCMD_CMD_SET_ENABLE_REG, + ILI9805_SETEXTC_PARAMETER1, ILI9805_SETEXTC_PARAMETER2, + ILI9805_SETEXTC_PARAMETER3); + mipi_dsi_msleep(ctx, 100); + mipi_dsi_dcs_write_seq_multi(ctx, 0xfd, 0x0f, 0x13, 0x44, 0x00); + mipi_dsi_msleep(ctx, 100); + mipi_dsi_dcs_write_seq_multi(ctx, 0xf8, 0x18, 0x02, 0x02, 0x18, 0x02, 0x02, 0x30, 0x01, + 0x01, 0x30, 0x01, 0x01, 0x30, 0x01, 0x01); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb8, 0x74); + mipi_dsi_dcs_write_seq_multi(ctx, 0xf1, 0x00); + mipi_dsi_dcs_write_seq_multi(ctx, 0xf2, 0x00, 0x58, 0x40); + mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x04, 0x0f, 0x01); + mipi_dsi_dcs_write_seq_multi(ctx, 0xeb, 0x08, 0x0f); + mipi_dsi_dcs_write_seq_multi(ctx, 0xe0, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, + 0x04, 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08); + mipi_dsi_dcs_write_seq_multi(ctx, 0xe1, 0x01, 0x0d, 0x15, 0x0e, 0x0f, 0x0f, 0x0b, 0x08, + 0x04, 0x07, 0x0a, 0x0d, 0x0c, 0x15, 0x0f, 0x08); + mipi_dsi_dcs_write_seq_multi(ctx, 0xc1, 0x15, 0x03, 0x03, 0x31); + mipi_dsi_msleep(ctx, 10); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb1, 0x00, 0x12, 0x14); + mipi_dsi_msleep(ctx, 10); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb4, 0x02); + mipi_dsi_msleep(ctx, 10); + mipi_dsi_dcs_write_seq_multi(ctx, 0xbb, 0x14, 0x55); + mipi_dsi_dcs_write_seq_multi(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x0a); + mipi_dsi_dcs_set_pixel_format_multi(ctx, 0x77); + mipi_dsi_dcs_write_seq_multi(ctx, 0x20); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb0, 0x00); + mipi_dsi_dcs_write_seq_multi(ctx, 0xb6, 0x01); + mipi_dsi_dcs_write_seq_multi(ctx, 0xc2, 0x11); + mipi_dsi_dcs_write_seq_multi(ctx, 0x51, 0xff); + mipi_dsi_dcs_write_seq_multi(ctx, 0x53, 0x24); + mipi_dsi_dcs_write_seq_multi(ctx, 0x55, 0x00); +} static inline struct ili9805 *panel_to_ili9805(struct drm_panel *panel) { @@ -148,47 +149,24 @@ static int ili9805_power_on(struct ili9805 *ctx) return 0; } -static int ili9805_power_off(struct ili9805 *ctx) +static void ili9805_power_off(struct ili9805 *ctx) { gpiod_set_value(ctx->reset_gpio, 0); regulator_disable(ctx->dvdd); regulator_disable(ctx->avdd); - - return 0; } static int ili9805_activate(struct ili9805 *ctx) { - struct mipi_dsi_device *dsi = ctx->dsi; - struct device *dev = &dsi->dev; - int i, ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - for (i = 0; i < ctx->desc->init_length; i++) { - const struct ili9805_instr *instr = &ctx->desc->init[i]; + ctx->desc->init(&dsi_ctx); - ret = mipi_dsi_dcs_write_buffer(ctx->dsi, instr->data, instr->len); - if (ret < 0) - return ret; + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); - if (instr->delay > 0) - msleep(instr->delay); - } - - ret = mipi_dsi_dcs_exit_sleep_mode(ctx->dsi); - if (ret) { - dev_err(dev, "Failed to exit sleep mode (%d)\n", ret); - return ret; - } - - usleep_range(5000, 6000); - - ret = mipi_dsi_dcs_set_display_on(ctx->dsi); - if (ret) { - dev_err(dev, "Failed to set display ON (%d)\n", ret); - return ret; - } - - return 0; + return dsi_ctx.accum_err; } static int ili9805_prepare(struct drm_panel *panel) @@ -209,27 +187,13 @@ static int ili9805_prepare(struct drm_panel *panel) return 0; } -static int ili9805_deactivate(struct ili9805 *ctx) +static void ili9805_deactivate(struct ili9805 *ctx) { - struct mipi_dsi_device *dsi = ctx->dsi; - struct device *dev = &dsi->dev; - int ret; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - ret = mipi_dsi_dcs_set_display_off(ctx->dsi); - if (ret < 0) { - dev_err(dev, "Failed to set display OFF (%d)\n", ret); - return ret; - } - - usleep_range(5000, 10000); - - ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi); - if (ret < 0) { - dev_err(dev, "Failed to enter sleep mode (%d)\n", ret); - return ret; - } - - return 0; + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 5000, 10000); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); } static int ili9805_unprepare(struct drm_panel *panel) @@ -271,7 +235,7 @@ static const struct drm_display_mode tm041xdhg01_timing = { }; static int ili9805_get_modes(struct drm_panel *panel, - struct drm_connector *connector) + struct drm_connector *connector) { struct ili9805 *ctx = panel_to_ili9805(panel); struct drm_display_mode *mode; @@ -368,7 +332,6 @@ static void ili9805_dsi_remove(struct mipi_dsi_device *dsi) static const struct ili9805_desc gpm1780a0_desc = { .init = gpm1780a0_init, - .init_length = ARRAY_SIZE(gpm1780a0_init), .mode = &gpm1780a0_timing, .width_mm = 65, .height_mm = 65, @@ -376,7 +339,6 @@ static const struct ili9805_desc gpm1780a0_desc = { static const struct ili9805_desc tm041xdhg01_desc = { .init = tm041xdhg01_init, - .init_length = ARRAY_SIZE(tm041xdhg01_init), .mode = &tm041xdhg01_timing, .width_mm = 42, .height_mm = 96, diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c index 67ef9e0fea03..5202c1ffc6e1 100644 --- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c +++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c @@ -2931,6 +2931,248 @@ static const struct jadard_panel_desc waveshare_10_1_inch_b_desc = { MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS, }; +static int dcltek_dt300250_init_cmds(struct jadard *jadard) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi }; + + jd9365da_switch_page(&dsi_ctx, 0x00); + jadard_enable_standard_cmds(&dsi_ctx); + + jd9365da_switch_page(&dsi_ctx, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x2B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x2B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xCF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0xCF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xFE); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x5C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0xFF); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x7F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xC8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x66); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x8D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5A, 0x29); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x1A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x7F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x65); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x53); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x3E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x33); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x3B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x3C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x3F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x5D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x4D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x3D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x37); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x15); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x65); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x53); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x3E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x33); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x3B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x3C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7A, 0x3F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7B, 0x5D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7C, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7D, 0x4D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7E, 0x3D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7F, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x28); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x15); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00); + + jd9365da_switch_page(&dsi_ctx, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x41); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x4B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x5C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x5A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0A, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0B, 0x47); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0C, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0D, 0x4F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0E, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0F, 0x45); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x4D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x51); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1A, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1B, 0x5B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1C, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1D, 0x48); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1E, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1F, 0x59); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x4E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x44); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x4C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2A, 0x50); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2B, 0x5F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2C, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2D, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2E, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2F, 0x0C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3A, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3B, 0x1B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3C, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3D, 0x0A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3E, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3F, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x0D); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x0F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4A, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4B, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4C, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4D, 0x1A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4E, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4F, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x1C); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x0B); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5B, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5C, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5D, 0x70); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5E, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5F, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x70); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x56); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x4F); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0xF7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6A, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6B, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6C, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6D, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6E, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6F, 0x88); + + jd9365da_switch_page(&dsi_ctx, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0E); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xB3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x4A); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0F); + + jd9365da_switch_page(&dsi_ctx, 0x05); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x34); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x76); + + jd9365da_switch_page(&dsi_ctx, 0x00); + + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 20); + + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + + return dsi_ctx.accum_err; +} + +static const struct jadard_panel_desc dcltek_dt300250_desc = { + .mode_4ln = &(const struct drm_display_mode) { + .clock = (600 + 48 + 32 + 80) * (1600 + 18 + 6 + 23) * 60 / 1000, + + .hdisplay = 600, + .hsync_start = 600 + 48, + .hsync_end = 600 + 48 + 32, + .htotal = 600 + 48 + 32 + 80, + + .vdisplay = 1600, + .vsync_start = 1600 + 18, + .vsync_end = 1600 + 18 + 6, + .vtotal = 1600 + 18 + 6 + 23, + + .width_mm = 83, + .height_mm = 221, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .init = dcltek_dt300250_init_cmds, +}; + static int jadard_dsi_probe(struct mipi_dsi_device *dsi) { struct device *dev = &dsi->dev; @@ -2950,7 +3192,8 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi) else dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | - MIPI_DSI_MODE_NO_EOT_PACKET; + MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_MODE_LPM; dsi->format = desc->format; dsi->lanes = desc->lanes; @@ -3036,6 +3279,10 @@ static const struct of_device_id jadard_of_match[] = { .compatible = "chongzhou,cz101b4001", .data = &cz101b4001_desc }, + { + .compatible = "dcltek,dt300250", + .data = &dcltek_dt300250_desc + }, { .compatible = "kingdisplay,kd101ne3-40ti", .data = &kingdisplay_kd101ne3_40ti_desc diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36536.c b/drivers/gpu/drm/panel/panel-novatek-nt36536.c index 2a82b54880c3..8bd125650168 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36536.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36536.c @@ -429,11 +429,9 @@ static int novatek_probe(struct mipi_dsi_device *dsi) ctx->dsi[i]->mode_flags = desc->mode_flags; ctx->dsi[i]->dsc = &ctx->dsc; ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]); - if (ret < 0) { - drm_panel_remove(&ctx->panel); + if (ret < 0) return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); - } } if (desc->has_dcs_backlight) { diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 292212119fda..c95964ba25a9 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -912,6 +912,36 @@ static const struct panel_desc ampire_am_800480l1tmqw_t00h = { .connector_type = DRM_MODE_CONNECTOR_DPI, }; +static const struct display_timing ampire_am_800480n3tzqw_00h_timing = { + .pixelclock = {23000000, 25000000, 27000000}, + .hactive = { 800, 800, 800 }, + .hfront_porch = { 4, 8, 48 }, + .hback_porch = { 4, 8, 48 }, + .hsync_len = { 2, 8, 8 }, + .vactive = { 480, 480, 480 }, + .vfront_porch = { 4, 8, 12 }, + .vback_porch = { 4, 8, 12 }, + .vsync_len = { 2, 4, 8 }, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_POSEDGE | + DISPLAY_FLAGS_SYNC_POSEDGE, +}; + +static const struct panel_desc ampire_am_800480n3tzqw_00h = { + .timings = &ire_am_800480n3tzqw_00h_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 108, + .height = 65, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE | + DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct panel_desc ampire_am800480r3tmqwa1h = { .modes = &ire_am800480r3tmqwa1h_mode, .num_modes = 1, @@ -5648,6 +5678,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "ampire,am-800480l1tmqw-t00h", .data = &ire_am_800480l1tmqw_t00h, + }, { + .compatible = "ampire,am-800480n3tzqw-00h", + .data = &ire_am_800480n3tzqw_00h, }, { .compatible = "ampire,am800480r3tmqwa1h", .data = &ire_am800480r3tmqwa1h, diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c index fcbb0b7d50e4..70f7282471e6 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -47,6 +47,7 @@ #define ST7703_CMD_SETCABC 0xC8 #define ST7703_CMD_SETPANEL 0xCC #define ST7703_CMD_SETGAMMA 0xE0 +#define ST7703_CMD_UNKNOWN_E1 0xE1 #define ST7703_CMD_SETEQ 0xE3 #define ST7703_CMD_SETGIP1 0xE9 #define ST7703_CMD_SETGIP2 0xEA @@ -673,6 +674,100 @@ static const struct st7703_panel_desc gameforcechi_desc = { .init_sequence = gameforcechi_init_sequence, }; +static void ayaneo_pocket_ds_lower_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) +{ + /* Init sequence extracted from the AYANEO Pocket DS BSP kernel. */ + + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEXTC, 0xf1, 0x12, + 0x87); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETDISP, 0x00, 0x01, + 0x78); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETRGBIF, 0x10, 0x10, + 0x28, 0x28, 0x03, 0xff, 0x00, 0x00, 0x00, + 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCYC, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETBGP, 0x0a, 0x0a); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVCOM, 0x90, 0x90); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER_EXT, 0x76, + 0x22, 0xf0, 0x13); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETMIPI, 0x33, 0x81, + 0x05, 0xf9, 0x0e, 0x0e, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, + 0x00, 0x91, 0x0a, 0x00, 0x00, 0x01, 0x4f, + 0x01, 0x00, 0x00, 0x37); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETVDC, 0x47); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_BF, 0x02, 0x10, + 0x00, 0x80, 0x04); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETSCR, 0x73, 0x73, + 0x50, 0x50, 0x00, 0x00, 0x12, 0x73, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPOWER, 0x55, 0x00, + 0x32, 0x32, 0x77, 0xf1, 0xf7, 0xf7, 0xcc, + 0xcc, 0x7f, 0x7f, 0x11, 0x11, 0x00, 0x00, + 0x32); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETIO, 0xb8, 0x00, + 0x0a, 0x10, 0x01, 0x09, 0x00, 0x00, 0xed, + 0xc5, 0x00, 0xa5); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETCABC, 0x10, 0x40, + 0x1e, 0x03); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETPANEL, 0x0b); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGAMMA, 0x00, 0x00, + 0x00, 0x25, 0x31, 0x3f, 0x26, 0x29, 0x04, + 0x08, 0x0d, 0x0e, 0x12, 0x0f, 0x11, 0x12, + 0x18, 0x00, 0x00, 0x00, 0x25, 0x31, 0x3f, + 0x26, 0x29, 0x04, 0x08, 0x0d, 0x0e, 0x12, + 0x0f, 0x11, 0x12, 0x18); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_E1, 0x11, 0x11, + 0x91, 0x00, 0x00, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETEQ, 0x07, 0x07, + 0x0b, 0x0b, 0x0b, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0xff, 0x84, 0xc0, 0x10); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP1, 0xc8, 0x10, + 0x04, 0x04, 0x02, 0x80, 0x81, 0x12, 0x31, + 0x23, 0x2f, 0x80, 0x80, 0x81, 0x27, 0x20, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x44, 0x00, + 0x88, 0xff, 0x00, 0x22, 0x44, 0x66, 0x88, + 0x88, 0x88, 0x55, 0x11, 0x88, 0xff, 0x11, + 0x33, 0x55, 0x77, 0x88, 0x88, 0x88, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_SETGIP2, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, 0x55, 0xff, 0x88, + 0x77, 0x55, 0x33, 0x11, 0x88, 0x88, 0x88, + 0x00, 0x44, 0xff, 0x88, 0x66, 0x44, 0x22, + 0x00, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00, + 0x00, 0xec, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x80, 0x81, 0x00, + 0x00, 0x00, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, ST7703_CMD_UNKNOWN_EF, 0xff, 0xff, + 0x01); +} + +static const struct drm_display_mode ayaneo_pocket_ds_lower_mode = { + .hdisplay = 768, + .hsync_start = 768 + 110, + .hsync_end = 768 + 110 + 45, + .htotal = 768 + 110 + 45 + 110, + .vdisplay = 1024, + .vsync_start = 1024 + 18, + .vsync_end = 1024 + 18 + 6, + .vtotal = 1024 + 18 + 6 + 16, + .clock = 65946, + .width_mm = 76, + .height_mm = 102, +}; + +static const struct st7703_panel_desc ayaneo_pocket_ds_lower_desc = { + .mode = &ayaneo_pocket_ds_lower_mode, + .lanes = 4, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .init_sequence = ayaneo_pocket_ds_lower_init_sequence, +}; + static int st7703_enable(struct drm_panel *panel) { struct st7703 *ctx = panel_to_st7703(panel); @@ -916,6 +1011,7 @@ static void st7703_remove(struct mipi_dsi_device *dsi) static const struct of_device_id st7703_of_match[] = { { .compatible = "anbernic,rg353v-panel-v2", .data = &rg353v2_desc }, + { .compatible = "ayaneo,pocket-ds-lower-panel", .data = &ayaneo_pocket_ds_lower_desc }, { .compatible = "gameforce,chi-panel", .data = &gameforcechi_desc }, { .compatible = "powkiddy,rgb10max3-panel", .data = &rgb10max3_panel_desc }, { .compatible = "powkiddy,rgb30-panel", .data = &rgb30panel_desc }, diff --git a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c index 13cfe252a838..fa961abdd2d7 100644 --- a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c +++ b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c @@ -35,6 +35,7 @@ struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel) static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel) { struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = tdo_tl070wsh30->link }; int err; err = regulator_enable(tdo_tl070wsh30->supply); @@ -51,44 +52,27 @@ static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel) msleep(200); - err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link); - if (err < 0) { - dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 200); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 20); + + if (dsi_ctx.accum_err) regulator_disable(tdo_tl070wsh30->supply); - return err; - } - msleep(200); - - err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link); - if (err < 0) { - dev_err(panel->dev, "failed to set display on: %d\n", err); - regulator_disable(tdo_tl070wsh30->supply); - return err; - } - - msleep(20); - - return 0; + return dsi_ctx.accum_err; } static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel) { struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel); - int err; - - err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link); - if (err < 0) - dev_err(panel->dev, "failed to set display off: %d\n", err); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = tdo_tl070wsh30->link }; + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + /* Reset error to continue power-down sequence even if display off failed */ + dsi_ctx.accum_err = 0; usleep_range(10000, 11000); - - err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link); - if (err < 0) { - dev_err(panel->dev, "failed to enter sleep mode: %d\n", err); - return err; - } - + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); usleep_range(10000, 11000); regulator_disable(tdo_tl070wsh30->supply); diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 106da676fd2e..46a3080b0b20 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -1824,7 +1824,7 @@ static int panthor_probe(struct platform_device *pdev) ptdev = devm_drm_dev_alloc(&pdev->dev, &panthor_drm_driver, struct panthor_device, base); if (IS_ERR(ptdev)) - return -ENOMEM; + return PTR_ERR(ptdev); platform_set_drvdata(pdev, ptdev); diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index de8e6689a869..fc1a423e48a8 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -697,7 +697,8 @@ static int panthor_fw_read_build_info(struct panthor_device *ptdev, return ret; if (hdr.meta_start > fw->size || - hdr.meta_start + hdr.meta_size > fw->size) { + hdr.meta_size > fw->size - hdr.meta_start || + hdr.meta_size <= header_len) { drm_err(&ptdev->base, "Firmware build info corrupt\n"); /* We don't need the build info, so continue */ return 0; @@ -877,14 +878,15 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev, struct panthor_fw_csg_iface *csg_iface = panthor_fw_get_csg_iface(ptdev, csg_idx); struct panthor_fw_cs_iface *cs_iface = &ptdev->fw->iface.streams[csg_idx][cs_idx]; u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); - u32 iface_offset = CSF_GROUP_CONTROL_OFFSET + - (csg_idx * glb_iface->control->group_stride) + + u64 iface_offset = CSF_GROUP_CONTROL_OFFSET + + ((u64)csg_idx * glb_iface->control->group_stride) + CSF_STREAM_CONTROL_OFFSET + - (cs_idx * csg_iface->control->stream_stride); + ((u64)cs_idx * csg_iface->control->stream_stride); struct panthor_fw_cs_iface *first_cs_iface = panthor_fw_get_cs_iface(ptdev, 0, 0); - if (iface_offset + sizeof(*cs_iface) >= shared_section_sz) + if (iface_offset > shared_section_sz || + sizeof(*cs_iface->control) > shared_section_sz - iface_offset) return -EINVAL; spin_lock_init(&cs_iface->lock); @@ -932,10 +934,12 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev, struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev); struct panthor_fw_csg_iface *csg_iface = &ptdev->fw->iface.groups[csg_idx]; u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); - u32 iface_offset = CSF_GROUP_CONTROL_OFFSET + (csg_idx * glb_iface->control->group_stride); + u64 iface_offset = CSF_GROUP_CONTROL_OFFSET + + ((u64)csg_idx * glb_iface->control->group_stride); unsigned int i; - if (iface_offset + sizeof(*csg_iface) >= shared_section_sz) + if (iface_offset > shared_section_sz || + sizeof(*csg_iface->control) > shared_section_sz - iface_offset) return -EINVAL; spin_lock_init(&csg_iface->lock); @@ -985,11 +989,15 @@ static u32 panthor_get_instr_features(struct panthor_device *ptdev) static int panthor_fw_init_ifaces(struct panthor_device *ptdev) { struct panthor_fw_global_iface *glb_iface = &ptdev->fw->iface.global; + u64 shared_section_sz = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); unsigned int i; if (!ptdev->fw->shared_section->mem->kmap) return -EINVAL; + if (sizeof(*glb_iface->control) > shared_section_sz) + return -EINVAL; + spin_lock_init(&glb_iface->lock); glb_iface->control = ptdev->fw->shared_section->mem->kmap; diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index f45ef5824ff2..0182b72f1932 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -306,10 +306,13 @@ struct panthor_vm { /** @kernel_auto_va.start: Start of the automatic VA-range for kernel BOs. */ u64 start; - /** @kernel_auto_va.size: Size of the automatic VA-range for kernel BOs. */ + /** @kernel_auto_va.end: End of the automatic VA-range for kernel BOs. */ u64 end; } kernel_auto_va; + /** @user_va_range: Upper boundary of VAs VM users can map objects against. */ + u64 user_va_range; + /** @as: Address space related fields. */ struct { /** @@ -2892,6 +2895,8 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu, va_range = full_va_range; } + vm->user_va_range = kernel_va_start; + mutex_init(&vm->mm_lock); drm_mm_init(&vm->mm, kernel_va_start, kernel_va_size); vm->kernel_auto_va.start = auto_kernel_va_start; @@ -2980,6 +2985,10 @@ panthor_vm_bind_prepare_op_ctx(struct drm_file *file, if (!IS_ALIGNED(op->va | op->size | op->bo_offset, vm_pgsz)) return -EINVAL; + /* We don't allow mappings that overlap with kbo's reserved range */ + if (range_overflows(op->va, op->size, vm->user_va_range)) + return -EINVAL; + switch (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) { case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP: if (!(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) { diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 0940eae7a2e4..0b0fc6fe3df2 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -140,6 +141,11 @@ #define SSD133X_SET_PRECHARGE_VOLTAGE 0xbb #define SSD133X_SET_VCOMH_VOLTAGE 0xbe +/* ssd133x A/B/C channel contrast at full brightness (white balance) */ +#define SSD133X_DEFAULT_CONTRAST_A 0x91 +#define SSD133X_DEFAULT_CONTRAST_B 0x50 +#define SSD133X_DEFAULT_CONTRAST_C 0x7d + #define MAX_CONTRAST 255 const struct ssd130x_deviceinfo ssd130x_variants[] = { @@ -582,12 +588,37 @@ static int ssd132x_init(struct ssd130x_device *ssd130x) return ssd130x_run_cmd_seq(ssd130x, cmds); } -static int ssd133x_init(struct ssd130x_device *ssd130x) +/* Scale a channel's white-balance calibration contrast by the requested brightness */ +static u8 ssd130x_scale_contrast(u8 calibration, u32 brightness) +{ + return DIV_ROUND_CLOSEST(calibration * brightness, MAX_CONTRAST); +} + +/* + * The A/B/C contrast channels drive sub-pixels whose OLED materials differ + * in luminous efficiency, so the per-channel values are a white-balance + * calibration. Scale them by the requested brightness instead of + * overwriting them, to keep the white point while dimming. + */ +static int ssd133x_set_contrast(struct ssd130x_device *ssd130x, u32 brightness) { const u8 cmds[] = { - 2, SSD133X_CONTRAST_A, 0x91, - 2, SSD133X_CONTRAST_B, 0x50, - 2, SSD133X_CONTRAST_C, 0x7d, + 2, SSD133X_CONTRAST_A, + ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_A, brightness), + 2, SSD133X_CONTRAST_B, + ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_B, brightness), + 2, SSD133X_CONTRAST_C, + ssd130x_scale_contrast(SSD133X_DEFAULT_CONTRAST_C, brightness), + 0, + }; + + return ssd130x_run_cmd_seq(ssd130x, cmds); +} + +static int ssd133x_init(struct ssd130x_device *ssd130x) +{ + int ret; + const u8 cmds[] = { 2, SSD133X_SET_MASTER_CURRENT, 0x06, 3, SSD133X_SET_COL_RANGE, 0x00, ssd130x->width - 1, 3, SSD133X_SET_ROW_RANGE, 0x00, ssd130x->height - 1, @@ -614,6 +645,10 @@ static int ssd133x_init(struct ssd130x_device *ssd130x) 0, }; + ret = ssd133x_set_contrast(ssd130x, ssd130x->contrast); + if (ret < 0) + return ret; + return ssd130x_run_cmd_seq(ssd130x, cmds); } @@ -1676,8 +1711,25 @@ static int ssd130x_update_bl(struct backlight_device *bdev) return 0; } -static const struct backlight_ops ssd130xfb_bl_ops = { - .update_status = ssd130x_update_bl, +static int ssd133x_update_bl(struct backlight_device *bdev) +{ + struct ssd130x_device *ssd130x = bl_get_data(bdev); + + ssd130x->contrast = backlight_get_brightness(bdev); + + return ssd133x_set_contrast(ssd130x, ssd130x->contrast); +} + +static const struct backlight_ops ssd130xfb_bl_ops[] = { + [SSD130X_FAMILY] = { + .update_status = ssd130x_update_bl, + }, + [SSD132X_FAMILY] = { + .update_status = ssd130x_update_bl, + }, + [SSD133X_FAMILY] = { + .update_status = ssd133x_update_bl, + }, }; static void ssd130x_parse_properties(struct ssd130x_device *ssd130x) @@ -1884,7 +1936,8 @@ struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap) return ERR_PTR(ret); bl = devm_backlight_device_register(dev, dev_name(dev), dev, ssd130x, - &ssd130xfb_bl_ops, NULL); + &ssd130xfb_bl_ops[ssd130x->device_info->family_id], + NULL); if (IS_ERR(bl)) return ERR_PTR(dev_err_probe(dev, PTR_ERR(bl), "Unable to register backlight device\n")); diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c index c2f7f69baf75..3fa1f4fda0f0 100644 --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c @@ -208,7 +208,7 @@ struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm, planes = sunxi_engine_layers_init(drm, engine); if (IS_ERR(planes)) { dev_err(drm->dev, "Couldn't create the planes\n"); - return NULL; + return ERR_CAST(planes); } /* find primary and cursor planes for drm_crtc_init_with_planes */ diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 8a409eee1dca..e9baca360160 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -289,6 +289,8 @@ static void sun4i_drv_traverse_endpoints(struct endpoint_list *list, kfifo_put(&list->fifo, remote); } + + of_node_put(port); } static int sun4i_drv_add_endpoints(struct device *dev, @@ -394,6 +396,7 @@ static int sun4i_drv_probe(struct platform_device *pdev) /* process this endpoint */ ret = sun4i_drv_add_endpoints(&pdev->dev, &list, &match, endpoint); + of_node_put(endpoint); /* sun4i_drv_add_endpoints can fail to allocate memory */ if (ret < 0) diff --git a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c index 9c8ef927d929..fc822bc2c62b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_framebuffer.c +++ b/drivers/gpu/drm/sun4i/sun4i_framebuffer.c @@ -14,24 +14,8 @@ #include "sun4i_drv.h" #include "sun4i_framebuffer.h" -static int sun4i_de_atomic_check(struct drm_device *dev, - struct drm_atomic_commit *state) -{ - int ret; - - ret = drm_atomic_helper_check_modeset(dev, state); - if (ret) - return ret; - - ret = drm_atomic_normalize_zpos(dev, state); - if (ret) - return ret; - - return drm_atomic_helper_check_planes(dev, state); -} - static const struct drm_mode_config_funcs sun4i_de_mode_config_funcs = { - .atomic_check = sun4i_de_atomic_check, + .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, .fb_create = drm_gem_fb_create, }; @@ -46,6 +30,7 @@ void sun4i_framebuffer_init(struct drm_device *drm) drm->mode_config.max_width = 8192; drm->mode_config.max_height = 8192; + drm->mode_config.normalize_zpos = true; drm->mode_config.funcs = &sun4i_de_mode_config_funcs; drm->mode_config.helper_private = &sun4i_de_mode_config_helpers; diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index e99f52ebb26f..97253d631b2b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -160,7 +160,7 @@ static void sun4i_hdmi_enable(struct drm_encoder *encoder, drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); - val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); + val = SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END); writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0)); diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index d4c1723c5e3d..bf405a2aa324 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -717,9 +717,11 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, case DRM_MODE_ENCODER_DSI: /* DSI is tied to special case of CPU interface */ sun4i_tcon0_mode_set_cpu(tcon, encoder, mode); + sun4i_tcon_set_mux(tcon, 0, encoder); break; case DRM_MODE_ENCODER_LVDS: sun4i_tcon0_mode_set_lvds(tcon, encoder, mode); + sun4i_tcon_set_mux(tcon, 0, encoder); break; case DRM_MODE_ENCODER_NONE: sun4i_tcon0_mode_set_rgb(tcon, encoder, mode); @@ -970,6 +972,7 @@ static int sun4i_tcon_of_get_id_from_port(struct device_node *port) continue; ret = of_property_read_u32(remote, "reg", ®); + of_node_put(remote); if (ret) continue; @@ -1409,7 +1412,7 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon, { struct device_node *port, *remote; struct platform_device *pdev; - int id, ret; + int id, ret = 0; /* find TCON TOP platform device and TCON id */ @@ -1432,21 +1435,20 @@ static int sun8i_r40_tcon_tv_set_mux(struct sun4i_tcon *tcon, if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) && encoder->encoder_type == DRM_MODE_ENCODER_TMDS) { ret = sun8i_tcon_top_set_hdmi_src(&pdev->dev, id); - if (ret) { - put_device(&pdev->dev); - return ret; - } + if (ret) + goto out_put_device; } if (IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP)) { ret = sun8i_tcon_top_de_config(&pdev->dev, tcon->id, id); - if (ret) { - put_device(&pdev->dev); - return ret; - } + if (ret) + goto out_put_device; } - return 0; +out_put_device: + put_device(&pdev->dev); + + return ret; } static const struct sun4i_tcon_quirks sun4i_a10_quirks = { diff --git a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c index 9694ad142a7d..bd3923ef5a41 100644 --- a/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c +++ b/drivers/gpu/drm/sun4i/sun8i_dw_hdmi.c @@ -86,6 +86,8 @@ static u32 sun8i_dw_hdmi_find_possible_crtcs(struct drm_device *drm, of_node_put(remote_port); } } + + of_node_put(port); } else { crtcs = drm_of_find_possible_crtcs(drm, node); } diff --git a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c index 4fa69c463dc4..489ea94693ff 100644 --- a/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c +++ b/drivers/gpu/drm/sun4i/sun8i_hdmi_phy.c @@ -91,7 +91,7 @@ static const struct dw_hdmi_mpll_config sun50i_h6_mpll_cfg[] = { }, }, { 594000000, { - { 0x1a40, 0x0003 }, + { 0x1a7c, 0x0003 }, { 0x3b4c, 0x0003 }, { 0x5a64, 0x0003 }, }, diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index 4e4226295010..b6cd7352e76c 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -785,7 +785,7 @@ static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { .de_type = SUN8I_MIXER_DE2, .vi_scaler_num = 2, .scaler_mask = 0x3, - .scanline_yuv = 2048, + .scanline_yuv = 1024, }, .de_type = SUN8I_MIXER_DE2, .mod_rate = 150000000, diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c index 8adda578c51b..9cbd655518b2 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c @@ -25,6 +25,49 @@ static bool sun8i_tcon_top_node_is_tcon_top(struct device_node *node) return !!of_match_node(sun8i_tcon_top_of_table, node); } +static unsigned int sun8i_tcon_top_get_tcon_map(struct device_node *node) +{ + static const u32 out_ports[] = { + TCON_TOP_MIXER0_OUT_PORT, + TCON_TOP_MIXER1_OUT_PORT, + }; + unsigned int i, map = 0; + + for (i = 0; i < ARRAY_SIZE(out_ports); i++) { + struct device_node *port; + + port = of_graph_get_port_by_id(node, out_ports[i]); + if (!port) + continue; + + for_each_of_graph_port_endpoint(port, ep) { + struct of_endpoint endpoint; + + if (of_graph_parse_endpoint(ep, &endpoint)) + continue; + + if (endpoint.id < TCON_TOP_PORT_TCON_NUM) + map |= BIT(endpoint.id); + } + + of_node_put(port); + } + + return map; +} + +static unsigned int sun8i_tcon_top_park_index(struct sun8i_tcon_top *tcon_top, + int tcon) +{ + unsigned int candidates; + + candidates = tcon_top->tcon_map & ~BIT(tcon); + if (!candidates) + candidates = GENMASK(TCON_TOP_PORT_TCON_NUM - 1, 0) & ~BIT(tcon); + + return ffs(candidates) - 1; +} + int sun8i_tcon_top_set_hdmi_src(struct device *dev, int tcon) { struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev); @@ -57,6 +100,7 @@ EXPORT_SYMBOL(sun8i_tcon_top_set_hdmi_src); int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon) { struct sun8i_tcon_top *tcon_top = dev_get_drvdata(dev); + u32 mixer_msk, other_msk; unsigned long flags; u32 reg; @@ -70,21 +114,27 @@ int sun8i_tcon_top_de_config(struct device *dev, int mixer, int tcon) return -EINVAL; } - if (tcon > 3) { - dev_err(dev, "TCON index is too high!\n"); + if (tcon < 0 || tcon >= TCON_TOP_PORT_TCON_NUM) { + dev_err(dev, "TCON index is invalid!\n"); return -EINVAL; } + mixer_msk = mixer ? TCON_TOP_PORT_DE1_MSK : TCON_TOP_PORT_DE0_MSK; + other_msk = mixer ? TCON_TOP_PORT_DE0_MSK : TCON_TOP_PORT_DE1_MSK; + spin_lock_irqsave(&tcon_top->reg_lock, flags); reg = readl(tcon_top->regs + TCON_TOP_PORT_SEL_REG); - if (mixer == 0) { - reg &= ~TCON_TOP_PORT_DE0_MSK; - reg |= FIELD_PREP(TCON_TOP_PORT_DE0_MSK, tcon); - } else { - reg &= ~TCON_TOP_PORT_DE1_MSK; - reg |= FIELD_PREP(TCON_TOP_PORT_DE1_MSK, tcon); + + reg &= ~mixer_msk; + reg |= field_prep(mixer_msk, tcon); + + if (field_get(other_msk, reg) == tcon) { + reg &= ~other_msk; + reg |= field_prep(other_msk, + sun8i_tcon_top_park_index(tcon_top, tcon)); } + writel(reg, tcon_top->regs + TCON_TOP_PORT_SEL_REG); spin_unlock_irqrestore(&tcon_top->reg_lock, flags); @@ -143,6 +193,7 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, return -ENOMEM; clk_data->num = CLK_NUM; tcon_top->clk_data = clk_data; + tcon_top->tcon_map = sun8i_tcon_top_get_tcon_map(dev->of_node); spin_lock_init(&tcon_top->reg_lock); @@ -175,10 +226,6 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, goto err_assert_reset; } - /* - * At least on H6, some registers have some bits set by default - * which may cause issues. Clear them here. - */ writel(0, regs + TCON_TOP_PORT_SEL_REG); writel(0, regs + TCON_TOP_GATE_SRC_REG); diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.h b/drivers/gpu/drm/sun4i/sun8i_tcon_top.h index 0390584a330e..2b887470a49f 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.h +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.h @@ -14,6 +14,10 @@ #define TCON_TOP_PORT_SEL_REG 0x1C #define TCON_TOP_PORT_DE0_MSK GENMASK(1, 0) #define TCON_TOP_PORT_DE1_MSK GENMASK(5, 4) +#define TCON_TOP_PORT_TCON_NUM 4 + +#define TCON_TOP_MIXER0_OUT_PORT 1 +#define TCON_TOP_MIXER1_OUT_PORT 3 #define TCON_TOP_GATE_SRC_REG 0x20 #define TCON_TOP_HDMI_SRC_MSK GENMASK(29, 28) @@ -29,6 +33,8 @@ struct sun8i_tcon_top { void __iomem *regs; struct reset_control *rst; + unsigned int tcon_map; + /* * spinlock is used to synchronize access to same * register where multiple clock gates can be set. diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c index 3dec4eeb1ba2..a5cc99981006 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c @@ -894,20 +894,26 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base, lan3coefftab32_left[offset + i]); regmap_write(map, SUN8I_SCALER_VSU_YHCOEFF1(base, i), lan3coefftab32_right[offset + i]); + } + offset = sun8i_vi_scaler_coef_index(vstep) * + SUN8I_VI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) + regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i), + lan2coefftab32[offset + i]); + + offset = sun8i_vi_scaler_coef_index(hstep / format->hsub) * + SUN8I_VI_SCALER_COEFF_COUNT; + for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF0(base, i), ch_left[offset + i]); regmap_write(map, SUN8I_SCALER_VSU_CHCOEFF1(base, i), ch_right[offset + i]); } - - offset = sun8i_vi_scaler_coef_index(hstep) * + offset = sun8i_vi_scaler_coef_index(vstep / format->vsub) * SUN8I_VI_SCALER_COEFF_COUNT; - for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) { - regmap_write(map, SUN8I_SCALER_VSU_YVCOEFF(base, i), - lan2coefftab32[offset + i]); + for (i = 0; i < SUN8I_VI_SCALER_COEFF_COUNT; i++) regmap_write(map, SUN8I_SCALER_VSU_CVCOEFF(base, i), cy[offset + i]); - } } void sun8i_vi_scaler_enable(struct sun8i_layer *layer, bool enable) @@ -969,6 +975,8 @@ void sun8i_vi_scaler_setup(struct sun8i_layer *layer, regmap_write(layer->regs, SUN50I_SCALER_VSU_SCALE_MODE(base), val); + regmap_write(layer->regs, + SUN50I_SCALER_VSU_GLB_ALPHA(base), 0xff); } regmap_write(layer->regs, diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h index 245fe2f431c3..d89c0cbe188d 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h +++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h @@ -36,6 +36,7 @@ #define SUN50I_SCALER_VSU_EDSCL_CTRL(base) ((base) + 0x28) #define SUN50I_SCALER_VSU_ANGLE_THR(base) ((base) + 0x2c) #define SUN8I_SCALER_VSU_OUTSIZE(base) ((base) + 0x40) +#define SUN50I_SCALER_VSU_GLB_ALPHA(base) ((base) + 0x44) #define SUN8I_SCALER_VSU_YINSIZE(base) ((base) + 0x80) #define SUN8I_SCALER_VSU_YHSTEP(base) ((base) + 0x88) #define SUN8I_SCALER_VSU_YVSTEP(base) ((base) + 0x8c) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 3980f376e3ba..ef56c18ded1b 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -488,6 +488,118 @@ int ttm_bo_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man return ret; } +struct ttm_bo_alloc_state { + /** @charge_pool: The memory pool the resource is charged to */ + struct dmem_cgroup_pool_state *charge_pool; + /** @limit_pool: Which pool limit we should test against */ + struct dmem_cgroup_pool_state *limit_pool; + /** @in_evict: Whether we are currently evicting buffers */ + bool in_evict; + /** @may_try_low: If only unprotected BOs, i.e. BOs whose cgroup + * is exceeding its dmem low/min protection, should be considered for eviction + */ + bool may_try_low; +}; + +/** + * ttm_bo_alloc_at_place - Attempt allocating a BO's backing store in a place + * + * @bo: The buffer to allocate the backing store of + * @place: The place to attempt allocation in + * @ctx: ttm_operation_ctx associated with this allocation + * @force_space: If we should evict buffers to force space + * @res: On allocation success, the resulting struct ttm_resource. + * @alloc_state: Object holding allocation state such as charged cgroups. + * + * Returns: + * -EBUSY: No space available, but allocation should be retried with ttm_bo_evict_alloc. + * -ENOSPC: No space available, allocation should not be retried. + * -ERESTARTSYS: An interruptible sleep was interrupted by a signal. + * + */ +static int ttm_bo_alloc_at_place(struct ttm_buffer_object *bo, + const struct ttm_place *place, + bool force_space, + struct ttm_resource **res, + struct ttm_bo_alloc_state *alloc_state) +{ + bool may_evict; + int ret; + + may_evict = !alloc_state->in_evict && force_space && + place->mem_type != TTM_PL_SYSTEM; + if (!alloc_state->charge_pool) { + ret = ttm_resource_try_charge(bo, place, &alloc_state->charge_pool, + force_space ? &alloc_state->limit_pool + : NULL); + if (ret) { + /* + * -EAGAIN means the charge failed, which we treat + * like an allocation failure. Therefore, return an + * error code indicating the allocation failed - + * either -EBUSY if the allocation should be + * retried with eviction, or -ENOSPC if there should + * be no second attempt. + */ + if (!alloc_state->in_evict) + alloc_state->may_try_low = may_evict; + if (ret == -EAGAIN) + ret = may_evict ? -EBUSY : -ENOSPC; + return ret; + } + } + + /* + * cgroup protection plays a special role in eviction. + * Conceptually, protection of memory via the dmem cgroup controller + * entitles the protected cgroup to use a certain amount of memory. + * There are two types of protection - the 'low' limit is a + * "best-effort" protection, whereas the 'min' limit provides a hard + * guarantee that memory within the cgroup's allowance will not be + * evicted under any circumstance. + * + * To faithfully model this concept in TTM, we also need to take cgroup + * protection into account when allocating. When allocation in one + * place fails, TTM will default to trying other places first before + * evicting. + * If the allocation is covered by dmem cgroup protection, however, + * this prevents the allocation from using the memory it is "entitled" + * to. To make sure unprotected allocations cannot push new protected + * allocations out of places they are "entitled" to use, we should + * evict buffers not covered by any cgroup protection, if this + * allocation is covered by cgroup protection. + * + * Buffers covered by 'min' protection are a special case - the 'min' + * limit is a stronger guarantee than 'low', and thus buffers protected + * by 'low' but not 'min' should also be considered for eviction. + * Buffers protected by 'min' will never be considered for eviction + * anyway, so the regular eviction path should be triggered here. + * Buffers protected by 'low' but not 'min' will take a special + * eviction path that only evicts buffers covered by neither 'low' or + * 'min' protections. + */ + if (!alloc_state->in_evict) { + may_evict |= dmem_cgroup_below_min(NULL, alloc_state->charge_pool); + alloc_state->may_try_low = may_evict; + + may_evict |= dmem_cgroup_below_low(NULL, alloc_state->charge_pool); + } + + ret = ttm_resource_alloc(bo, place, res, alloc_state->charge_pool); + if (ret) { + if (ret == -ENOSPC && may_evict) + ret = -EBUSY; + return ret; + } + + /* + * Ownership of charge_pool has been transferred to the TTM resource, + * don't make the caller think we still hold a reference to it. + */ + alloc_state->charge_pool = NULL; + return 0; +} + /** * struct ttm_bo_evict_walk - Parameters for the evict walk. */ @@ -503,24 +615,70 @@ struct ttm_bo_evict_walk { /** @evicted: Number of successful evictions. */ unsigned long evicted; - /** @limit_pool: Which pool limit we should test against */ - struct dmem_cgroup_pool_state *limit_pool; /** @try_low: Whether we should attempt to evict BO's with low watermark threshold */ bool try_low; /** @hit_low: If we cannot evict a bo when @try_low is false (first pass) */ bool hit_low; + + /** @alloc_state: State associated with the allocation attempt. */ + struct ttm_bo_alloc_state *alloc_state; }; static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) { struct ttm_bo_evict_walk *evict_walk = container_of(walk, typeof(*evict_walk), walk); + struct dmem_cgroup_pool_state *limit_pool, *ancestor = NULL; + s64 bo_size = bo->base.size; + bool evict_valuable; s64 lret; - if (!dmem_cgroup_state_evict_valuable(evict_walk->limit_pool, bo->resource->css, - evict_walk->try_low, &evict_walk->hit_low)) + /* + * If may_try_low is not set, then we're trying to evict unprotected + * buffers in favor of a protected allocation for charge_pool. Explicitly skip + * buffers belonging to the same cgroup here - that cgroup is definitely protected, + * even though dmem_cgroup_state_evict_valuable would allow the eviction because a + * cgroup is always allowed to evict from itself even if it is protected. + */ + if (!evict_walk->alloc_state->may_try_low && + bo->resource->css == evict_walk->alloc_state->charge_pool) return 0; + limit_pool = evict_walk->alloc_state->limit_pool; + /* + * If there is no explicit limit pool, find the root of the shared subtree between + * evictor and evictee. This is important so that recursive protection rules can + * apply properly: Recursive protection distributes cgroup protection afforded + * to a parent cgroup but not used explicitly by a child cgroup between all child + * cgroups (see docs of effective_protection in mm/page_counter.c). However, when + * direct siblings compete for memory, siblings that were explicitly protected + * should get prioritized over siblings that weren't. This only happens correctly + * when the root of the shared subtree is passed to + * dmem_cgroup_state_evict_valuable. Otherwise, the effective-protection + * calculation cannot distinguish direct siblings from unrelated subtrees and the + * calculated protection ends up wrong. + */ + if (!limit_pool) { + ancestor = dmem_cgroup_get_common_ancestor(bo->resource->css, + evict_walk->alloc_state->charge_pool); + limit_pool = ancestor; + } + + evict_valuable = dmem_cgroup_state_evict_valuable(limit_pool, bo->resource->css, + evict_walk->try_low, + &evict_walk->hit_low); + if (ancestor) + dmem_cgroup_pool_state_put(ancestor); + + if (!evict_valuable) + return 0; + + /* + * evict_walk->place is NULL in cgroup drain mode. Drivers' + * eviction_valuable() callbacks must handle a NULL place, treating it + * as "any placement": the TTM base implementation already does so via + * ttm_resource_intersects(). + */ if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo, evict_walk->place)) return 0; @@ -536,11 +694,17 @@ static s64 ttm_bo_evict_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object * goto out; evict_walk->evicted++; - if (evict_walk->res) - lret = ttm_resource_alloc(evict_walk->evictor, evict_walk->place, - evict_walk->res, NULL); - if (lret == 0) - return 1; + if (evict_walk->res) { + lret = ttm_bo_alloc_at_place(evict_walk->evictor, + evict_walk->place, false, + evict_walk->res, + evict_walk->alloc_state); + if (lret == 0) + return 1; + } else { + /* Cgroup drain: return bytes freed for byte-denominated progress. */ + return bo_size; + } out: /* Errors that should terminate the walk. */ if (lret == -ENOSPC) @@ -560,7 +724,7 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, struct ww_acquire_ctx *ticket, struct ttm_resource **res, - struct dmem_cgroup_pool_state *limit_pool) + struct ttm_bo_alloc_state *state) { struct ttm_bo_evict_walk evict_walk = { .walk = { @@ -573,15 +737,21 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev, .place = place, .evictor = evictor, .res = res, - .limit_pool = limit_pool, + .alloc_state = state, }; s64 lret; + state->in_evict = true; + evict_walk.walk.arg.trylock_only = true; lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1); - /* One more attempt if we hit low limit? */ - if (!lret && evict_walk.hit_low) { + /* If we failed to find enough BOs to evict, but we skipped over + * some BOs because they were covered by dmem low protection, retry + * evicting these protected BOs too, except if we're told not to + * consider protected BOs at all. + */ + if (!lret && evict_walk.hit_low && state->may_try_low) { evict_walk.try_low = true; lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, 1); } @@ -602,11 +772,13 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev, } while (!lret && evict_walk.evicted); /* We hit the low limit? Try once more */ - if (!lret && evict_walk.hit_low && !evict_walk.try_low) { + if (!lret && evict_walk.hit_low && !evict_walk.try_low && + state->may_try_low) { evict_walk.try_low = true; goto retry; } out: + state->in_evict = false; if (lret < 0) return lret; if (lret == 0) @@ -614,6 +786,86 @@ static int ttm_bo_evict_alloc(struct ttm_device *bdev, return 0; } +/** + * ttm_bo_evict_cgroup() - Evict buffer objects charged to a specific cgroup. + * @bdev: The TTM device. + * @man: The resource manager whose LRU to walk. + * @limit_pool: The cgroup pool state whose members should be evicted. + * @target_bytes: Number of bytes to free. + * @ctx: The TTM operation context. + * + * Walk the LRU of @man and evict buffer objects that are charged to the + * cgroup identified by @limit_pool, until at least @target_bytes have been + * freed. Mirrors the two-pass (trylock -> sleeping-lock, low-watermark) + * strategy used by ttm_bo_evict_alloc(). + * + * Return: >= @target_bytes on full success, 0..target_bytes-1 if partial, + * negative error code on fatal error. + */ +s64 ttm_bo_evict_cgroup(struct ttm_device *bdev, + struct ttm_resource_manager *man, + struct dmem_cgroup_pool_state *limit_pool, + s64 target_bytes, + struct ttm_operation_ctx *ctx) +{ + struct ttm_bo_evict_walk evict_walk = { + .walk = { + .ops = &ttm_evict_walk_ops, + .arg = { .ctx = ctx }, + }, + .alloc_state = &(struct ttm_bo_alloc_state) { + .limit_pool = limit_pool, + .in_evict = true, + }, + /* place, evictor, res left NULL: selects cgroup drain mode */ + }; + s64 lret, pass; + + evict_walk.walk.arg.trylock_only = true; + lret = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, target_bytes); + if (lret < 0 || lret >= target_bytes) + return lret; + + /* Second pass: also evict BOs at the low watermark. */ + if (evict_walk.hit_low) { + evict_walk.try_low = true; + pass = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, + target_bytes - lret); + if (pass < 0) + return pass; + lret += pass; + if (lret >= target_bytes) + return lret; + } + + /* Full sleeping-lock pass for remaining target. */ + evict_walk.try_low = evict_walk.hit_low = false; + evict_walk.walk.arg.trylock_only = false; + +retry: + evict_walk.walk.arg.sleeping_lock = true; + do { + evict_walk.evicted = 0; + pass = ttm_lru_walk_for_evict(&evict_walk.walk, bdev, man, + target_bytes - lret); + if (pass < 0) { + lret = pass; + goto out; + } + lret += pass; + } while (lret < target_bytes && evict_walk.evicted); + + /* One more attempt if we hit the low limit during sleeping-lock pass. */ + if (lret < target_bytes && evict_walk.hit_low && !evict_walk.try_low) { + evict_walk.try_low = true; + goto retry; + } + +out: + return lret; +} +EXPORT_SYMBOL(ttm_bo_evict_cgroup); + /** * ttm_bo_pin - Pin the buffer object. * @bo: The buffer object to pin @@ -724,9 +976,8 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo, for (i = 0; i < placement->num_placement; ++i) { const struct ttm_place *place = &placement->placement[i]; - struct dmem_cgroup_pool_state *limit_pool = NULL; + struct ttm_bo_alloc_state alloc_state = {}; struct ttm_resource_manager *man; - bool may_evict; man = ttm_manager_type(bdev, place->mem_type); if (!man || !ttm_resource_manager_used(man)) @@ -736,25 +987,30 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo, TTM_PL_FLAG_FALLBACK)) continue; - may_evict = (force_space && place->mem_type != TTM_PL_SYSTEM); - ret = ttm_resource_alloc(bo, place, res, force_space ? &limit_pool : NULL); - if (ret) { - if (ret != -ENOSPC) { - dmem_cgroup_pool_state_put(limit_pool); - return ret; - } - if (!may_evict) { - dmem_cgroup_pool_state_put(limit_pool); - continue; - } + ret = ttm_bo_alloc_at_place(bo, place, force_space, res, + &alloc_state); + if (ret == -ENOSPC) { + dmem_cgroup_uncharge(alloc_state.charge_pool, bo->base.size); + dmem_cgroup_pool_state_put(alloc_state.limit_pool); + continue; + } else if (ret == -EBUSY) { ret = ttm_bo_evict_alloc(bdev, man, place, bo, ctx, - ticket, res, limit_pool); - dmem_cgroup_pool_state_put(limit_pool); - if (ret == -EBUSY) - continue; - if (ret) + ticket, res, &alloc_state); + + dmem_cgroup_pool_state_put(alloc_state.limit_pool); + + if (ret) { + dmem_cgroup_uncharge(alloc_state.charge_pool, + bo->base.size); + if (ret == -EBUSY) + continue; return ret; + } + } else if (ret) { + dmem_cgroup_uncharge(alloc_state.charge_pool, bo->base.size); + dmem_cgroup_pool_state_put(alloc_state.limit_pool); + return ret; } ret = ttm_bo_add_pipelined_eviction_fences(bo, man, ctx->no_wait_gpu); diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 3e3c201a0222..bd0b23ac2cc4 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -999,7 +999,8 @@ __ttm_bo_lru_cursor_next(struct ttm_bo_lru_cursor *curs) bo = res->bo; if (ttm_lru_walk_trylock(curs, bo)) bo_locked = true; - else if (!arg->ticket || arg->ctx->no_wait_gpu || arg->trylock_only) + else if ((!arg->ticket && !arg->sleeping_lock) || arg->ctx->no_wait_gpu || + arg->trylock_only) continue; if (!ttm_bo_get_unless_zero(bo)) { diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 154d6739256f..4e6d3d658d85 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -386,33 +386,52 @@ void ttm_resource_fini(struct ttm_resource_manager *man, } EXPORT_SYMBOL(ttm_resource_fini); -int ttm_resource_alloc(struct ttm_buffer_object *bo, - const struct ttm_place *place, - struct ttm_resource **res_ptr, - struct dmem_cgroup_pool_state **ret_limit_pool) +/** + * ttm_resource_try_charge - charge a resource manager's cgroup pool + * @bo: buffer for which an allocation should be charged + * @place: where the allocation is attempted to be placed + * @ret_pool: on charge success, the pool that was charged + * @ret_limit_pool: on charge failure, the pool responsible for the failure + * + * Should be used to charge cgroups before attempting resource allocation. + * When charging succeeds, the value of ret_pool should be passed to + * ttm_resource_alloc. + * + * Returns: 0 on charge success, negative errno on failure. + */ +int ttm_resource_try_charge(struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct dmem_cgroup_pool_state **ret_pool, + struct dmem_cgroup_pool_state **ret_limit_pool) +{ + struct ttm_resource_manager *man = + ttm_manager_type(bo->bdev, place->mem_type); + + if (!man->cg) { + *ret_pool = NULL; + if (ret_limit_pool) + *ret_limit_pool = NULL; + return 0; + } + + return dmem_cgroup_try_charge(man->cg, bo->base.size, ret_pool, + ret_limit_pool); +} + +int ttm_resource_alloc(struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_resource **res_ptr, + struct dmem_cgroup_pool_state *charge_pool) { struct ttm_resource_manager *man = ttm_manager_type(bo->bdev, place->mem_type); - struct dmem_cgroup_pool_state *pool = NULL; int ret; - if (man->cg) { - ret = dmem_cgroup_try_charge(man->cg, bo->base.size, &pool, ret_limit_pool); - if (ret) { - if (ret == -EAGAIN) - ret = -ENOSPC; - return ret; - } - } - ret = man->func->alloc(man, bo, place, res_ptr); - if (ret) { - if (pool) - dmem_cgroup_uncharge(pool, bo->base.size); + if (ret) return ret; - } - (*res_ptr)->css = pool; + (*res_ptr)->css = charge_pool; spin_lock(&bo->bdev->lru_lock); ttm_resource_add_bulk_move(*res_ptr, bo); @@ -437,7 +456,7 @@ void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res) man = ttm_manager_type(bo->bdev, (*res)->mem_type); man->func->free(man, *res); *res = NULL; - if (man->cg) + if (pool) dmem_cgroup_uncharge(pool, bo->base.size); } EXPORT_SYMBOL(ttm_resource_free); @@ -953,3 +972,55 @@ void ttm_resource_manager_create_debugfs(struct ttm_resource_manager *man, #endif } EXPORT_SYMBOL(ttm_resource_manager_create_debugfs); + +/** + * ttm_resource_manager_dmem_reclaim() - dmem cgroup reclaim callback for TTM + * resource managers. + * @pool: The dmem cgroup pool state for the cgroup being reclaimed. + * @target_bytes: Number of bytes to try to free. + * @priv: The &ttm_resource_manager pointer, passed as @init.reclaim_priv to + * dmem_cgroup_register_region(). + * + * Drivers should use this as the @reclaim member of their own + * &struct dmem_cgroup_ops, with the &ttm_resource_manager pointer as + * @init.reclaim_priv. + * + * Return: 0 if some memory was freed, -ENOSPC if nothing was freed, or + * another negative error code on fatal failure. + */ +int ttm_resource_manager_dmem_reclaim(struct dmem_cgroup_pool_state *pool, + u64 target_bytes, void *priv) +{ + struct ttm_resource_manager *man = priv; + struct ttm_operation_ctx ctx = { .interruptible = true }; + s64 freed; + + freed = ttm_bo_evict_cgroup(man->bdev, man, pool, target_bytes, &ctx); + if (freed < 0) + return freed; + + return freed > 0 ? 0 : -ENOSPC; +} +EXPORT_SYMBOL(ttm_resource_manager_dmem_reclaim); + +/** + * ttm_resource_manager_set_dmem_region() - Associate a dmem cgroup region with a + * resource manager. + * @man: The resource manager. + * @region: The dmem cgroup region to associate, may be NULL or IS_ERR(). + * + * When @region is valid, stores it in @man->cg so that TTM can look up the + * associated pool during charging and eviction-target selection. When + * @region is %NULL, clears @man->cg to detach the region before teardown. + * An IS_ERR() @region is ignored, leaving @man->cg unchanged. + * The reclaim callback must be wired up using ttm_resource_manager_dmem_reclaim() + * in the driver's own &struct dmem_cgroup_ops, with the manager pointer as + * @init.reclaim_priv. + */ +void ttm_resource_manager_set_dmem_region(struct ttm_resource_manager *man, + struct dmem_cgroup_region *region) +{ + if (!IS_ERR(region)) + man->cg = region; +} +EXPORT_SYMBOL(ttm_resource_manager_set_dmem_region); diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c index b518f7dec680..05911904c1f9 100644 --- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c +++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c @@ -4,6 +4,8 @@ * Copyright (C) 2021-2022 Red Hat */ +#include + #include #include #include @@ -13,6 +15,7 @@ #include "xe_bo.h" #include "xe_device.h" +#include "xe_pm.h" #include "xe_res_cursor.h" #include "xe_ttm_vram_mgr.h" #include "xe_vram_types.h" @@ -274,14 +277,47 @@ static const struct ttm_resource_manager_func xe_ttm_vram_mgr_func = { .debug = xe_ttm_vram_mgr_debug }; +static const struct dmem_cgroup_ops xe_ttm_vram_mgr_dmem_ops; + +static int xe_ttm_vram_mgr_dmem_reclaim(struct dmem_cgroup_pool_state *pool, + u64 target_bytes, void *priv) +{ + struct ttm_resource_manager *man = priv; + struct xe_device *xe = ttm_to_xe_device(man->bdev); + int ret, idx; + + if (!drm_dev_enter(&xe->drm, &idx)) + return -ENODEV; + + { + ACQUIRE(xe_pm_runtime_ioctl, pm)(xe); + + ret = ACQUIRE_ERR(xe_pm_runtime_ioctl, &pm); + if (ret >= 0) + ret = ttm_resource_manager_dmem_reclaim(pool, target_bytes, priv); + } + + drm_dev_exit(idx); + return ret; +} + +static const struct dmem_cgroup_ops xe_ttm_vram_mgr_dmem_ops = { + .reclaim = xe_ttm_vram_mgr_dmem_reclaim, +}; + +static void xe_ttm_vram_mgr_set_unused(struct drm_device *dev, void *arg) +{ + struct ttm_resource_manager *man = arg; + + ttm_resource_manager_set_used(man, false); +} + static void xe_ttm_vram_mgr_fini(struct drm_device *dev, void *arg) { struct xe_device *xe = to_xe_device(dev); struct xe_ttm_vram_mgr *mgr = arg; struct ttm_resource_manager *man = &mgr->manager; - ttm_resource_manager_set_used(man, false); - if (ttm_resource_manager_evict_all(&xe->ttm, man)) return; @@ -299,14 +335,10 @@ int __xe_ttm_vram_mgr_init(struct xe_device *xe, struct xe_ttm_vram_mgr *mgr, u64 default_page_size) { struct ttm_resource_manager *man = &mgr->manager; + struct dmem_cgroup_region *cg; const char *name; int err; - name = mem_type == XE_PL_VRAM0 ? "vram0" : "vram1"; - man->cg = drmm_cgroup_register_region(&xe->drm, name, size); - if (IS_ERR(man->cg)) - return PTR_ERR(man->cg); - man->func = &xe_ttm_vram_mgr_func; mgr->mem_type = mem_type; err = drmm_mutex_init(&xe->drm, &mgr->lock); @@ -323,9 +355,30 @@ int __xe_ttm_vram_mgr_init(struct xe_device *xe, struct xe_ttm_vram_mgr *mgr, gpu_buddy_driver_set_lock(&mgr->mm, &mgr->lock); ttm_set_driver_manager(&xe->ttm, mem_type, &mgr->manager); - ttm_resource_manager_set_used(&mgr->manager, true); - return drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_fini, mgr); + /* + * Register the fini action before the cgroup region so that devres + * LIFO teardown runs unregister_region before manager teardown + * (draining any in-flight reclaim callbacks) and the manager fini second. + */ + err = drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_fini, mgr); + if (err) + return err; + + name = mem_type == XE_PL_VRAM0 ? "vram0" : "vram1"; + cg = drmm_cgroup_register_region(&xe->drm, name, + &(struct dmem_cgroup_init){ + .size = size, + .ops = &xe_ttm_vram_mgr_dmem_ops, + .reclaim_priv = man, + }); + if (IS_ERR(cg)) + return PTR_ERR(cg); + + ttm_resource_manager_set_dmem_region(man, cg); + ttm_resource_manager_set_used(man, true); + + return drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_set_unused, man); } /** diff --git a/drivers/gpu/tests/.kunitconfig b/drivers/gpu/tests/.kunitconfig new file mode 100644 index 000000000000..fd611b3dfe09 --- /dev/null +++ b/drivers/gpu/tests/.kunitconfig @@ -0,0 +1,2 @@ +CONFIG_KUNIT=y +CONFIG_GPU_BUDDY_KUNIT_TEST=y diff --git a/drivers/gpu/tests/gpu_buddy_test.c b/drivers/gpu/tests/gpu_buddy_test.c index 89698563c61b..ed4c1c3acb3c 100644 --- a/drivers/gpu/tests/gpu_buddy_test.c +++ b/drivers/gpu/tests/gpu_buddy_test.c @@ -1004,42 +1004,51 @@ static void gpu_test_buddy_alloc_clear(struct kunit *test) gpu_buddy_fini(&mm); /* - * Using a non-power-of-two mm size, allocate alternating blocks of 4KiB in an - * even sequence and free them as cleared. All blocks should be marked as - * dirty and the split blocks should be merged back to their original - * size when the blocks clear reset function is called. + * Using a non-power-of-two mm size, allocate all 4KiB blocks and split + * them across two alternating lists, then free one list as cleared and + * the other as dirty. This interleaves cleared and dirty blocks so that + * neighbouring buddies cannot be merged, fragmenting the address space. + * After gpu_buddy_reset_clear(false) every block should be marked dirty + * and the split blocks should be merged back to their original size, so + * clear_avail must drop to 0. */ KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); KUNIT_EXPECT_EQ(test, mm.max_order, max_order); - i = 0; n_pages = mm_size / ps; - do { - if (i % 2 == 0) - KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, - ps, ps, &allocated, 0), - "buddy_alloc hit an error size=%lu\n", ps); - } while (++i < n_pages); + for (i = 0; i < n_pages; i++) { + struct list_head *list = (i % 2) ? &clean : &dirty; - gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, + ps, ps, list, 0), + "buddy_alloc hit an error size=%lu\n", ps); + } + + gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &dirty, 0); gpu_buddy_reset_clear(&mm, false); KUNIT_EXPECT_EQ(test, mm.clear_avail, 0); + gpu_buddy_fini(&mm); /* - * Using a non-power-of-two mm size, allocate alternating blocks of 4KiB in an - * odd sequence and free them as cleared. All blocks should be marked as - * cleared and the split blocks should be merged back to their original - * size when the blocks clear reset function is called. + * Repeat the same fragmented setup, but this time call + * gpu_buddy_reset_clear(true). Every block should be marked cleared and + * the split blocks should be merged back to their original size, so the + * whole address space (clear_avail) must equal mm_size. */ - i = 0; - do { - if (i % 2 != 0) - KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, - ps, ps, &allocated, 0), - "buddy_alloc hit an error size=%lu\n", ps); - } while (++i < n_pages); + KUNIT_EXPECT_FALSE(test, gpu_buddy_init(&mm, mm_size, ps)); + KUNIT_EXPECT_EQ(test, mm.max_order, max_order); - gpu_buddy_free_list(&mm, &allocated, GPU_BUDDY_CLEARED); + for (i = 0; i < n_pages; i++) { + struct list_head *list = (i % 2) ? &clean : &dirty; + + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0, mm_size, + ps, ps, list, 0), + "buddy_alloc hit an error size=%lu\n", ps); + } + + gpu_buddy_free_list(&mm, &clean, GPU_BUDDY_CLEARED); + gpu_buddy_free_list(&mm, &dirty, 0); gpu_buddy_reset_clear(&mm, true); KUNIT_EXPECT_EQ(test, mm.clear_avail, mm_size); gpu_buddy_fini(&mm); @@ -1422,6 +1431,50 @@ static void gpu_test_buddy_alloc_exceeds_max_order(struct kunit *test) gpu_buddy_fini(&mm); } +static void gpu_test_buddy_addr_to_block(struct kunit *test) +{ + struct gpu_buddy_block *allocated_block, *found_block; + LIST_HEAD(allocated_list); + const u64 test_size = SZ_4M + SZ_2M; + const u64 alloc_start = SZ_4M; + const u64 alloc_size = SZ_4K; + const u64 chunk_size = SZ_4K; + struct gpu_buddy mm; + + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, test_size, chunk_size), + "buddy_init failed\n"); + + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, alloc_start, + alloc_start + alloc_size, + alloc_size, chunk_size, + &allocated_list, 0), + "buddy_alloc failed\n"); + + allocated_block = list_first_entry(&allocated_list, struct gpu_buddy_block, link); + KUNIT_EXPECT_EQ(test, gpu_buddy_block_offset(allocated_block), alloc_start); + KUNIT_EXPECT_EQ(test, gpu_buddy_block_size(&mm, allocated_block), alloc_size); + + found_block = gpu_buddy_allocated_addr_to_block(&mm, alloc_start); + KUNIT_EXPECT_PTR_EQ(test, found_block, allocated_block); + + /* Unaligned address inside the allocated block (should resolve to the same block) */ + found_block = gpu_buddy_allocated_addr_to_block(&mm, alloc_start + 16); + KUNIT_EXPECT_PTR_EQ(test, found_block, allocated_block); + + /* An unallocated address inside the manager should return NULL. */ + found_block = gpu_buddy_allocated_addr_to_block(&mm, + alloc_start - chunk_size); + KUNIT_EXPECT_NULL(test, found_block); + + /* An address outside the manager should return -ENXIO. */ + found_block = gpu_buddy_allocated_addr_to_block(&mm, test_size); + KUNIT_EXPECT_EQ(test, PTR_ERR(found_block), -ENXIO); + + /* 3. Standard inline cleanup flow */ + gpu_buddy_free_list(&mm, &allocated_list, 0); + gpu_buddy_fini(&mm); +} + static int gpu_buddy_suite_init(struct kunit_suite *suite) { while (!random_seed) @@ -1446,6 +1499,7 @@ static struct kunit_case gpu_buddy_tests[] = { KUNIT_CASE(gpu_test_buddy_alloc_exceeds_max_order), KUNIT_CASE(gpu_test_buddy_offset_aligned_allocation), KUNIT_CASE(gpu_test_buddy_subtree_offset_alignment_stress), + KUNIT_CASE(gpu_test_buddy_addr_to_block), {} }; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3ac84ed57ab2..58fff047f43b 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -40,9 +40,9 @@ struct drm_bridge; struct drm_bridge_timings; struct drm_connector; struct drm_display_info; +struct drm_edid; struct drm_minor; struct drm_panel; -struct edid; struct hdmi_codec_daifmt; struct hdmi_codec_params; struct i2c_adapter; diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index e09559495c5b..b23830494ed4 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -34,6 +34,7 @@ #include +struct dmem_cgroup_init; struct dmem_cgroup_region; struct drm_fb_helper; struct drm_fb_helper_surface_size; @@ -433,7 +434,8 @@ void *__devm_drm_dev_alloc(struct device *parent, struct dmem_cgroup_region * drmm_cgroup_register_region(struct drm_device *dev, - const char *region_name, u64 size); + const char *region_name, + const struct dmem_cgroup_init *init); /** * devm_drm_dev_alloc - Resource managed allocation of a &drm_device instance diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h index 8310bc3d55f9..32791c4db2a9 100644 --- a/include/drm/ttm/ttm_bo.h +++ b/include/drm/ttm/ttm_bo.h @@ -226,6 +226,11 @@ struct ttm_lru_walk_arg { struct ww_acquire_ctx *ticket; /** @trylock_only: Only use trylock for locking. */ bool trylock_only; + /** + * @sleeping_lock: Use sleeping locks even with %NULL @ticket. + * @trylock_only has precedence over this field. + */ + bool sleeping_lock; }; /** @@ -431,6 +436,11 @@ void ttm_bo_unpin(struct ttm_buffer_object *bo); int ttm_bo_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man, struct ttm_operation_ctx *ctx); +s64 ttm_bo_evict_cgroup(struct ttm_device *bdev, + struct ttm_resource_manager *man, + struct dmem_cgroup_pool_state *limit_pool, + s64 target_bytes, + struct ttm_operation_ctx *ctx); int ttm_bo_access(struct ttm_buffer_object *bo, unsigned long offset, void *buf, int len, int write); vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, diff --git a/include/drm/ttm/ttm_resource.h b/include/drm/ttm/ttm_resource.h index a5d386583fb6..3f2812743a7e 100644 --- a/include/drm/ttm/ttm_resource.h +++ b/include/drm/ttm/ttm_resource.h @@ -39,6 +39,7 @@ struct dentry; struct dmem_cgroup_device; +struct dmem_cgroup_region; struct drm_printer; struct ttm_device; struct ttm_resource_manager; @@ -458,10 +459,14 @@ void ttm_resource_init(struct ttm_buffer_object *bo, void ttm_resource_fini(struct ttm_resource_manager *man, struct ttm_resource *res); +int ttm_resource_try_charge(struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct dmem_cgroup_pool_state **ret_pool, + struct dmem_cgroup_pool_state **ret_limit_pool); int ttm_resource_alloc(struct ttm_buffer_object *bo, const struct ttm_place *place, struct ttm_resource **res, - struct dmem_cgroup_pool_state **ret_limit_pool); + struct dmem_cgroup_pool_state *charge_pool); void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res); bool ttm_resource_intersects(struct ttm_device *bdev, struct ttm_resource *res, @@ -477,6 +482,12 @@ void ttm_resource_manager_init(struct ttm_resource_manager *man, struct ttm_device *bdev, uint64_t size); +void ttm_resource_manager_set_dmem_region(struct ttm_resource_manager *man, + struct dmem_cgroup_region *region); + +int ttm_resource_manager_dmem_reclaim(struct dmem_cgroup_pool_state *pool, + u64 target_bytes, void *priv); + int ttm_resource_manager_evict_all(struct ttm_device *bdev, struct ttm_resource_manager *man); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f2aa46a4f871..83a17ded1c51 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -623,6 +623,27 @@ static inline struct cgroup *cgroup_ancestor(struct cgroup *cgrp, return cgrp->ancestors[ancestor_level]; } +/** + * cgroup_common_ancestor - find common ancestor of two cgroups + * @a: first cgroup to find common ancestor of + * @b: second cgroup to find common ancestor of + * + * Find the first cgroup that is an ancestor of both @a and @b, if it exists + * and return a pointer to it. If such a cgroup doesn't exist, return NULL. + * + * This function is safe to call as long as both @a and @b are accessible. + */ +static inline struct cgroup *cgroup_common_ancestor(struct cgroup *a, + struct cgroup *b) +{ + int level; + + for (level = min(a->level, b->level); level >= 0; level--) + if (a->ancestors[level] == b->ancestors[level]) + return a->ancestors[level]; + return NULL; +} + /** * task_under_cgroup_hierarchy - test task's membership of cgroup ancestry * @task: the task to be tested diff --git a/include/linux/cgroup_dmem.h b/include/linux/cgroup_dmem.h index dd4869f1d736..c6cad9501770 100644 --- a/include/linux/cgroup_dmem.h +++ b/include/linux/cgroup_dmem.h @@ -14,8 +14,40 @@ struct dmem_cgroup_pool_state; /* Opaque definition of a cgroup region, used internally */ struct dmem_cgroup_region; +/** + * struct dmem_cgroup_ops - Operations for a dmem cgroup region. + * @reclaim: Optional callback invoked when dmem.max is set below the current + * usage of a pool. The driver should attempt to free at least + * @target_bytes from @pool. May be called multiple times if usage + * remains above the limit after returning. + * + * Return: 0 if some progress was made (even if less than + * @target_bytes was freed), -ENOSPC if no progress could be made + * (the caller will retry up to a bounded number of times), or + * another negative error code if a fatal error occurred (stops + * further reclaim attempts immediately). + */ +struct dmem_cgroup_ops { + int (*reclaim)(struct dmem_cgroup_pool_state *pool, + u64 target_bytes, void *priv); +}; + +/** + * struct dmem_cgroup_init - Initialization parameters for a dmem cgroup region. + * @size: Size of the region in bytes. + * @ops: Optional operations for this region. May be NULL. + * @reclaim_priv: Opaque pointer passed to @ops->reclaim. May be NULL. + */ +struct dmem_cgroup_init { + u64 size; + const struct dmem_cgroup_ops *ops; + void *reclaim_priv; +}; + #if IS_ENABLED(CONFIG_CGROUP_DMEM) -struct dmem_cgroup_region *dmem_cgroup_register_region(u64 size, const char *name_fmt, ...) __printf(2,3); +struct dmem_cgroup_region * +dmem_cgroup_register_region(const struct dmem_cgroup_init *init, + const char *name_fmt, ...) __printf(2, 3); void dmem_cgroup_unregister_region(struct dmem_cgroup_region *region); int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size, struct dmem_cgroup_pool_state **ret_pool, @@ -24,11 +56,17 @@ void dmem_cgroup_uncharge(struct dmem_cgroup_pool_state *pool, u64 size); bool dmem_cgroup_state_evict_valuable(struct dmem_cgroup_pool_state *limit_pool, struct dmem_cgroup_pool_state *test_pool, bool ignore_low, bool *ret_hit_low); +bool dmem_cgroup_below_min(struct dmem_cgroup_pool_state *root, + struct dmem_cgroup_pool_state *test); +bool dmem_cgroup_below_low(struct dmem_cgroup_pool_state *root, + struct dmem_cgroup_pool_state *test); +struct dmem_cgroup_pool_state *dmem_cgroup_get_common_ancestor(struct dmem_cgroup_pool_state *a, + struct dmem_cgroup_pool_state *b); void dmem_cgroup_pool_state_put(struct dmem_cgroup_pool_state *pool); #else -static inline __printf(2,3) struct dmem_cgroup_region * -dmem_cgroup_register_region(u64 size, const char *name_fmt, ...) +static inline __printf(2, 3) struct dmem_cgroup_region * +dmem_cgroup_register_region(const struct dmem_cgroup_init *init, const char *name_fmt, ...) { return NULL; } @@ -59,6 +97,25 @@ bool dmem_cgroup_state_evict_valuable(struct dmem_cgroup_pool_state *limit_pool, return true; } +static inline bool dmem_cgroup_below_min(struct dmem_cgroup_pool_state *root, + struct dmem_cgroup_pool_state *test) +{ + return false; +} + +static inline bool dmem_cgroup_below_low(struct dmem_cgroup_pool_state *root, + struct dmem_cgroup_pool_state *test) +{ + return false; +} + +static inline +struct dmem_cgroup_pool_state *dmem_cgroup_get_common_ancestor(struct dmem_cgroup_pool_state *a, + struct dmem_cgroup_pool_state *b) +{ + return NULL; +} + static inline void dmem_cgroup_pool_state_put(struct dmem_cgroup_pool_state *pool) { } diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h index e037714563d8..2c36124bb696 100644 --- a/include/linux/gpu_buddy.h +++ b/include/linux/gpu_buddy.h @@ -287,6 +287,8 @@ void gpu_buddy_reset_clear(struct gpu_buddy *mm, bool is_clear); void gpu_buddy_free_block(struct gpu_buddy *mm, struct gpu_buddy_block *block); +struct gpu_buddy_block *gpu_buddy_allocated_addr_to_block(struct gpu_buddy *mm, u64 addr); + void gpu_buddy_free_list(struct gpu_buddy *mm, struct list_head *objects, unsigned int flags); diff --git a/include/uapi/drm/nouveau_drm.h b/include/uapi/drm/nouveau_drm.h index 1fa82fa6af38..c4905b3e4e91 100644 --- a/include/uapi/drm/nouveau_drm.h +++ b/include/uapi/drm/nouveau_drm.h @@ -89,6 +89,7 @@ struct drm_nouveau_getparam { #define NOUVEAU_FIFO_ENGINE_PPP 0x04 #define NOUVEAU_FIFO_ENGINE_BSP 0x08 #define NOUVEAU_FIFO_ENGINE_CE 0x30 +#define NOUVEAU_FIFO_ENGINE_NVDEC 0x300 struct drm_nouveau_channel_alloc { __u32 fb_ctxdma_handle; diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c index 39930c59cb76..4683f3d68022 100644 --- a/kernel/cgroup/dmem.c +++ b/kernel/cgroup/dmem.c @@ -17,6 +17,13 @@ #include #include #include +#include + +/* Maximum reclaim attempts before giving up when lowering dmem.max. */ +#define DMEM_MAX_RECLAIM_RETRIES 16 + +/* SRCU domain serialising reclaim callbacks against region unregistration. */ +DEFINE_STATIC_SRCU(dmemcg_srcu); struct dmem_cgroup_region { /** @@ -48,9 +55,18 @@ struct dmem_cgroup_region { /** * @unregistered: Whether the region is unregistered by its caller. - * No new pools should be added to the region afterwards. + * No new pools should be added to the region afterwards, and no new + * reclaim callbacks should be invoked. */ bool unregistered; + + /** + * @ops: Optional driver operations for this region. + */ + const struct dmem_cgroup_ops *ops; + + /** @reclaim_priv: Private data passed to @ops->reclaim. */ + void *reclaim_priv; }; struct dmemcg_state { @@ -145,21 +161,52 @@ static void free_cg_pool(struct dmem_cgroup_pool_state *pool) } static void -set_resource_min(struct dmem_cgroup_pool_state *pool, u64 val) +set_resource_min(struct dmem_cgroup_pool_state *pool, u64 val, bool nonblock) { page_counter_set_min(&pool->cnt, val); } static void -set_resource_low(struct dmem_cgroup_pool_state *pool, u64 val) +set_resource_low(struct dmem_cgroup_pool_state *pool, u64 val, bool nonblock) { page_counter_set_low(&pool->cnt, val); } static void -set_resource_max(struct dmem_cgroup_pool_state *pool, u64 val) +set_resource_max(struct dmem_cgroup_pool_state *pool, u64 val, bool nonblock) { - page_counter_set_max(&pool->cnt, val); + struct dmem_cgroup_region *region = pool->region; + unsigned long limit = (unsigned long)val; + + /* Apply the new limit immediately so concurrent allocations are throttled. */ + xchg(&pool->cnt.max, limit); + + if (nonblock) + return; + + int srcu_idx = srcu_read_lock(&dmemcg_srcu); + + if (!READ_ONCE(region->unregistered) && region->ops && region->ops->reclaim) { + for (int retries = DMEM_MAX_RECLAIM_RETRIES; ; ) { + u64 usage = page_counter_read(&pool->cnt); + int ret; + + if (usage <= limit) + break; + + if (signal_pending(current)) + break; + + ret = region->ops->reclaim(pool, usage - limit, region->reclaim_priv); + + /* -ENOSPC means no progress; other errors are fatal. */ + if (ret && (ret != -ENOSPC || !retries--)) + break; + + cond_resched(); + } + } + srcu_read_unlock(&dmemcg_srcu, srcu_idx); } static u64 get_resource_low(struct dmem_cgroup_pool_state *pool) @@ -189,9 +236,10 @@ static u64 get_resource_peak(struct dmem_cgroup_pool_state *pool) static void reset_all_resource_limits(struct dmem_cgroup_pool_state *rpool) { - set_resource_min(rpool, 0); - set_resource_low(rpool, 0); - set_resource_max(rpool, PAGE_COUNTER_MAX); + set_resource_min(rpool, 0, false); + set_resource_low(rpool, 0, false); + /* nonblock: raising to max makes reclaim a no-op; sleeping is forbidden here. */ + set_resource_max(rpool, PAGE_COUNTER_MAX, true); } static void dmemcs_offline(struct cgroup_subsys_state *css) @@ -468,7 +516,10 @@ static void dmemcg_free_region(struct kref *ref) * dmem_cgroup_unregister_region() - Unregister a previously registered region. * @region: The region to unregister. * - * This function undoes dmem_cgroup_register_region. + * This function undoes dmem_cgroup_register_region. It drains any + * in-flight reclaim callbacks before returning, so the caller may safely + * free the resources pointed to by the @reclaim_priv that was passed at + * registration time. */ void dmem_cgroup_unregister_region(struct dmem_cgroup_region *region) { @@ -493,16 +544,18 @@ void dmem_cgroup_unregister_region(struct dmem_cgroup_region *region) * no new pools should be added to the dead region * by get_cg_pool_unlocked. */ - region->unregistered = true; + WRITE_ONCE(region->unregistered, true); spin_unlock(&dmemcg_lock); + synchronize_srcu(&dmemcg_srcu); + kref_put(®ion->ref, dmemcg_free_region); } EXPORT_SYMBOL_GPL(dmem_cgroup_unregister_region); /** * dmem_cgroup_register_region() - Register a regions for dev cgroup. - * @size: Size of region to register, in bytes. + * @init: Initialization parameters for the region. * @fmt: Region parameters to register * * This function registers a node in the dmem cgroup with the @@ -511,13 +564,15 @@ EXPORT_SYMBOL_GPL(dmem_cgroup_unregister_region); * * Return: NULL or a struct on success, PTR_ERR on failure. */ -struct dmem_cgroup_region *dmem_cgroup_register_region(u64 size, const char *fmt, ...) +struct dmem_cgroup_region * +dmem_cgroup_register_region(const struct dmem_cgroup_init *init, + const char *fmt, ...) { struct dmem_cgroup_region *ret; char *region_name; va_list ap; - if (!size) + if (!init || !init->size) return NULL; va_start(ap, fmt); @@ -534,7 +589,9 @@ struct dmem_cgroup_region *dmem_cgroup_register_region(u64 size, const char *fmt INIT_LIST_HEAD(&ret->pools); ret->name = region_name; - ret->size = size; + ret->size = init->size; + ret->ops = init->ops; + ret->reclaim_priv = init->reclaim_priv; kref_init(&ret->ref); spin_lock(&dmemcg_lock); @@ -700,6 +757,110 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size, } EXPORT_SYMBOL_GPL(dmem_cgroup_try_charge); +/** + * dmem_cgroup_below_min() - Tests whether current usage is within min limit. + * + * @root: Root of the subtree to calculate protection for, or NULL to calculate global protection. + * @test: The pool to test the usage/min limit of. + * + * Return: true if usage is below min and the cgroup is protected, false otherwise. + */ +bool dmem_cgroup_below_min(struct dmem_cgroup_pool_state *root, + struct dmem_cgroup_pool_state *test) +{ + if (root == test || !pool_parent(test)) + return false; + + if (!root) { + for (root = test; pool_parent(root); root = pool_parent(root)) + {} + } + + /* + * In mem_cgroup_below_min(), the memcg pendant, this call is missing. + * mem_cgroup_below_min() gets called during traversal of the cgroup tree, where + * protection is already calculated as part of the traversal. dmem cgroup eviction + * does not traverse the cgroup tree, so we need to recalculate effective protection + * here. + */ + dmem_cgroup_calculate_protection(root, test); + return page_counter_read(&test->cnt) <= READ_ONCE(test->cnt.emin); +} +EXPORT_SYMBOL_GPL(dmem_cgroup_below_min); + +/** + * dmem_cgroup_below_low() - Tests whether current usage is within low limit. + * + * @root: Root of the subtree to calculate protection for, or NULL to calculate global protection. + * @test: The pool to test the usage/low limit of. + * + * Return: true if usage is below low and the cgroup is protected, false otherwise. + */ +bool dmem_cgroup_below_low(struct dmem_cgroup_pool_state *root, + struct dmem_cgroup_pool_state *test) +{ + if (root == test || !pool_parent(test)) + return false; + + if (!root) { + for (root = test; pool_parent(root); root = pool_parent(root)) + {} + } + + /* + * In mem_cgroup_below_low(), the memcg pendant, this call is missing. + * mem_cgroup_below_low() gets called during traversal of the cgroup tree, where + * protection is already calculated as part of the traversal. dmem cgroup eviction + * does not traverse the cgroup tree, so we need to recalculate effective protection + * here. + */ + dmem_cgroup_calculate_protection(root, test); + return page_counter_read(&test->cnt) <= READ_ONCE(test->cnt.elow); +} +EXPORT_SYMBOL_GPL(dmem_cgroup_below_low); + +/** + * dmem_cgroup_get_common_ancestor(): Find the first common ancestor of two pools. + * @a: First pool to find the common ancestor of. + * @b: First pool to find the common ancestor of. + * + * Return: The first pool that is a parent of both @a and @b, or NULL if either @a or @b are NULL, + * or if such a pool does not exist. A reference to the returned pool is grabbed and must be + * released by the caller when it is done using the pool. + */ +struct dmem_cgroup_pool_state *dmem_cgroup_get_common_ancestor(struct dmem_cgroup_pool_state *a, + struct dmem_cgroup_pool_state *b) +{ + struct cgroup *ancestor_cgroup; + struct cgroup_subsys_state *ancestor_css; + struct dmemcg_state *ancestor_dmemcs = NULL; + struct dmem_cgroup_pool_state *pool = NULL; + + if (!a || !b) + return NULL; + + ancestor_cgroup = cgroup_common_ancestor(a->cs->css.cgroup, b->cs->css.cgroup); + if (!ancestor_cgroup) + return NULL; + + rcu_read_lock(); + ancestor_css = cgroup_e_css(ancestor_cgroup, &dmem_cgrp_subsys); + if (css_tryget(ancestor_css)) + ancestor_dmemcs = css_to_dmemcs(ancestor_css); + rcu_read_unlock(); + + if (ancestor_dmemcs) { + pool = get_cg_pool_unlocked(css_to_dmemcs(ancestor_css), + a->region); + if (WARN_ON(IS_ERR(pool))) { + pool = NULL; + css_put(ancestor_css); + } + } + return pool; +} +EXPORT_SYMBOL_GPL(dmem_cgroup_get_common_ancestor); + static int dmem_cgroup_region_capacity_show(struct seq_file *sf, void *v) { struct dmem_cgroup_region *region; @@ -731,11 +892,12 @@ static int dmemcg_parse_limit(char *options, u64 *new_limit) static ssize_t dmemcg_limit_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off, - void (*apply)(struct dmem_cgroup_pool_state *, u64)) + void (*apply)(struct dmem_cgroup_pool_state *, u64, bool)) { struct dmemcg_state *dmemcs = css_to_dmemcs(of_css(of)); struct dmem_cgroup_pool_state *pool; struct dmem_cgroup_region *region; + bool nonblock = of->file->f_flags & O_NONBLOCK; char *region_name; u64 new_limit; int err; @@ -761,7 +923,7 @@ static ssize_t dmemcg_limit_write(struct kernfs_open_file *of, goto out_put; } - apply(pool, new_limit); + apply(pool, new_limit, nonblock); dmemcg_pool_put(pool); out_put: