diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 993446ab66d0..f0e6bce3f162 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2861,6 +2861,12 @@ DMEM Interface Files The semantics are the same as for the memory cgroup controller, and are calculated in the same way. + dmem.peak + A read-only nested-keyed file that exists on non-root cgroups. + + The max device memory usage recorded for the cgroup and its + descendants since the creation of the cgroup for each region. + dmem.capacity A read-only file that describes maximum region capacity. It only exists on the root cgroup. Not all memory can be diff --git a/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml new file mode 100644 index 000000000000..f820dc2f732b --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/anbernic,td4310.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Anbernic TD4310 Based Panels + +maintainers: + - Chris Morgan + +description: + Anbernic TD4310 Based Panels, such as the RG-Vita-Pro panel + (a 1080x1920 5.5 inch panel). + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - anbernic,panel-vita-pro + - const: anbernic,td4310 + + reg: + maxItems: 1 + + vdd-supply: + description: Panel power supply + +required: + - compatible + - port + - reg + - reset-gpios + - vdd-supply + +unevaluatedProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "anbernic,panel-vita-pro", "anbernic,td4310"; + reg = <0>; + backlight = <&backlight>; + enable-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + rotation = <270>; + vdd-supply = <&vdd_lcd>; + + port { + endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/chipone,icna3512.yaml b/Documentation/devicetree/bindings/display/panel/chipone,icna3512.yaml new file mode 100644 index 000000000000..90e69f30cd91 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/chipone,icna3512.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/chipone,icna3512.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Chipone ICNA3512 and ICNA3520 display drivers + +maintainers: + - Neil Armstrong + +description: + The Chipone ICNA3512 and ICNA3520 are DDICs connected + using a MIPI-DSI video interface. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - ayaneo,pocketds-panel-top + - ayntec,odin2portal-panel + - const: chipone,icna3512 + + - items: + - enum: + - ayntec,odin3-panel + - ayntec,thor-panel-top + - const: chipone,icna3520 + + reg: + maxItems: 1 + description: DSI virtual channel + + vdd-supply: true + vddio-supply: true + vci-supply: true + disp-supply: true + blvdd-supply: true + + port: true + reset-gpios: true + rotation: true + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + - vci-supply + - disp-supply + - blvdd-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "ayntec,odin2portal-panel", "chipone,icna3512"; + reg = <0>; + vdd-supply = <&vreg_l11b_1p2>; + vddio-supply = <&vreg_l12b_1p8>; + vci-supply = <&vreg_l13b_3p0>; + disp-supply = <&vdd_disp_2v8>; + blvdd-supply = <&vdd_bl_5v0>; + reset-gpios = <&tlmm 133 GPIO_ACTIVE_LOW>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml b/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml index e067a2f6d0b2..aeca3c9a599c 100644 --- a/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml +++ b/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml @@ -40,6 +40,9 @@ properties: vddi-supply: description: power supply for IC + bl-supply: + description: power supply for backlight, in case it's managed via DSC + backlight: true ports: true diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml new file mode 100644 index 000000000000..ea7449273022 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/ilitek,ili9488.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI9488 based MIPI-DSI panels + +maintainers: + - Igor Reznichenko + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - focuslcds,e35gh-i-mw800cb + - const: ilitek,ili9488 + + reg: + maxItems: 1 + + vci-supply: true + iovcc-supply: true + +required: + - compatible + - reg + - vci-supply + - iovcc-supply + - reset-gpios + - backlight + - port + +unevaluatedProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "focuslcds,e35gh-i-mw800cb", "ilitek,ili9488"; + reg = <0>; + vci-supply = <®_vci_panel>; + iovcc-supply = <®_iovcc_panel>; + reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; + backlight = <&pwm_bl>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml index 1bbe0da3997c..27536eeaed85 100644 --- a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml +++ b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml @@ -25,6 +25,8 @@ properties: - samsung,atna40ct06 # Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel - samsung,atna40cu11 + # Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel + - samsung,atna40hq08 # Samsung 14" WQXGA+ (2880×1800 pixels) eDP AMOLED panel - samsung,atna40yk20 # Samsung 14.5" WQXGA+ (2880x1800 pixels) eDP AMOLED panel diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml index d99b23b88cc5..6548f157fd96 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -15,18 +15,24 @@ properties: enum: - rockchip,rk3288-dp - rockchip,rk3399-edp + - rockchip,rk3576-edp - rockchip,rk3588-edp clocks: minItems: 2 - maxItems: 3 + items: + - description: Reference clock + - description: APB bus clock + - description: GRF or AHB bus clock clock-names: minItems: 2 items: - const: dp - const: pclk - - const: grf + - enum: + - grf + - hclk power-domains: maxItems: 1 @@ -65,9 +71,46 @@ allOf: compatible: contains: enum: + - rockchip,rk3288-dp + then: + properties: + clocks: + maxItems: 2 + clock-names: + maxItems: 2 + + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3399-edp + then: + properties: + clocks: + minItems: 3 + clock-names: + items: + - const: dp + - const: pclk + - const: grf + + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3576-edp - rockchip,rk3588-edp then: properties: + clocks: + minItems: 3 + clock-names: + items: + - const: dp + - const: pclk + - const: hclk resets: minItems: 2 reset-names: diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 396044f368e7..b1af9deac2e9 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -225,6 +225,8 @@ patternProperties: description: Axis Communications AB "^ayaneo,.*": description: Anyun Intelligent Technology (Hong Kong) Co., Ltd + "^ayntec,.*": + description: AYN Technologies Co., Ltd. "^azoteq,.*": description: Azoteq (Pty) Ltd "^azw,.*": @@ -604,6 +606,8 @@ patternProperties: description: Flipkart Inc. "^focaltech,.*": description: FocalTech Systems Co.,Ltd + "^focuslcds,.*": + description: Focus Display Solutions, Inc. "^forlinx,.*": description: Baoding Forlinx Embedded Technology Co., Ltd. "^foursemi,.*": @@ -1411,6 +1415,8 @@ patternProperties: description: Embest RIoT "^riscv,.*": description: RISC-V Foundation + "^riverdi,.*": + description: Riverdi Sp. z o.o "^rockchip,.*": description: Rockchip Electronics Co., Ltd. "^rocktech,.*": diff --git a/Documentation/gpu/automated_testing.rst b/Documentation/gpu/automated_testing.rst index 62aa3ede02a5..8a7328aef10e 100644 --- a/Documentation/gpu/automated_testing.rst +++ b/Documentation/gpu/automated_testing.rst @@ -99,7 +99,8 @@ How to enable automated testing on your tree ============================================ 1. Create a Linux tree in https://gitlab.freedesktop.org/ if you don't have one -yet +yet, by forking https://gitlab.freedesktop.org/drm/kernel (this allows GitLab +to internally track that these are the same git objects). 2. In your kernel repo's configuration (eg. https://gitlab.freedesktop.org/janedoe/linux/-/settings/ci_cd), change the diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 80453dda33b8..94cfc26acecc 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -171,6 +171,12 @@ Bridge Operations .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :doc: bridge operations +Bridge Chain Format Selection +----------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_bridge.c + :doc: bridge chain format selection + Bridge Connector Helper ----------------------- diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index fa69a5450f96..0dd440a14946 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -287,6 +287,12 @@ structure, ordering of committing state changes to hardware is sequenced using Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed coverage of specific topics. +Atomic State Lifetime +--------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_atomic.c + :doc: state lifetime + Handling Driver Private State ----------------------------- @@ -604,6 +610,12 @@ Color Management Properties .. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c :doc: overview +Color Format Property +--------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_connector.c + :doc: Color format + Tile Group Property ------------------- diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index cdddf8db35f5..e3d169d42f19 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -55,7 +55,7 @@ There are still drivers that use drm_simple_display_pipe. The task here is to convert them to use regular atomic helpers. Search for a driver that calls drm_simple_display_pipe_init() and inline all helpers from drm_simple_kms_helper.c into the driver, such that no simple-KMS interfaces are required. Please also -rename all inlined fucntions according to driver conventions. +rename all inlined functions according to driver conventions. Contact: Thomas Zimmermann, respective driver maintainer @@ -948,6 +948,47 @@ Contact: Philipp Stanner Level: Intermediate +Replace the lockless queue with a locked list +--------------------------------------------- + +drm_sched is the only user in the entire kernel of a special lockless queue, the +spsc_queue. This queue utilizes: + +- preempt_disable() +- atomic instructions +- memory barriers +- ACCESS_ONCE() + +whereas a conventional spinlock utilizes: + +- preempt_disable() +- 1 atomic instruction for taking / releasing the lock +- memory barriers + +Moreover, drm_sched_entity_push_job(), the only user of spsc_queue_push(), has +to take a lock in some situations anyways and calls to it are often serialized +with a driver lock. + +It is, thus, highly questionable whether the lockless queue grants any advantage +at all. Considering that its internals are not well documented and its correctness +is not formally proven, it seems desirable to replace the queue with a mere list +or hlist that is protected by a spinlock. + +Tasks: + +- Replace the spsc_queue in drm/sched (and those who might access the scheduler's + internal queue) with a spinlock + (h)list. +- Ideally, check with some micro benchmarks and real world tests (preferably + with amdgpu) for relevant performance regressions. +- Remove the spsc_queue from the kernel altogether. + +Contact: + +- Philipp Stanner +- Christian König + +Level: Beginner + Outside DRM =========== diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b0fd665ce..4986208396f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8120,6 +8120,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/ilitek,ili9486.yaml F: drivers/gpu/drm/tiny/ili9486.c +DRM DRIVER FOR ILITEK ILI9488 PANELS +M: Igor Reznichenko +S: Maintained +F: Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml +F: drivers/gpu/drm/panel/panel-ilitek-ili9488.c + DRM DRIVER FOR ILITEK ILI9805 PANELS M: Michael Trimarchi S: Maintained diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 891112c2cddf..1f190b319bb6 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -731,9 +731,15 @@ static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map return 0; } +static struct dma_buf *amdxdna_gem_dev_obj_export(struct drm_gem_object *gobj, int flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = { .free = amdxdna_gem_dev_obj_free, .vmap = amdxdna_gem_dev_obj_vmap, + .export = amdxdna_gem_dev_obj_export, }; static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { diff --git a/drivers/accel/ethosu/Makefile b/drivers/accel/ethosu/Makefile index 17db5a600416..598a388b7179 100644 --- a/drivers/accel/ethosu/Makefile +++ b/drivers/accel/ethosu/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o -ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o +ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o ethosu_perfmon.o diff --git a/drivers/accel/ethosu/ethosu_device.h b/drivers/accel/ethosu/ethosu_device.h index b189fa783d6a..d4458eac8447 100644 --- a/drivers/accel/ethosu/ethosu_device.h +++ b/drivers/accel/ethosu/ethosu_device.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -43,6 +44,15 @@ struct gen_pool; #define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8) #define NPU_BASEP_REGION_MAX 8 +#define NPU_REG_PMCR 0x0180 +#define NPU_REG_PMCNTENSET 0x0184 +#define NPU_REG_PMCNTENCLR 0x0188 +#define NPU_REG_PMCCNTR_LO 0x01A0 +#define NPU_REG_PMCCNTR_HI 0x01A4 +#define NPU_REG_PMCCNTR_CFG 0x01A8 +#define NPU_REG_PMU_EVCNTR(x) (0x0300 + (x) * 4) +#define NPU_REG_PMU_EVTYPER(x) (0x0380 + (x) * 4) + #define ID_ARCH_MAJOR_MASK GENMASK(31, 28) #define ID_ARCH_MINOR_MASK GENMASK(27, 20) #define ID_ARCH_PATCH_MASK GENMASK(19, 16) @@ -67,6 +77,15 @@ struct gen_pool; #define PROT_ACTIVE_CSL BIT(1) +#define PMCR_NUM_EVENT_CNT_MASK GENMASK(15, 11) +#define PMCR_CYCLE_CNT_RST BIT(2) +#define PMCR_EVENT_CNT_RST BIT(1) +#define PMCR_CNT_EN BIT(0) + +#define PMU_EV_TYPE_NONE 0 +#define PMU_EV_TYPE_CYCLES 0x11 +#define PMU_EV_TYPE_IDLE 0x20 + enum ethosu_cmds { NPU_OP_CONV = 0x2, NPU_OP_DEPTHWISE = 0x3, @@ -152,6 +171,8 @@ enum ethosu_cmds { #define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */ +struct ethosu_perfmon; + /** * struct ethosu_device - Ethosu device */ @@ -161,6 +182,7 @@ struct ethosu_device { /** @iomem: CPU mapping of the registers. */ void __iomem *regs; + void __iomem *pmu_regs; void __iomem *sram; struct gen_pool *srampool; @@ -173,8 +195,6 @@ struct ethosu_device { struct drm_ethosu_npu_info npu_info; struct ethosu_job *in_flight_job; - /* For in_flight_job and ethosu_job_hw_submit() */ - struct mutex job_lock; /* For dma_fence */ spinlock_t fence_lock; @@ -184,6 +204,17 @@ struct ethosu_device { struct mutex sched_lock; u64 fence_context; u64 emit_seqno; + + /* Tracks the performance monitor state. */ + struct { + /* Protects @active. */ + struct mutex lock; + + /* Perfmon currently programmed in HW (or NULL if none). */ + struct ethosu_perfmon *active; + } perfmon_state; + + struct ethosu_perfmon *global_perfmon; }; #define to_ethosu_device(drm_dev) \ diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c index ed9c748a54ad..d121fb0d7732 100644 --- a/drivers/accel/ethosu/ethosu_drv.c +++ b/drivers/accel/ethosu/ethosu_drv.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "ethosu_drv.h" @@ -154,6 +155,7 @@ static int ethosu_open(struct drm_device *ddev, struct drm_file *file) if (ret) goto err_put_mod; + ethosu_perfmon_open_file(priv); file->driver_priv = no_free_ptr(priv); return 0; @@ -165,6 +167,7 @@ static int ethosu_open(struct drm_device *ddev, struct drm_file *file) static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file) { ethosu_job_close(file->driver_priv); + ethosu_perfmon_close_file(file->driver_priv); kfree(file->driver_priv); module_put(THIS_MODULE); } @@ -179,6 +182,10 @@ static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = { ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0), ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0), ETHOSU_IOCTL(SUBMIT, submit, 0), + ETHOSU_IOCTL(PERFMON_CREATE, perfmon_create, 0), + ETHOSU_IOCTL(PERFMON_DESTROY, perfmon_destroy, 0), + ETHOSU_IOCTL(PERFMON_GET_VALUES, perfmon_get_values, 0), + ETHOSU_IOCTL(PERFMON_SET_GLOBAL, perfmon_set_global, 0), }; DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops); @@ -314,8 +321,14 @@ static int ethosu_init(struct ethosu_device *ethosudev) ethosu_sram_init(ethosudev); + if (!ethosu_is_u65(ethosudev)) + ethosudev->pmu_regs += 0x1000; + + ethosudev->npu_info.pmu_counters = FIELD_GET(PMCR_NUM_EVENT_CNT_MASK, + readl_relaxed(ethosudev->pmu_regs + NPU_REG_PMCR)); + dev_info(ethosudev->base.dev, - "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n", + "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM, %d PMU cntrs\n", FIELD_GET(ID_ARCH_MAJOR_MASK, id), FIELD_GET(ID_ARCH_MINOR_MASK, id), FIELD_GET(ID_ARCH_PATCH_MASK, id), @@ -323,7 +336,8 @@ static int ethosu_init(struct ethosu_device *ethosudev) FIELD_GET(ID_VER_MINOR_MASK, id), FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config), 1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config), - ethosudev->npu_info.sram_size / 1024); + ethosudev->npu_info.sram_size / 1024, + ethosudev->npu_info.pmu_counters); return 0; } @@ -342,11 +356,16 @@ static int ethosu_probe(struct platform_device *pdev) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); ethosudev->regs = devm_platform_ioremap_resource(pdev, 0); + ethosudev->pmu_regs = ethosudev->regs; ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, ðosudev->clks); if (ethosudev->num_clks < 0) return ethosudev->num_clks; + ret = drmm_mutex_init(ðosudev->base, ðosudev->perfmon_state.lock); + if (ret) + return ret; + ret = ethosu_job_init(ethosudev); if (ret) return ret; diff --git a/drivers/accel/ethosu/ethosu_drv.h b/drivers/accel/ethosu/ethosu_drv.h index 9e21dfe94184..2193bc51d425 100644 --- a/drivers/accel/ethosu/ethosu_drv.h +++ b/drivers/accel/ethosu/ethosu_drv.h @@ -1,15 +1,74 @@ /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ -/* Copyright 2025 Arm, Ltd. */ +/* Copyright 2025-2026 Arm, Ltd. */ #ifndef __ETHOSU_DRV_H__ #define __ETHOSU_DRV_H__ +#include +#include #include struct ethosu_device; +struct drm_device; +struct drm_file; struct ethosu_file_priv { struct ethosu_device *edev; struct drm_sched_entity sched_entity; + struct xarray perfmons; }; +/* Performance monitor object. The perfmon lifetime is controlled by userspace + * using perfmon related ioctls. A perfmon can be attached to a DRM_ETHOSU_SUBMIT + * request, and when this is the case, HW perf counters will be activated just + * before the job is submitted to the NPU and disabled when the job is + * done. This way, only events related to a specific job will be counted. + */ +struct ethosu_perfmon { + /* Tracks the number of users of the perfmon, when this counter reaches + * zero the perfmon is destroyed. + */ + refcount_t refcnt; + + /* Number of counters activated in this perfmon instance + * (should be less than or equal to DRM_ETHOSU_MAX_PERF_COUNTERS). + */ + u8 ncounters; + + /* Events counted by the HW perf counters. */ + u16 counters[DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS]; + + /* + * Storage for counter values. Counters are incremented by the HW + * perf counter values every time the perfmon is attached to an + * NPU job. This way, perfmon users don't have to retrieve the + * results after each job if they want to track events covering + * several submissions. Note that counter values can't be reset, + * but you can fake a reset by destroying the perfmon and + * creating a new one. + */ + u64 values[] __counted_by(ncounters); +}; + +/* ethosu_perfmon.c */ +void ethosu_perfmon_get(struct ethosu_perfmon *perfmon); +void ethosu_perfmon_put(struct ethosu_perfmon *perfmon); +void ethosu_perfmon_start(struct ethosu_device *ethosu, + struct ethosu_perfmon *perfmon); +void ethosu_perfmon_stop(struct ethosu_device *ethosu, + struct ethosu_perfmon *perfmon, bool capture); +void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture); +struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, + int id); +void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv); +void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv); +int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data, + struct drm_file *file_priv); + #endif diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c index b76924645aaa..74f3493fb11a 100644 --- a/drivers/accel/ethosu/ethosu_job.c +++ b/drivers/accel/ethosu/ethosu_job.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2024-2025 Tomeu Vizoso */ -/* Copyright 2025 Arm, Ltd. */ +/* Copyright 2025-2026 Arm, Ltd. */ #include #include @@ -147,6 +147,8 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job) { unsigned int i; + ethosu_perfmon_put(job->perfmon); + for (i = 0; i < job->region_cnt; i++) drm_gem_object_put(job->region_bo[i]); @@ -181,6 +183,26 @@ static void ethosu_job_free(struct drm_sched_job *sched_job) ethosu_job_put(job); } +static void +ethosu_switch_perfmon(struct ethosu_device *ethosu, struct ethosu_job *job) +{ + struct ethosu_perfmon *perfmon; + + guard(mutex)(ðosu->perfmon_state.lock); + + perfmon = ethosu->global_perfmon; + if (!perfmon) + perfmon = job->perfmon; + + if (perfmon == ethosu->perfmon_state.active) + return; + + ethosu_perfmon_stop_locked(ethosu, ethosu->perfmon_state.active, true); + + if (perfmon) + ethosu_perfmon_start(ethosu, perfmon); +} + static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) { struct ethosu_job *job = to_ethosu_job(sched_job); @@ -194,10 +216,10 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) dev->fence_context, ++dev->emit_seqno); dma_fence_get(fence); - scoped_guard(mutex, &dev->job_lock) { - dev->in_flight_job = job; - ethosu_job_hw_submit(dev, job); - } + ethosu_switch_perfmon(dev, job); + + WRITE_ONCE(dev->in_flight_job, job); + ethosu_job_hw_submit(dev, job); return fence; } @@ -205,6 +227,7 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) static void ethosu_job_handle_irq(struct ethosu_device *dev) { u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS); + struct ethosu_job *job; if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) { dev_err(dev->base.dev, "Error IRQ - %x\n", status); @@ -212,11 +235,10 @@ static void ethosu_job_handle_irq(struct ethosu_device *dev) return; } - scoped_guard(mutex, &dev->job_lock) { - if (dev->in_flight_job) { - dma_fence_signal(dev->in_flight_job->done_fence); - dev->in_flight_job = NULL; - } + job = READ_ONCE(dev->in_flight_job); + if (job) { + WRITE_ONCE(dev->in_flight_job, NULL); + dma_fence_signal(job->done_fence); } } @@ -272,8 +294,7 @@ static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad) drm_sched_stop(&dev->sched, bad); - scoped_guard(mutex, &dev->job_lock) - dev->in_flight_job = NULL; + WRITE_ONCE(dev->in_flight_job, NULL); /* Proceed with reset now. */ pm_runtime_force_suspend(dev->base.dev); @@ -304,9 +325,6 @@ int ethosu_job_init(struct ethosu_device *edev) int ret; spin_lock_init(&edev->fence_lock); - ret = devm_mutex_init(dev, &edev->job_lock); - if (ret) - return ret; ret = devm_mutex_init(dev, &edev->sched_lock); if (ret) return ret; @@ -365,7 +383,8 @@ void ethosu_job_close(struct ethosu_file_priv *ethosu_priv) } static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file, - struct drm_ethosu_job *job) + struct drm_ethosu_job *job, + int perfmon_id) { struct ethosu_device *edev = to_ethosu_device(dev); struct ethosu_file_priv *file_priv = file->driver_priv; @@ -389,6 +408,9 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file ejob->dev = edev; ejob->sram_size = job->sram_size; + if (perfmon_id) + ejob->perfmon = ethosu_perfmon_find(file_priv, perfmon_id); + ejob->done_fence = kzalloc_obj(*ejob->done_fence); if (!ejob->done_fence) { ret = -ENOMEM; @@ -491,11 +513,6 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil int ret = 0; unsigned int i = 0; - if (args->pad) { - drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n"); - return -EINVAL; - } - struct drm_ethosu_job __free(kvfree) *jobs = kvmalloc_objs(*jobs, args->job_count); if (!jobs) @@ -509,7 +526,7 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil } for (i = 0; i < args->job_count; i++) { - ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]); + ret = ethosu_ioctl_submit_job(dev, file, &jobs[i], args->perfmon_id); if (ret) return ret; } diff --git a/drivers/accel/ethosu/ethosu_job.h b/drivers/accel/ethosu/ethosu_job.h index ff1cf448d094..8988edd00eed 100644 --- a/drivers/accel/ethosu/ethosu_job.h +++ b/drivers/accel/ethosu/ethosu_job.h @@ -21,6 +21,8 @@ struct ethosu_job { u8 region_cnt; u32 sram_size; + struct ethosu_perfmon *perfmon; + /* Fence to be signaled by drm-sched once its done with the job */ struct dma_fence *inference_done_fence; diff --git a/drivers/accel/ethosu/ethosu_perfmon.c b/drivers/accel/ethosu/ethosu_perfmon.c new file mode 100644 index 000000000000..26f625374f9d --- /dev/null +++ b/drivers/accel/ethosu/ethosu_perfmon.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2026 Arm, Ltd. */ +/* Based on v3d_perfmon.c, Copyright (C) 2021 Raspberry Pi */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "ethosu_drv.h" +#include "ethosu_device.h" + +void ethosu_perfmon_get(struct ethosu_perfmon *perfmon) +{ + if (perfmon) + refcount_inc(&perfmon->refcnt); +} + +void ethosu_perfmon_put(struct ethosu_perfmon *perfmon) +{ + if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) + kfree(perfmon); +} + +void ethosu_perfmon_start(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon) +{ + unsigned int i; + u8 ncounters; + u32 mask; + + lockdep_assert_held(ðosu->perfmon_state.lock); + + if (WARN_ON_ONCE(!perfmon || ethosu->perfmon_state.active)) + return; + + writel_relaxed(PMCR_CNT_EN, ethosu->pmu_regs + NPU_REG_PMCR); + writel_relaxed(PMU_EV_TYPE_CYCLES, ethosu->pmu_regs + NPU_REG_PMCCNTR_CFG); + + mask = 0x80000000; + ncounters = perfmon->ncounters - 1; + if (ncounters) + mask |= GENMASK(ncounters - 1, 0); + + for (i = 0; i < ncounters; i++) + writel_relaxed(perfmon->counters[i], ethosu->pmu_regs + NPU_REG_PMU_EVTYPER(i)); + + writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENSET); + writel_relaxed(PMCR_CNT_EN | PMCR_EVENT_CNT_RST | PMCR_CYCLE_CNT_RST, + ethosu->pmu_regs + NPU_REG_PMCR); + ethosu->perfmon_state.active = perfmon; +} + +void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture) +{ + unsigned int i; + u8 ncounters; + u32 mask; + + lockdep_assert_held(ðosu->perfmon_state.lock); + + if (!perfmon || perfmon != ethosu->perfmon_state.active) + return; + + ncounters = perfmon->ncounters - 1; + + if (!pm_runtime_get_if_active(ethosu->base.dev)) { + ethosu->perfmon_state.active = NULL; + return; + } + + if (capture) { + for (i = 0; i < ncounters; i++) + perfmon->values[i] += readl_relaxed(ethosu->pmu_regs + NPU_REG_PMU_EVCNTR(i)); + + perfmon->values[ncounters] += + readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_LO) | + (u64)readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_HI) << 32; + } + + mask = 0x80000000; + if (ncounters) + mask |= GENMASK(ncounters - 1, 0); + writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENCLR); + + writel_relaxed(0, ethosu->pmu_regs + NPU_REG_PMCR); + ethosu->perfmon_state.active = NULL; + + pm_runtime_put(ethosu->base.dev); +} + +void ethosu_perfmon_stop(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture) +{ + if (!perfmon) + return; + + guard(mutex)(ðosu->perfmon_state.lock); + ethosu_perfmon_stop_locked(ethosu, perfmon, capture); +} + +struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, int id) +{ + struct ethosu_perfmon *perfmon; + + xa_lock(ðosu_priv->perfmons); + perfmon = xa_load(ðosu_priv->perfmons, id); + ethosu_perfmon_get(perfmon); + xa_unlock(ðosu_priv->perfmons); + + return perfmon; +} + +void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv) +{ + xa_init_flags(ðosu_priv->perfmons, XA_FLAGS_ALLOC1); +} + +static void ethosu_perfmon_delete(struct ethosu_file_priv *ethosu_priv, + struct ethosu_perfmon *perfmon) +{ + struct ethosu_device *ethosu = ethosu_priv->edev; + + /* If the active perfmon is being destroyed, stop it first */ + scoped_guard(mutex, ðosu->perfmon_state.lock) { + /* If the global perfmon is being destroyed, set it to NULL */ + if (ethosu->global_perfmon == perfmon) { + ethosu->global_perfmon = NULL; + ethosu_perfmon_put(perfmon); + } + + ethosu_perfmon_stop_locked(ethosu, perfmon, false); + } + + ethosu_perfmon_put(perfmon); +} + +void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv) +{ + struct ethosu_perfmon *perfmon; + unsigned long id; + + xa_for_each(ðosu_priv->perfmons, id, perfmon) + ethosu_perfmon_delete(ethosu_priv, perfmon); + + xa_destroy(ðosu_priv->perfmons); +} + +int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_create *req = data; + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_perfmon *perfmon; + unsigned int i, event_max; + int ret; + u32 id; + + /* Number of monitored counters cannot exceed HW limits. */ + if (req->ncounters > ethosu->npu_info.pmu_counters) + return -EINVAL; + + /* Make sure all counters are valid. */ + event_max = ethosu_is_u65(ethosu) ? 433 : 671; + for (i = 0; i < req->ncounters; i++) { + if (req->counters[i] > event_max) + return -EINVAL; + } + + /* Add 1 more counter for cycle counter */ + req->ncounters++; + + perfmon = kzalloc_flex(*perfmon, values, req->ncounters); + if (!perfmon) + return -ENOMEM; + + for (i = 0; i < req->ncounters - 1; i++) + perfmon->counters[i] = req->counters[i]; + + perfmon->ncounters = req->ncounters; + + refcount_set(&perfmon->refcnt, 1); + + ret = xa_alloc(ðosu_priv->perfmons, &id, perfmon, xa_limit_32b, + GFP_KERNEL); + + if (ret < 0) { + kfree(perfmon); + return ret; + } + + req->id = id; + + return 0; +} + +int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_destroy *req = data; + struct ethosu_perfmon *perfmon; + + perfmon = xa_erase(ðosu_priv->perfmons, req->id); + if (!perfmon) + return -EINVAL; + + ethosu_perfmon_delete(ethosu_priv, perfmon); + + return 0; +} + +int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_get_values *req = data; + struct ethosu_perfmon *perfmon; + int ret = 0; + + if (req->pad != 0) + return -EINVAL; + + perfmon = ethosu_perfmon_find(ethosu_priv, req->id); + if (!perfmon) + return -EINVAL; + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret) { + ethosu_perfmon_put(perfmon); + return ret; + } + ethosu_perfmon_stop(ethosu, perfmon, true); + + pm_runtime_put_autosuspend(dev->dev); + + if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values, + perfmon->ncounters * sizeof(u64))) + ret = -EFAULT; + + ethosu_perfmon_put(perfmon); + + return ret; +} + +int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_set_global *req = data; + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_perfmon *perfmon; + + if (req->flags & ~DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) + return -EINVAL; + + perfmon = ethosu_perfmon_find(ethosu_priv, req->id); + if (!perfmon) + return -EINVAL; + + /* If the request is to clear the global performance monitor */ + if (req->flags & DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) { + struct ethosu_perfmon *old; + + scoped_guard(mutex, ðosu->perfmon_state.lock) { + old = ethosu->global_perfmon; + if (!old) { + ethosu_perfmon_put(perfmon); + return -EINVAL; + } + + ethosu->global_perfmon = NULL; + ethosu_perfmon_stop_locked(ethosu, old, true); + } + + ethosu_perfmon_put(old); + ethosu_perfmon_put(perfmon); + + return 0; + } + + scoped_guard(mutex, ðosu->perfmon_state.lock) { + if (ethosu->perfmon_state.active || ethosu->global_perfmon) { + ethosu_perfmon_put(perfmon); + return -EBUSY; + } + + ethosu->global_perfmon = perfmon; + } + + return 0; +} diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 35e506074d5f..95120957f42a 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -307,6 +307,11 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) return -ENODEV; limits = ivpu_user_limits_get(vdev); + if (IS_ERR(limits) && PTR_ERR(limits) == -EMFILE) { + /* Context limit may be held by jobs pending deferred cleanup */ + flush_work(&vdev->job_destroy_work); + limits = ivpu_user_limits_get(vdev); + } if (IS_ERR(limits)) { ret = PTR_ERR(limits); goto err_dev_exit; @@ -510,9 +515,9 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev) { ivpu_hw_irq_disable(vdev); disable_irq(vdev->irq); - flush_work(&vdev->irq_ipc_work); flush_work(&vdev->irq_dct_work); flush_work(&vdev->context_abort_work); + flush_work(&vdev->job_destroy_work); ivpu_ipc_disable(vdev); ivpu_mmu_disable(vdev); } @@ -584,6 +589,11 @@ static const struct drm_driver driver = { .major = 1, }; +static void ivpu_destroy_workqueue(void *wq) +{ + destroy_workqueue(wq); +} + static int ivpu_irq_init(struct ivpu_device *vdev) { struct pci_dev *pdev = to_pci_dev(vdev->drm.dev); @@ -595,16 +605,26 @@ static int ivpu_irq_init(struct ivpu_device *vdev) return ret; } - INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn); INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn); INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn); + init_llist_head(&vdev->job_destroy_list); + INIT_WORK(&vdev->job_destroy_work, ivpu_job_destroy_work_fn); + + vdev->job_destroy_wq = alloc_workqueue("ivpu_job_destroy", WQ_UNBOUND | WQ_MEM_RECLAIM, 0); + if (!vdev->job_destroy_wq) + return -ENOMEM; + + ret = devm_add_action_or_reset(vdev->drm.dev, ivpu_destroy_workqueue, vdev->job_destroy_wq); + if (ret) + return ret; ivpu_irq_handlers_init(vdev); vdev->irq = pci_irq_vector(pdev, 0); - ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler, - IRQF_NO_AUTOEN, DRIVER_NAME, vdev); + ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler, + ivpu_ipc_irq_thread_handler, IRQF_NO_AUTOEN, + DRIVER_NAME, vdev); if (ret) ivpu_err(vdev, "Failed to request an IRQ %d\n", ret); diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 9eefbbb7ba11..86d7c9966cac 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -157,9 +158,11 @@ struct ivpu_device { struct xa_limit db_limit; u32 db_next; - struct work_struct irq_ipc_work; struct work_struct irq_dct_work; struct work_struct context_abort_work; + struct llist_head job_destroy_list; + struct work_struct job_destroy_work; + struct workqueue_struct *job_destroy_wq; struct mutex bo_list_lock; /* Protects bo_list */ struct list_head bo_list; diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c index d4a9bcda4100..647dc045c231 100644 --- a/drivers/accel/ivpu/ivpu_hw.c +++ b/drivers/accel/ivpu/ivpu_hw.c @@ -399,6 +399,10 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr) return IRQ_NONE; pm_runtime_mark_last_busy(vdev->drm.dev); + + if (ip_handled) + return IRQ_WAKE_THREAD; + return IRQ_HANDLED; } diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index 978bc3d8704f..62607ec8ca8f 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -146,7 +146,7 @@ ivpu_ipc_rx_msg_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, lockdep_assert_held(&ipc->cons_lock); - rx_msg = kzalloc_obj(*rx_msg, GFP_ATOMIC); + rx_msg = kmem_cache_zalloc(ipc->rx_msg_cache, GFP_ATOMIC); if (!rx_msg) { ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); return; @@ -174,7 +174,7 @@ ivpu_ipc_rx_msg_del(struct ivpu_device *vdev, struct ivpu_ipc_rx_msg *rx_msg) list_del(&rx_msg->link); ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); atomic_dec(&vdev->ipc->rx_msg_count); - kfree(rx_msg); + kmem_cache_free(vdev->ipc->rx_msg_cache, rx_msg); } void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, @@ -462,13 +462,11 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev) ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); } } - - queue_work(system_percpu_wq, &vdev->irq_ipc_work); } -void ivpu_ipc_irq_work_fn(struct work_struct *work) +irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr) { - struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_ipc_work); + struct ivpu_device *vdev = ptr; struct ivpu_ipc_info *ipc = vdev->ipc; struct ivpu_ipc_rx_msg *rx_msg, *r; struct list_head cb_msg_list; @@ -483,6 +481,8 @@ void ivpu_ipc_irq_work_fn(struct work_struct *work) rx_msg->callback(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); ivpu_ipc_rx_msg_del(vdev, rx_msg); } + + return IRQ_HANDLED; } int ivpu_ipc_init(struct ivpu_device *vdev) @@ -490,10 +490,18 @@ int ivpu_ipc_init(struct ivpu_device *vdev) struct ivpu_ipc_info *ipc = vdev->ipc; int ret; + ipc->rx_msg_cache = kmem_cache_create("ivpu_ipc_rx_msg", sizeof(struct ivpu_ipc_rx_msg), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!ipc->rx_msg_cache) { + ivpu_err(vdev, "Failed to create rx_msg_cache\n"); + return -ENOMEM; + } + ipc->mem_tx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); if (!ipc->mem_tx) { ivpu_err(vdev, "Failed to allocate mem_tx\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err_destroy_cache; } ipc->mem_rx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); @@ -532,6 +540,8 @@ int ivpu_ipc_init(struct ivpu_device *vdev) ivpu_bo_free(ipc->mem_rx); err_free_tx: ivpu_bo_free(ipc->mem_tx); +err_destroy_cache: + kmem_cache_destroy(ipc->rx_msg_cache); return ret; } @@ -544,6 +554,7 @@ void ivpu_ipc_fini(struct ivpu_device *vdev) drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0); ivpu_ipc_mem_fini(vdev); + kmem_cache_destroy(ipc->rx_msg_cache); } void ivpu_ipc_enable(struct ivpu_device *vdev) diff --git a/drivers/accel/ivpu/ivpu_ipc.h b/drivers/accel/ivpu/ivpu_ipc.h index b524a1985b9d..fb3e5816ca50 100644 --- a/drivers/accel/ivpu/ivpu_ipc.h +++ b/drivers/accel/ivpu/ivpu_ipc.h @@ -70,6 +70,7 @@ struct ivpu_ipc_info { struct gen_pool *mm_tx; struct ivpu_bo *mem_tx; struct ivpu_bo *mem_rx; + struct kmem_cache *rx_msg_cache; atomic_t rx_msg_count; @@ -90,7 +91,7 @@ void ivpu_ipc_disable(struct ivpu_device *vdev); void ivpu_ipc_reset(struct ivpu_device *vdev); void ivpu_ipc_irq_handler(struct ivpu_device *vdev); -void ivpu_ipc_irq_work_fn(struct work_struct *work); +irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr); void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, u32 channel, ivpu_ipc_rx_callback_t callback); diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index b24f31a8b567..ebb2c865b09a 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -535,6 +535,20 @@ static void ivpu_job_destroy(struct ivpu_job *job) kfree(job); } +void ivpu_job_destroy_work_fn(struct work_struct *work) +{ + struct ivpu_device *vdev = container_of(work, struct ivpu_device, job_destroy_work); + struct ivpu_job *job, *tmp; + struct llist_node *list; + + list = llist_del_all(&vdev->job_destroy_list); + + llist_for_each_entry_safe(job, tmp, list, destroy_node) { + ivpu_job_destroy(job); + ivpu_rpm_put(vdev); + } +} + static struct ivpu_job * ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count) { @@ -619,7 +633,7 @@ bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_ return false; } -static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) +static struct ivpu_job *ivpu_job_signal(struct ivpu_device *vdev, u32 job_id, u32 job_status) { struct ivpu_job *job; @@ -627,7 +641,7 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job = xa_load(&vdev->submitted_jobs_xa, job_id); if (!job) - return -ENOENT; + return NULL; ivpu_job_remove_from_submitted_jobs(vdev, job_id); @@ -646,14 +660,37 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx, job->job_status); - ivpu_job_destroy(job); ivpu_stop_job_timeout_detection(vdev); - ivpu_rpm_put(vdev); - if (!xa_empty(&vdev->submitted_jobs_xa)) ivpu_start_job_timeout_detection(vdev); + return job; +} + +static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) +{ + struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status); + + if (!job) + return -ENOENT; + + ivpu_job_destroy(job); + ivpu_rpm_put(vdev); + + return 0; +} + +static int ivpu_job_signal_and_defer_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) +{ + struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status); + + if (!job) + return -ENOENT; + + llist_add(&job->destroy_node, &vdev->job_destroy_list); + queue_work(vdev->job_destroy_wq, &vdev->job_destroy_work); + return 0; } @@ -689,6 +726,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id) struct ivpu_file_priv *file_priv = job->file_priv; struct ivpu_device *vdev = job->vdev; struct ivpu_cmdq *cmdq; + bool flushed = false; bool is_first_job; int ret; @@ -696,6 +734,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id) if (ret < 0) return ret; +retry: mutex_lock(&vdev->submitted_jobs_lock); mutex_lock(&file_priv->lock); @@ -709,6 +748,14 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id) } ret = ivpu_cmdq_register(file_priv, cmdq); + if (ret == -EBUSY && !flushed) { + /* Doorbell may be held by jobs pending deferred cleanup */ + mutex_unlock(&file_priv->lock); + mutex_unlock(&vdev->submitted_jobs_lock); + flush_work(&vdev->job_destroy_work); + flushed = true; + goto retry; + } if (ret) { ivpu_err(vdev, "Failed to register command queue: %d\n", ret); goto err_unlock; @@ -1101,7 +1148,7 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr, mutex_lock(&vdev->submitted_jobs_lock); if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status)) /* No engine error, complete the job normally */ - ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status); + ivpu_job_signal_and_defer_destroy(vdev, payload->job_id, payload->job_status); mutex_unlock(&vdev->submitted_jobs_lock); } diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h index 3ab61e6a5616..d8dbce82447a 100644 --- a/drivers/accel/ivpu/ivpu_job.h +++ b/drivers/accel/ivpu/ivpu_job.h @@ -6,8 +6,10 @@ #ifndef __IVPU_JOB_H__ #define __IVPU_JOB_H__ -#include #include +#include +#include +#include #include "ivpu_gem.h" @@ -47,6 +49,7 @@ struct ivpu_cmdq { * @vdev: Pointer to the VPU device * @file_priv: The client context that submitted this job * @done_fence: Fence signaled when job completes + * @destroy_node: List node for deferred resource cleanup after job completion * @cmd_buf_vpu_addr: VPU address of the command buffer for this job * @cmdq_id: Command queue ID used for submission * @job_id: Unique job ID for tracking and status reporting @@ -61,6 +64,7 @@ struct ivpu_job { struct ivpu_device *vdev; struct ivpu_file_priv *file_priv; struct dma_fence *done_fence; + struct llist_node destroy_node; u64 cmd_buf_vpu_addr; u32 cmdq_id; u32 job_id; @@ -87,6 +91,7 @@ void ivpu_job_done_consumer_init(struct ivpu_device *vdev); void ivpu_job_done_consumer_fini(struct ivpu_device *vdev); bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status); void ivpu_context_abort_work_fn(struct work_struct *work); +void ivpu_job_destroy_work_fn(struct work_struct *work); void ivpu_jobs_abort_all(struct ivpu_device *vdev); diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 87797bea91cb..c2bca23f7433 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -1102,9 +1102,12 @@ __dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, * context and seqno are used for easy comparison between fences, allowing * to check which fence is later by simply using dma_fence_later(). * - * It is strongly discouraged to provide an external lock because this couples - * lock and fence life time. This is only allowed for legacy use cases when - * multiple fences need to be prevented from signaling out of order. + * External locks are a relic of legacy use cases that needed a shared lock + * to serialize signaling when no out-of-order signaling was possible through + * &dma_fence_ops.signaled. Drivers have abandoned this concept since the + * introduction of the callback, but the external lock is still around. New + * users MUST NOT use external locks, as they force the issuer to outlive all + * fences that reference the lock. */ void dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, @@ -1129,9 +1132,8 @@ EXPORT_SYMBOL(dma_fence_init); * Context and seqno are used for easy comparison between fences, allowing * to check which fence is later by simply using dma_fence_later(). * - * It is strongly discouraged to provide an external lock because this couples - * lock and fence life time. This is only allowed for legacy use cases when - * multiple fences need to be prevented from signaling out of order. + * New users MUST NOT use external locks. Check the documentation in + * dma_fence_init() to understand the motives behind the legacy use cases. */ void dma_fence_init64(struct dma_fence *fence, const struct dma_fence_ops *ops, diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c index a76bf3f8b071..3937dd41bb0f 100644 --- a/drivers/dma-buf/dma-heap.c +++ b/drivers/dma-buf/dma-heap.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -154,7 +155,7 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd, in_size = 0; if ((ucmd & kcmd & IOC_OUT) == 0) out_size = 0; - ksize = max(max(in_size, out_size), drv_size); + ksize = max3(in_size, out_size, drv_size); /* If necessary, allocate buffer for ioctl argument */ if (ksize > sizeof(stack_kdata)) { diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index eb1457376307..dc81fe0301ce 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -193,6 +193,9 @@ static void mark_allocated(struct gpu_buddy *mm, block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_ALLOCATED; + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + mm->used_scoreboard[gpu_buddy_block_order(block)]++; + rbtree_remove(mm, block); } @@ -201,9 +204,14 @@ static void mark_free(struct gpu_buddy *mm, { enum gpu_buddy_free_tree tree; + if (gpu_buddy_block_is_allocated(block)) + mm->used_scoreboard[gpu_buddy_block_order(block)]--; + block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_FREE; + mm->free_scoreboard[gpu_buddy_block_order(block)]++; + tree = get_block_tree(block); rbtree_insert(mm, block, tree); } @@ -214,6 +222,8 @@ static void mark_split(struct gpu_buddy *mm, block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_SPLIT; + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + rbtree_remove(mm, block); } @@ -271,9 +281,13 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm, } rbtree_remove(mm, buddy); + mm->free_scoreboard[gpu_buddy_block_order(buddy)]--; if (force_merge && gpu_buddy_block_is_clear(buddy)) mm->clear_avail -= gpu_buddy_block_size(mm, buddy); + if (gpu_buddy_block_is_allocated(block)) + mm->used_scoreboard[gpu_buddy_block_order(block)]--; + gpu_block_free(mm, block); gpu_block_free(mm, buddy); @@ -335,6 +349,7 @@ static int __force_merge(struct gpu_buddy *mm, iter = rb_prev(iter); rbtree_remove(mm, block); + mm->free_scoreboard[gpu_buddy_block_order(block)]--; if (gpu_buddy_block_is_clear(block)) mm->clear_avail -= gpu_buddy_block_size(mm, block); @@ -384,11 +399,23 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER); + mm->free_scoreboard = kcalloc(mm->max_order + 1, + sizeof(*mm->free_scoreboard), + GFP_KERNEL); + if (!mm->free_scoreboard) + return -ENOMEM; + + mm->used_scoreboard = kcalloc(mm->max_order + 1, + sizeof(*mm->used_scoreboard), + GFP_KERNEL); + if (!mm->used_scoreboard) + goto out_free_free_scoreboard; + mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES, sizeof(*mm->free_trees), GFP_KERNEL); if (!mm->free_trees) - return -ENOMEM; + goto out_free_used_scoreboard; for_each_free_tree(i) { mm->free_trees[i] = kmalloc_array(mm->max_order + 1, @@ -450,6 +477,10 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) while (i--) kfree(mm->free_trees[i]); kfree(mm->free_trees); +out_free_used_scoreboard: + kfree(mm->used_scoreboard); +out_free_free_scoreboard: + kfree(mm->free_scoreboard); return -ENOMEM; } EXPORT_SYMBOL(gpu_buddy_init); @@ -484,10 +515,15 @@ void gpu_buddy_fini(struct gpu_buddy *mm) gpu_buddy_assert(mm->avail == mm->size); + for (i = 0; i <= mm->max_order; ++i) + gpu_buddy_assert(!mm->used_scoreboard[i]); + for_each_free_tree(i) kfree(mm->free_trees[i]); kfree(mm->free_trees); kfree(mm->roots); + kfree(mm->free_scoreboard); + kfree(mm->used_scoreboard); } EXPORT_SYMBOL(gpu_buddy_fini); @@ -650,6 +686,20 @@ static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags return needs_clear != gpu_buddy_block_is_clear(block); } +static void __gpu_buddy_undo_splits(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + struct gpu_buddy_block *buddy = __get_buddy(block); + + if (buddy && + (gpu_buddy_block_is_free(block) && + gpu_buddy_block_is_free(buddy))) { + rbtree_remove(mm, block); + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + __gpu_buddy_free(mm, block, false); + } +} + static struct gpu_buddy_block * __alloc_range_bias(struct gpu_buddy *mm, u64 start, u64 end, @@ -659,7 +709,6 @@ __alloc_range_bias(struct gpu_buddy *mm, { u64 req_size = mm->chunk_size << order; struct gpu_buddy_block *block; - struct gpu_buddy_block *buddy; LIST_HEAD(dfs); int err; int i; @@ -734,11 +783,7 @@ __alloc_range_bias(struct gpu_buddy *mm, * bigger is better, so make sure we merge everything back before we * free the allocated blocks. */ - buddy = __get_buddy(block); - if (buddy && - (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); return ERR_PTR(err); } @@ -847,8 +892,7 @@ alloc_from_freetree(struct gpu_buddy *mm, return block; err_undo: - if (tmp != order) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); return ERR_PTR(err); } @@ -912,7 +956,6 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm, { struct gpu_buddy_block *block = NULL; unsigned int order, tmp, alignment; - struct gpu_buddy_block *buddy; enum gpu_buddy_free_tree tree; unsigned long pages; int err; @@ -965,11 +1008,7 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm, * bigger is better, so make sure we merge everything back before we * free the allocated blocks. */ - buddy = __get_buddy(block); - if (buddy && - (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); return ERR_PTR(err); } @@ -980,7 +1019,6 @@ static int __alloc_range(struct gpu_buddy *mm, u64 *total_allocated_on_err) { struct gpu_buddy_block *block; - struct gpu_buddy_block *buddy; u64 total_allocated = 0; LIST_HEAD(allocated); u64 end; @@ -1051,11 +1089,7 @@ static int __alloc_range(struct gpu_buddy *mm, * bigger is better, so make sure we merge everything back before we * free the allocated blocks. */ - buddy = __get_buddy(block); - if (buddy && - (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); err_free: if (err == -ENOSPC && total_allocated_on_err) { @@ -1490,27 +1524,18 @@ void gpu_buddy_print(struct gpu_buddy *mm) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct gpu_buddy_block *block, *tmp; - struct rb_root *root; - u64 count = 0, free; - unsigned int tree; + u64 free_count = mm->free_scoreboard[order]; + u64 used_count = mm->used_scoreboard[order]; + u64 block_size = mm->chunk_size << order; + u64 free = free_count * block_size; + u64 used = used_count * block_size; - for_each_free_tree(tree) { - root = &mm->free_trees[tree][order]; - - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - BUG_ON(!gpu_buddy_block_is_free(block)); - count++; - } - } - - free = count * (mm->chunk_size << order); - if (free < SZ_1M) - pr_info("order-%2d free: %8llu KiB, blocks: %llu\n", - order, free >> 10, count); + if (block_size < SZ_1M) + pr_info("order-%2d free: %8llu KiB, used: %8llu KiB, free_blocks: %llu, used_blocks: %llu\n", + order, free >> 10, used >> 10, free_count, used_count); else - pr_info("order-%2d free: %8llu MiB, blocks: %llu\n", - order, free >> 20, count); + pr_info("order-%2d free: %8llu MiB, used: %8llu MiB, free_blocks: %llu, used_blocks: %llu\n", + order, free >> 20, used >> 20, free_count, used_count); } } EXPORT_SYMBOL(gpu_buddy_print); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 18145d78334f..31467246878f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -6964,11 +6964,14 @@ static void fill_stream_properties_from_drm_display_mode( const struct dc_stream_state *old_stream, int requested_bpc) { + bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal); struct dc_crtc_timing *timing_out = &stream->timing; const struct drm_display_info *info = &connector->display_info; struct amdgpu_dm_connector *aconnector = NULL; struct hdmi_vendor_infoframe hv_frame; struct hdmi_avi_infoframe avi_frame; + bool want_420; + bool want_422; ssize_t err; if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) @@ -6981,31 +6984,41 @@ static void fill_stream_properties_from_drm_display_mode( timing_out->h_border_right = 0; timing_out->v_border_top = 0; timing_out->v_border_bottom = 0; - /* TODO: un-hardcode */ - if (drm_mode_is_420_only(info, mode_in) - && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || - stream->signal == SIGNAL_TYPE_HDMI_FRL) - && aconnector - && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) + + want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) || + (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420); + want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) || + (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422); + + if (drm_mode_is_420_only(info, mode_in) && + (want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - else if (drm_mode_is_420_also(info, mode_in) - && aconnector - && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 - || aconnector->force_yuv420_output)) + } else if (drm_mode_is_420_also(info, mode_in) && want_420) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) - && aconnector - && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422 - || aconnector->force_yuv422_output)) + } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) && + want_422 && is_dp_or_hdmi) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422; - else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) - && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || - stream->signal == SIGNAL_TYPE_HDMI_FRL) - && aconnector - && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444) + } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 && + (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && + is_dp_or_hdmi) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; - else + } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 || + connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) { timing_out->pixel_encoding = PIXEL_ENCODING_RGB; + } else { + /* + * If a format was explicitly requested but the requested format + * can't be satisfied, set it to an invalid value so that an + * error bubbles up to userspace. This way, userspace knows it + * needs to make a better choice. + */ + if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) + timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED; + else if (drm_mode_is_420_only(info, mode_in)) + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + else + timing_out->pixel_encoding = PIXEL_ENCODING_RGB; + } timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE; timing_out->display_color_depth = convert_color_depth_from_display_info( @@ -8419,6 +8432,38 @@ static enum dc_status dm_validate_stream_and_context(struct dc *dc, return dc_result; } +static enum dc_status +dm_validate_stream_color_format(const struct drm_connector_state *drm_state, + const struct dc_stream_state *stream) +{ + enum dc_pixel_encoding encoding; + + if (!drm_state->color_format) + return DC_OK; + + switch (drm_state->color_format) { + case DRM_CONNECTOR_COLOR_FORMAT_AUTO: + case DRM_CONNECTOR_COLOR_FORMAT_RGB444: + encoding = PIXEL_ENCODING_RGB; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: + encoding = PIXEL_ENCODING_YCBCR444; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: + encoding = PIXEL_ENCODING_YCBCR422; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: + encoding = PIXEL_ENCODING_YCBCR420; + break; + default: + encoding = PIXEL_ENCODING_UNDEFINED; + break; + } + + return encoding == stream->timing.pixel_encoding ? + DC_OK : DC_UNSUPPORTED_VALUE; +} + struct dc_stream_state * create_validate_stream_for_sink(struct drm_connector *connector, const struct drm_display_mode *drm_mode, @@ -8466,6 +8511,9 @@ create_validate_stream_for_sink(struct drm_connector *connector, if (dc_result == DC_OK) dc_result = dm_validate_stream_and_context(adev->dm.dc, stream); + if (dc_result == DC_OK) + dc_result = dm_validate_stream_color_format(drm_state, stream); + if (dc_result != DC_OK) { drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n", drm_mode->hdisplay, @@ -9295,6 +9343,12 @@ static const u32 supported_colorspaces = BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) | BIT(DRM_MODE_COLORIMETRY_BT2020_YCC); +static const u32 supported_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector, int connector_type, @@ -9411,8 +9465,11 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, connector_type == DRM_MODE_CONNECTOR_eDP) { drm_connector_attach_hdr_output_metadata_property(&aconnector->base); - if (!aconnector->mst_root) + if (!aconnector->mst_root) { drm_connector_attach_vrr_capable_property(&aconnector->base); + drm_connector_attach_color_format_property(&aconnector->base, + supported_colorformats); + } if (adev->dm.hdcp_workqueue) drm_connector_attach_content_protection_property(&aconnector->base, true); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 8cf6b73bceac..5dc07ff84cd3 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -619,7 +619,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp) for (;;) { timeout_loop++; - if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0) + if (analogix_dp_is_slave_video_stream_clock_on(dp)) break; if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) { dev_err(dp->dev, "Timeout of slave video streamclk ok\n"); @@ -647,7 +647,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp) for (;;) { timeout_loop++; - if (analogix_dp_is_video_stream_on(dp) == 0) { + if (analogix_dp_is_video_stream_on(dp)) { done_count++; if (done_count > 10) break; @@ -870,7 +870,7 @@ static int analogix_dp_bridge_atomic_check(struct drm_bridge *bridge, struct drm_display_info *di = &conn_state->connector->display_info; u32 mask = BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422); - if (is_rockchip(dp->plat_data->dev_type)) { + if (analogix_dp_is_rockchip(dp->plat_data->dev_type)) { if ((di->color_formats & mask)) { DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n"); di->color_formats &= ~mask; @@ -1249,6 +1249,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp) video_info->max_link_rate = 0x0A; video_info->max_lane_count = 0x04; break; + case RK3576_EDP: case RK3588_EDP: video_info->max_link_rate = 0x14; video_info->max_lane_count = 0x04; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 17347448c6b0..94348c4e3623 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -211,7 +211,7 @@ void analogix_dp_reset_macro(struct analogix_dp_device *dp); void analogix_dp_init_video(struct analogix_dp_device *dp); void analogix_dp_set_video_color_format(struct analogix_dp_device *dp); -int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp); +bool analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp); void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp, enum clock_recovery_m_value_type type, u32 m_value, @@ -220,7 +220,7 @@ void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type); void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable); void analogix_dp_start_video(struct analogix_dp_device *dp); -int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp); +bool analogix_dp_is_video_stream_on(struct analogix_dp_device *dp); void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp); void analogix_dp_enable_scrambling(struct analogix_dp_device *dp); void analogix_dp_disable_scrambling(struct analogix_dp_device *dp); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 38fd8d5014d2..ea8401293a23 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -72,7 +72,7 @@ void analogix_dp_init_analog_param(struct analogix_dp_device *dp) reg = SEL_24M | TX_DVDD_BIT_1_0625V; writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { reg = REF_CLK_24M; if (dp->plat_data->dev_type == RK3288_DP) reg ^= REF_CLK_MASK; @@ -123,7 +123,7 @@ void analogix_dp_reset(struct analogix_dp_device *dp) analogix_dp_stop_video(dp); analogix_dp_enable_video_mute(dp, 0); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) reg = RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N | SW_FUNC_EN_N; else @@ -233,7 +233,7 @@ void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable) u32 mask = DP_PLL_PD; u32 pd_addr = ANALOGIX_DP_PLL_CTL; - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { pd_addr = ANALOGIX_DP_PD; mask = RK_PLL_PD; } @@ -254,12 +254,12 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, u32 phy_pd_addr = ANALOGIX_DP_PHY_PD; u32 mask; - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) phy_pd_addr = ANALOGIX_DP_PD; switch (block) { case AUX_BLOCK: - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) mask = RK_AUX_PD; else mask = AUX_PD; @@ -317,7 +317,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, * to power off everything instead of DP_PHY_PD in * Rockchip */ - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) mask = DP_INC_BG; else mask = DP_PHY_PD; @@ -329,7 +329,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, reg &= ~mask; writel(reg, dp->reg_base + phy_pd_addr); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) usleep_range(10, 15); break; case POWER_ALL: @@ -465,7 +465,7 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp) analogix_dp_reset_aux(dp); /* AUX_BIT_PERIOD_EXPECTED_DELAY doesn't apply to Rockchip IP */ - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) reg = 0; else reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3); @@ -713,7 +713,7 @@ void analogix_dp_set_video_color_format(struct analogix_dp_device *dp) writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3); } -int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) +bool analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) { u32 reg; @@ -724,7 +724,7 @@ int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) if (!(reg & DET_STA)) { dev_dbg(dp->dev, "Input stream clock not detected.\n"); - return -EINVAL; + return false; } reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2); @@ -735,10 +735,10 @@ int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) if (reg & CHA_STA) { dev_dbg(dp->dev, "Input stream clk is changing\n"); - return -EINVAL; + return false; } - return 0; + return true; } void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp, @@ -816,7 +816,7 @@ void analogix_dp_start_video(struct analogix_dp_device *dp) writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1); } -int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp) +bool analogix_dp_is_video_stream_on(struct analogix_dp_device *dp) { u32 reg; @@ -826,10 +826,10 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp) reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3); if (!(reg & STRM_VALID)) { dev_dbg(dp->dev, "Input video stream is not detected.\n"); - return -EINVAL; + return false; } - return 0; + return true; } void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp) @@ -837,7 +837,7 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp) u32 reg; reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { reg &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N); } else { reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N); diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index 16c0631adeb1..f7a5bfd9c075 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,8 @@ struct display_connector { struct regulator *supply; struct gpio_desc *ddc_en; + + struct work_struct hpd_work; }; static inline struct display_connector * @@ -87,6 +90,34 @@ display_connector_bridge_detect(struct drm_bridge *bridge, struct drm_connector return display_connector_detect(bridge); } +static void display_connector_hpd_enable(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + enable_irq(conn->hpd_irq); + + if (conn->bridge.type == DRM_MODE_CONNECTOR_DisplayPort) + schedule_work(&conn->hpd_work); +} + +static void display_connector_hpd_disable(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + if (conn->bridge.type == DRM_MODE_CONNECTOR_DisplayPort) + cancel_work_sync(&conn->hpd_work); + + disable_irq(conn->hpd_irq); +} + +static void display_connector_hpd_work(struct work_struct *work) +{ + struct display_connector *conn = container_of(work, struct display_connector, hpd_work); + struct drm_bridge *bridge = &conn->bridge; + + drm_bridge_hpd_notify(bridge, display_connector_detect(bridge)); +} + static const struct drm_edid *display_connector_edid_read(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -178,6 +209,8 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs display_connector_bridge_funcs = { .attach = display_connector_attach, .detect = display_connector_bridge_detect, + .hpd_enable = display_connector_hpd_enable, + .hpd_disable = display_connector_hpd_disable, .edid_read = display_connector_edid_read, .atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts, .atomic_get_input_bus_fmts = display_connector_get_input_bus_fmts, @@ -307,6 +340,7 @@ static int display_connector_probe(struct platform_device *pdev) NULL, display_connector_hpd_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_NO_AUTOEN | IRQF_ONESHOT, "HPD", conn); if (ret) { @@ -378,6 +412,8 @@ static int display_connector_probe(struct platform_device *pdev) conn->bridge.ops |= DRM_BRIDGE_OP_DETECT; if (conn->hpd_irq >= 0) conn->bridge.ops |= DRM_BRIDGE_OP_HPD; + if (conn->hpd_irq >= 0 && type == DRM_MODE_CONNECTOR_DisplayPort) + INIT_WORK(&conn->hpd_work, display_connector_hpd_work); dev_dbg(&pdev->dev, "Found %s display connector '%s' %s DDC bus and %s HPD GPIO (ops 0x%x)\n", diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index ea543be4ae3e..38d176d4eef2 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -1158,9 +1158,7 @@ static int sii902x_probe(struct i2c_client *client) static const char * const supplies[] = {"iovcc", "cvcc12"}; int ret; - ret = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA); - if (!ret) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(dev, "I2C adapter not suitable\n"); return -EIO; } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c index 21541be094c4..36ee6e027af5 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c @@ -2093,6 +2093,12 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder, } EXPORT_SYMBOL_GPL(dw_dp_bind); +void dw_dp_unbind(struct dw_dp *dp) +{ + drm_dp_aux_unregister(&dp->aux); +} +EXPORT_SYMBOL_GPL(dw_dp_unbind); + MODULE_AUTHOR("Andy Yan "); MODULE_DESCRIPTION("DW DP Core Library"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 1c214a8e6dc2..13fddd5ebc82 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -1190,6 +1190,7 @@ static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts, #endif /* CONFIG_DRM_DW_HDMI_QP_CEC */ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = { + .atomic_get_output_bus_fmts = drm_atomic_helper_bridge_get_hdmi_output_bus_fmts, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_reset = drm_atomic_helper_bridge_reset, diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 42b451432bbb..c03ff1ea9df8 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -977,7 +977,6 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) dsi->lanes = dsi_lanes; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | - MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP | MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET; ret = devm_mipi_dsi_attach(dev, dsi); diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c index 8d21b785bead..e3e02c84a4cf 100644 --- a/drivers/gpu/drm/clients/drm_log.c +++ b/drivers/gpu/drm/clients/drm_log.c @@ -122,10 +122,12 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s, iosys_map_incr(&map, r.y1 * fb->pitches[0]); for (i = 0; i < len && i < scanout->columns; i++) { u32 color = (i < prefix_len) ? scanout->prefix_color : scanout->front_color; - src = drm_draw_get_char_bitmap(font, s[i], font_pitch); - drm_log_blit(&map, fb->pitches[0], src, font_pitch, - scanout->scaled_font_h, scanout->scaled_font_w, - px_width, color); + src = font_data_glyph_buf(font->data, font->width, font->height, + (unsigned char)s[i]); + if (src) + drm_log_blit(&map, fb->pitches[0], src, font_pitch, + scanout->scaled_font_h, scanout->scaled_font_w, + px_width, color); iosys_map_incr(&map, scanout->scaled_font_w * px_width); } diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 649969fca141..92f8a2d7aab4 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -265,26 +265,57 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector, } } -static void drm_bridge_connector_reset(struct drm_connector *connector) +static struct drm_connector_state * +drm_bridge_connector_create_state(struct drm_connector *connector) { struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector); + struct drm_connector_state *conn_state; + + conn_state = drm_atomic_helper_connector_create_state(connector); + if (IS_ERR(conn_state)) + return conn_state; - drm_atomic_helper_connector_reset(connector); if (bridge_connector->bridge_hdmi) - __drm_atomic_helper_connector_hdmi_reset(connector, - connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, + conn_state); + + return conn_state; +} + +static enum drm_connector_color_format +drm_bridge_connector_color_format(const struct drm_connector_state *conn_state) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(conn_state->connector); + + if (bridge_connector->bridge_hdmi) { + switch (conn_state->hdmi.output_format) { + default: + case DRM_OUTPUT_COLOR_FORMAT_RGB444: + return DRM_CONNECTOR_COLOR_FORMAT_RGB444; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR444: + return DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR422: + return DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR420: + return DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + } + } + + return conn_state->color_format; } static const struct drm_connector_funcs drm_bridge_connector_funcs = { - .reset = drm_bridge_connector_reset, .detect = drm_bridge_connector_detect, .force = drm_bridge_connector_force, .fill_modes = drm_helper_probe_single_connector_modes, + .atomic_create_state = drm_bridge_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .debugfs_init = drm_bridge_connector_debugfs_init, .oob_hotplug_event = drm_bridge_connector_oob_hotplug_event, + .color_format = drm_bridge_connector_color_format, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index 4867edbf2622..ce17eeefc2da 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -306,17 +306,18 @@ */ /** - * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources + * __drm_atomic_helper_connector_hdmi_state_init() - Initialize all HDMI @drm_connector_state resources * @connector: DRM connector - * @new_conn_state: connector state to reset + * @new_conn_state: connector state to initialize * * Initializes all HDMI resources from a @drm_connector_state without * actually allocating it. This is useful for HDMI drivers, in - * combination with __drm_atomic_helper_connector_reset() or - * drm_atomic_helper_connector_reset(). + * combination with __drm_atomic_helper_connector_state_init(), + * drm_atomic_helper_connector_reset(), or + * drm_atomic_helper_connector_create_state(). */ -void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, - struct drm_connector_state *new_conn_state) +void __drm_atomic_helper_connector_hdmi_state_init(struct drm_connector *connector, + struct drm_connector_state *new_conn_state) { unsigned int max_bpc = connector->max_bpc; @@ -324,7 +325,7 @@ void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, new_conn_state->max_requested_bpc = max_bpc; new_conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO; } -EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset); +EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_state_init); static enum hdmi_colorspace output_color_format_to_hdmi_colorspace(const struct drm_connector *connector, @@ -541,10 +542,11 @@ sink_supports_format_bpc(const struct drm_connector *connector, drm_dbg_kms(dev, "YUV444 format supported in that configuration.\n"); return true; - } - drm_dbg_kms(dev, "Unsupported pixel format.\n"); - return false; + default: + drm_dbg_kms(dev, "Unsupported pixel format.\n"); + return false; + } } static enum drm_mode_status @@ -669,8 +671,39 @@ hdmi_compute_config(const struct drm_connector *connector, unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, connector->max_bpc); + enum drm_output_color_format fmt; int ret; + if (conn_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) { + switch (conn_state->color_format) { + case DRM_CONNECTOR_COLOR_FORMAT_AUTO: + drm_warn(connector->dev, "AUTO format in non-AUTO path.\n"); + fallthrough; + case DRM_CONNECTOR_COLOR_FORMAT_RGB444: + fmt = DRM_OUTPUT_COLOR_FORMAT_RGB444; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR444; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR422; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420; + break; + default: + drm_dbg_kms(connector->dev, "HDMI does not support color format '%d'.\n", + conn_state->color_format); + return -EINVAL; + } + + return hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc, fmt); + } + + /* + * For %DRM_CONNECTOR_COLOR_FORMAT_AUTO, try RGB first, and fall back + * to the less bandwidth-intensive YCBCR420 if RGB fails. + */ ret = hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc, DRM_OUTPUT_COLOR_FORMAT_RGB444); if (ret) { @@ -910,8 +943,21 @@ drm_hdmi_connector_mode_valid(struct drm_connector *connector, const struct drm_display_mode *mode) { unsigned long long clock; + enum drm_output_color_format fmt; - clock = drm_hdmi_compute_mode_clock(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444); + if (drm_mode_is_420_only(&connector->display_info, mode)) { + if (connector->ycbcr_420_allowed) + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420; + else + return MODE_NO_420; + } else if (drm_mode_is_420_also(&connector->display_info, mode) && + connector->ycbcr_420_allowed) { + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420; + } else { + fmt = DRM_OUTPUT_COLOR_FORMAT_RGB444; + } + + clock = drm_hdmi_compute_mode_clock(mode, 8, fmt); if (!clock) return MODE_ERROR; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 080aec5a9774..735ab7badc2e 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -47,6 +47,86 @@ #include "drm_crtc_internal.h" #include "drm_internal.h" +/** + * DOC: state lifetime + * + * &drm_atomic_commit represents an update to modeset pipeline state. + * It's a transient object that holds a state update as a collection of + * pointers to individual objects' states. &drm_atomic_commit has a much + * shorter lifetime than the objects' states, since it's only allocated + * while preparing, checking or committing the update, while object + * states are allocated when preparing the update and kept alive as long + * as they are active in the device. + * + * Their respective lifetimes are: + * + * - at driver initialization time, the driver calls + * drm_mode_config_create_initial_state() to allocate an initial, + * pristine, state for each object and stores it in the objects state + * pointer. Historically, this was one of drm_mode_config_reset() job, + * so one might still encounter it in a driver. + * + * - When resuming from suspend, drm_mode_config_reset() resets the + * software and hardware state to a known default and stores it in the + * object's state pointer. Not all objects are affected by + * drm_mode_config_reset() though. + * + * - whenever a new update is needed: + * + * + drm_atomic_commit_alloc() allocates a new &drm_atomic_commit + * instance. + * + * + The code triggering the commit (ioctl, client modeset, + * drm_atomic_helper_reset_crtc(), etc.) copies the current active + * state of all entities affected by the update into this new + * &drm_atomic_commit using drm_atomic_get_plane_state(), + * drm_atomic_get_crtc_state(), drm_atomic_get_connector_state(), or + * drm_atomic_get_private_obj_state(). This new state can then be + * modified. + * + * At that point, &drm_atomic_commit stores three state pointers for + * any affected entity: the "old" and "new" states, and + * state_to_destroy. The old state is the state currently active in + * the hardware, which is either the one initialized by reset() or a + * newer one if a commit has been made. The new state is the state + * we just allocated and we might eventually commit to the hardware. + * The state_to_destroy points to the state we'll eventually have to + * free when the drm_atomic_commit will be destroyed, and points to + * the new state for now since the old state is still the active + * state. + * + * + After the calling code populated the commit with the entities + * states, it updates the new states with the new values we need to + * commit. The new commit instance is now ready. + * + * + Then we have two branches depending on the calling code intent: + * + * - If the calling code only wants to check that the commit would + * work (for example because of the DRM_MODE_ATOMIC_TEST_ONLY + * flag). It calls drm_atomic_check_only(), which in turn checks + * all these states by invoking atomic_check on all affected + * pipeline stages. + * + * - If the calling code actually wants to trigger a commit, it + * calls drm_atomic_commit(). The first stage is the check + * mentioned above, and if the check is successful, it performs + * the commit. Part of the commit is a call to + * drm_atomic_helper_swap_state() which turns the new states into + * the active states. After swapping states, each object's state + * pointer now refers to the formerly new state. The + * state_to_destroy now refers to the formerly old state. + * + * + Once done, and when the last reference to our &drm_atomic_commit + * is given up through drm_atomic_commit_put(), it calls + * __drm_atomic_commit_free(). In turn, __drm_atomic_commit_free() + * calls drm_atomic_commit_clear() that will free all + * state_to_destroy (ie. old states), and it finally frees + * &drm_atomic_commit instance. + * + * + Now, we don't have any active &drm_atomic_commit anymore, and + * only the entity active states remain allocated. + */ + void __drm_crtc_commit_free(struct kref *kref) { struct drm_crtc_commit *commit = diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 51f39edc31ed..285aac3554df 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -737,6 +738,11 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, if (old_connector_state->max_requested_bpc != new_connector_state->max_requested_bpc) new_crtc_state->connectors_changed = true; + + if (old_connector_state->color_format != + new_connector_state->color_format) + new_crtc_state->connectors_changed = true; + } if (funcs->atomic_check) @@ -4102,3 +4108,83 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, return input_fmts; } EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt); + +/** + * drm_atomic_helper_bridge_get_hdmi_output_bus_fmts - helper implementing + * atomic_get_output_bus_fmts for HDMI + * @bridge: pointer to &struct drm_bridge + * @bridge_state: pointer to the current bridge state + * @crtc_state: pointer to the current CRTC state + * @conn_state: pointer to the current connector state + * @num_output_fmts: pointer to where the number of entries in the returned array + * will be stored. Set to 0 if unsuccessful. + * + * Common implementation for the &drm_bridge_funcs.atomic_get_output_bus_fmts + * operation that's applicable to HDMI connectors. + * + * Returns: a newly allocated array of u32 values of length \*@num_output_fmts, + * representing all the MEDIA_BUS_FMTS\_ for the current connector state's + * chosen HDMI output bits per compoennt, or %NULL if it fails to allocate one. + */ +u32 * +drm_atomic_helper_bridge_get_hdmi_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + unsigned int num_fmts = 0; + u32 *out_fmts; + + /* + * bridge->supported_formats is a bit field of BIT(enum drm_output_color_format) + * values. The smallest hweight that is smaller than or equal to + * %DRM_OUTPUT_COLOR_FORMAT_COUNT will do for counting set bits here. + */ + BUILD_BUG_ON(const_true(DRM_OUTPUT_COLOR_FORMAT_COUNT > 8)); + out_fmts = kmalloc_array(hweight8(bridge->supported_formats), + sizeof(u32), GFP_KERNEL); + if (!out_fmts) { + *num_output_fmts = 0; + return NULL; + } + + switch (conn_state->hdmi.output_bpc) { + case 12: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB121212_1X36; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV12_1X36; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY12_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36; + break; + case 10: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB101010_1X30; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV10_1X30; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY10_1X20; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30; + break; + default: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB888_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV8_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY8_1X16; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; + break; + } + + *num_output_fmts = num_fmts; + + return out_fmts; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_get_hdmi_output_bus_fmts); + diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index cc70508d4fdb..07686e94aae0 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -63,7 +63,7 @@ */ /** - * __drm_atomic_helper_crtc_state_reset - reset the CRTC state + * __drm_atomic_helper_crtc_state_init - Initialize the CRTC state * @crtc_state: atomic CRTC state, must not be NULL * @crtc: CRTC object, must not be NULL * @@ -71,13 +71,13 @@ * values. This is useful for drivers that subclass the CRTC state. */ void -__drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *crtc_state, - struct drm_crtc *crtc) +__drm_atomic_helper_crtc_state_init(struct drm_crtc_state *crtc_state, + struct drm_crtc *crtc) { crtc_state->crtc = crtc; crtc_state->background_color = DRM_ARGB64_PREP(0xffff, 0, 0, 0); } -EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_init); /** * __drm_atomic_helper_crtc_reset - reset state on CRTC @@ -96,7 +96,7 @@ __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { if (crtc_state) - __drm_atomic_helper_crtc_state_reset(crtc_state, crtc); + __drm_atomic_helper_crtc_state_init(crtc_state, crtc); if (drm_dev_has_vblank(crtc->dev)) drm_crtc_vblank_reset(crtc); @@ -124,6 +124,31 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); +/** + * drm_atomic_helper_crtc_create_state - default &drm_crtc_funcs.atomic_create_state hook for crtcs + * @crtc: crtc object + * + * Allocates and initializes pristine @drm_crtc_state. + * + * This is useful for drivers that don't subclass @drm_crtc_state. + * + * RETURNS: + * Pointer to new crtc state, or ERR_PTR on failure. + */ +struct drm_crtc_state *drm_atomic_helper_crtc_create_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(state, crtc); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_create_state); + /** * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state * @crtc: CRTC object @@ -237,15 +262,15 @@ void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); /** - * __drm_atomic_helper_plane_state_reset - resets plane state to default values + * __drm_atomic_helper_plane_state_init - Initialize the plane state * @plane_state: atomic plane state, must not be NULL * @plane: plane object, must not be NULL * * Initializes the newly allocated @plane_state with default - * values. This is useful for drivers that subclass the CRTC state. + * values. This is useful for drivers that subclass the plane state. */ -void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, - struct drm_plane *plane) +void __drm_atomic_helper_plane_state_init(struct drm_plane_state *plane_state, + struct drm_plane *plane) { u64 val; @@ -297,7 +322,7 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, plane_state->hotspot_y = val; } } -EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_plane_state_init); /** * __drm_atomic_helper_plane_reset - reset state on plane @@ -305,7 +330,7 @@ EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset); * @plane_state: plane state to assign * * Initializes the newly allocated @plane_state and assigns it to - * the &drm_crtc->state pointer of @plane, usually required when + * the &drm_plane->state pointer of @plane, usually required when * initializing the drivers or when called from the &drm_plane_funcs.reset * hook. * @@ -315,7 +340,7 @@ void __drm_atomic_helper_plane_reset(struct drm_plane *plane, struct drm_plane_state *plane_state) { if (plane_state) - __drm_atomic_helper_plane_state_reset(plane_state, plane); + __drm_atomic_helper_plane_state_init(plane_state, plane); plane->state = plane_state; } @@ -340,6 +365,31 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); +/** + * drm_atomic_helper_plane_create_state - default &drm_plane_funcs.atomic_create_state hook for planes + * @plane: plane object + * + * Allocates and initializes pristine @drm_plane_state. + * + * This is useful for drivers that don't subclass @drm_plane_state. + * + * RETURNS: + * Pointer to new plane state, or ERR_PTR on failure. + */ +struct drm_plane_state *drm_atomic_helper_plane_create_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_plane_state_init(state, plane); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_plane_create_state); + /** * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state * @plane: plane object @@ -426,20 +476,20 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); /** - * __drm_atomic_helper_connector_state_reset - reset the connector state + * __drm_atomic_helper_connector_state_init - Initialize the connector state * @conn_state: atomic connector state, must not be NULL - * @connector: connectotr object, must not be NULL + * @connector: connector object, must not be NULL * * Initializes the newly allocated @conn_state with default * values. This is useful for drivers that subclass the connector state. */ void -__drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, - struct drm_connector *connector) +__drm_atomic_helper_connector_state_init(struct drm_connector_state *conn_state, + struct drm_connector *connector) { conn_state->connector = connector; } -EXPORT_SYMBOL(__drm_atomic_helper_connector_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_connector_state_init); /** * __drm_atomic_helper_connector_reset - reset state on connector @@ -458,7 +508,7 @@ __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state) { if (conn_state) - __drm_atomic_helper_connector_state_reset(conn_state, connector); + __drm_atomic_helper_connector_state_init(conn_state, connector); connector->state = conn_state; } @@ -484,6 +534,32 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); +/** + * drm_atomic_helper_connector_create_state - default &drm_connector_funcs.atomic_create_state hook for connectors + * @connector: connector object + * + * Allocates and initializes pristine @drm_connector_state. + * + * This is useful for drivers that don't subclass @drm_connector_state. + * + * RETURNS: + * Pointer to new connector state, or ERR_PTR on failure. + */ +struct drm_connector_state * +drm_atomic_helper_connector_create_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_connector_state_init(state, connector); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_create_state); + /** * drm_atomic_helper_connector_tv_margins_reset - Resets TV connector properties * @connector: DRM connector @@ -731,8 +807,6 @@ void __drm_atomic_helper_private_obj_create_state(struct drm_private_obj *obj, { if (state) state->obj = obj; - - obj->state = state; } EXPORT_SYMBOL(__drm_atomic_helper_private_obj_create_state); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index e997917819e8..1050dddadb17 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -960,6 +960,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, state->privacy_screen_sw_state = val; } else if (property == connector->broadcast_rgb_property) { state->hdmi.broadcast_rgb = val; + } else if (property == connector->color_format_property) { + state->color_format = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -1045,6 +1047,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->privacy_screen_sw_state; } else if (property == connector->broadcast_rgb_property) { *val = state->hdmi.broadcast_rgb; + } else if (property == connector->color_format_property) { + *val = state->color_format; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 687b36eea0c7..1daac8a7f4c9 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -198,6 +198,46 @@ * driver. */ +/** + * DOC: bridge chain format selection + * + * A bridge chain, from display output processor to connector, may contain + * bridges capable of converting between bus formats on their inputs, and + * output formats on their outputs. For example, a bridge may be able to convert + * from RGB to YCbCr 4:4:4, and pass through YCbCr 4:2:0 as-is, but not convert + * from RGB to YCbCr 4:2:0. This means not all input formats map to all output + * formats. + * + * Further adding to this, a desired output color format, as specified with the + * "color format" DRM property, might not correspond 1:1 to what the display + * driver should set at its output. The bridge chain it feeds into may only be + * able to reach the desired output format, if a conversion from a different + * starting format is performed. + * + * To deal with this complexity, the recursive bridge chain bus format selection + * logic starts with the last bridge in the chain, usually the connector, and + * then recursively walks the chain of bridges backwards to the first bridge, + * trying to find a path. + * + * For a display driver to work in such a scenario, it should read the first + * bridge's bridge state to figure out which bus format the chain resolved to. + * If the first bridge's input format resolved to %MEDIA_BUS_FMT_FIXED, then its + * output format should be used. + * + * Special handling is done for HDMI as it relates to format selection. Instead + * of directly using the "color format" DRM property for bridge chains that end + * in HDMI bridges, the bridge chain format selection logic will trust the logic + * that set the HDMI output format. For the common HDMI state helper + * functionality, this means that %DRM_CONNECTOR_COLOR_FORMAT_AUTO will allow + * fallbacks to YCBCr 4:2:0 if the bandwidth requirements would otherwise be too + * high but the mode and connector allow it. + * + * For bridge chains that do not end in an HDMI bridge, + * %DRM_CONNECTOR_COLOR_FORMAT_AUTO will be satisfied with the first output + * format on the last bridge for which it can find a path back to the first + * bridge. + */ + /* Protect bridge_list and bridge_lingering_list */ static DEFINE_MUTEX(bridge_lock); static LIST_HEAD(bridge_list); @@ -417,6 +457,7 @@ void drm_bridge_add(struct drm_bridge *bridge) if (!list_empty(&bridge->list)) list_del_init(&bridge->list); + mutex_init(&bridge->hpd_state_mutex); mutex_init(&bridge->hpd_mutex); if (bridge->ops & DRM_BRIDGE_OP_HDMI) @@ -469,6 +510,7 @@ void drm_bridge_remove(struct drm_bridge *bridge) mutex_unlock(&bridge_lock); mutex_destroy(&bridge->hpd_mutex); + mutex_destroy(&bridge->hpd_state_mutex); drm_bridge_put(bridge); } @@ -1149,6 +1191,47 @@ static int select_bus_fmt_recursive(struct drm_bridge *first_bridge, return ret; } +static bool __pure bus_format_is_color_fmt(u32 bus_fmt, enum drm_connector_color_format fmt) +{ + if (fmt == DRM_CONNECTOR_COLOR_FORMAT_AUTO) + return true; + + switch (bus_fmt) { + case MEDIA_BUS_FMT_FIXED: + return true; + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_RGB444; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + default: + return false; + } +} + /* * This function is called by &drm_atomic_bridge_chain_check() just before * calling &drm_bridge_funcs.atomic_check() on all elements of the chain. @@ -1192,6 +1275,7 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, struct drm_encoder *encoder = bridge->encoder; struct drm_bridge_state *last_bridge_state; unsigned int i, num_out_bus_fmts = 0; + enum drm_connector_color_format fmt; u32 *out_bus_fmts; int ret = 0; @@ -1233,11 +1317,31 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; } + /* + * Instead of directly accessing conn_state.color_format, call into a + * connector function that allows connector implementations (e.g. for + * bridge connectors including HDMI bridges, where the HDMI helpers will + * have already chosen an appropriate output format) to override the + * selected format. + */ + fmt = drm_connector_get_color_format(conn_state); + for (i = 0; i < num_out_bus_fmts; i++) { + if (!bus_format_is_color_fmt(out_bus_fmts[i], fmt)) { + drm_dbg_kms(last_bridge->dev, + "Skipping bus format 0x%04x as it doesn't match format %d\n", + out_bus_fmts[i], fmt); + ret = -ENOTSUPP; + continue; + } ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state, conn_state, out_bus_fmts[i]); - if (ret != -ENOTSUPP) + if (ret != -ENOTSUPP) { + drm_dbg_kms(last_bridge->dev, + "Found bridge chain ending with bus format 0x%04x\n", + out_bus_fmts[i]); break; + } } kfree(out_bus_fmts); @@ -1451,19 +1555,25 @@ void drm_bridge_hpd_enable(struct drm_bridge *bridge, if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) return; + mutex_lock(&bridge->hpd_state_mutex); + mutex_lock(&bridge->hpd_mutex); - if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) + if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) { + mutex_unlock(&bridge->hpd_mutex); goto unlock; + } bridge->hpd_cb = cb; bridge->hpd_data = data; + mutex_unlock(&bridge->hpd_mutex); + if (bridge->funcs->hpd_enable) bridge->funcs->hpd_enable(bridge); unlock: - mutex_unlock(&bridge->hpd_mutex); + mutex_unlock(&bridge->hpd_state_mutex); } EXPORT_SYMBOL_GPL(drm_bridge_hpd_enable); @@ -1484,13 +1594,15 @@ void drm_bridge_hpd_disable(struct drm_bridge *bridge) if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) return; - mutex_lock(&bridge->hpd_mutex); + mutex_lock(&bridge->hpd_state_mutex); if (bridge->funcs->hpd_disable) bridge->funcs->hpd_disable(bridge); + mutex_lock(&bridge->hpd_mutex); bridge->hpd_cb = NULL; bridge->hpd_data = NULL; mutex_unlock(&bridge->hpd_mutex); + mutex_unlock(&bridge->hpd_state_mutex); } EXPORT_SYMBOL_GPL(drm_bridge_hpd_disable); diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index faa025498de4..1536e59c6fe7 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -47,29 +47,23 @@ void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct gpu_buddy_block *block, *tmp; - struct rb_root *root; - u64 count = 0, free; - unsigned int tree; - - for_each_free_tree(tree) { - root = &mm->free_trees[tree][order]; - - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - BUG_ON(!gpu_buddy_block_is_free(block)); - count++; - } - } + u64 free_count = mm->free_scoreboard[order]; + u64 used_count = mm->used_scoreboard[order]; + u64 block_size = mm->chunk_size << order; + u64 free = free_count * block_size; + u64 used = used_count * block_size; drm_printf(p, "order-%2d ", order); - free = count * (mm->chunk_size << order); - if (free < SZ_1M) - drm_printf(p, "free: %8llu KiB", free >> 10); + if (block_size < SZ_1M) + drm_printf(p, "free: %8llu KiB, used: %8llu KiB", + free >> 10, used >> 10); else - drm_printf(p, "free: %8llu MiB", free >> 20); + drm_printf(p, "free: %8llu MiB, used: %8llu MiB", + free >> 20, used >> 20); - drm_printf(p, ", blocks: %llu\n", count); + drm_printf(p, ", free_blocks: %llu, used_blocks: %llu\n", + free_count, used_count); } } EXPORT_SYMBOL(drm_buddy_print); diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 65fec53f70fa..682fcc651525 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -498,15 +498,15 @@ void drm_colorop_atomic_destroy_state(struct drm_colorop *colorop, } /** - * __drm_colorop_state_reset - resets colorop state to default values + * __drm_colorop_state_init - Initializes colorop state to default values * @colorop_state: atomic colorop state, must not be NULL * @colorop: colorop object, must not be NULL * * Initializes the newly allocated @colorop_state with default - * values. This is useful for drivers that subclass the CRTC state. + * values. This is useful for drivers that subclass the colorop state. */ -static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state, - struct drm_colorop *colorop) +static void __drm_colorop_state_init(struct drm_colorop_state *colorop_state, + struct drm_colorop *colorop) { u64 val; @@ -535,15 +535,38 @@ static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state, } } +/** + * drm_atomic_helper_colorop_create_state - Allocates and initializes colorop atomic state + * @colorop: drm colorop + * + * Initializes a pristine @drm_colorop_state. + * + * RETURNS: + * Pointer to new colorop state, or ERR_PTR on failure. + */ +struct drm_colorop_state * +drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop) +{ + struct drm_colorop_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_colorop_state_init(state, colorop); + + return state; +} + /** * __drm_colorop_reset - reset state on colorop * @colorop: drm colorop * @colorop_state: colorop state to assign * - * Initializes the newly allocated @colorop_state and assigns it to - * the &drm_crtc->state pointer of @colorop, usually required when - * initializing the drivers or when called from the &drm_colorop_funcs.reset - * hook. + * Initializes the newly allocated @colorop_state and assigns it to the + * &drm_colorop->state pointer of @colorop, usually required when + * initializing the drivers or when called from the + * &drm_colorop_funcs.reset hook. * * This is useful for drivers that subclass the colorop state. */ @@ -551,7 +574,7 @@ static void __drm_colorop_reset(struct drm_colorop *colorop, struct drm_colorop_state *colorop_state) { if (colorop_state) - __drm_colorop_state_reset(colorop_state, colorop); + __drm_colorop_state_init(colorop_state, colorop); colorop->state = colorop_state; } diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 11646453aaac..95028483e0d1 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -618,8 +618,17 @@ int drmm_connector_hdmi_init(struct drm_device *dev, * drm_connector_attach_max_bpc_property() requires the * connector to have a state. */ - if (connector->funcs->reset) + if (connector->funcs->atomic_create_state) { + struct drm_connector_state *state; + + state = connector->funcs->atomic_create_state(connector); + if (IS_ERR(state)) + return PTR_ERR(state); + + connector->state = state; + } else if (connector->funcs->reset) { connector->funcs->reset(connector); + } drm_connector_attach_max_bpc_property(connector, 8, max_bpc); connector->max_bpc = max_bpc; @@ -627,6 +636,10 @@ int drmm_connector_hdmi_init(struct drm_device *dev, if (max_bpc > 8) drm_connector_attach_hdr_output_metadata_property(connector); + ret = drm_connector_attach_color_format_property(connector, supported_formats); + if (ret) + return ret; + connector->hdmi.funcs = hdmi_funcs; return 0; @@ -1388,6 +1401,18 @@ static const u32 hdmi_colorspaces = BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65) | BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER); +static const u32 hdmi_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + +static const u32 dp_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + /* * As per DP 1.4a spec, 2.2.5.7.5 VSC SDP Payload for Pixel Encoding/Colorimetry * Format Table 2-120 @@ -2557,7 +2582,8 @@ EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); * conversion matrix and convert to the appropriate quantization * range. * The variants BT2020_RGB and BT2020_YCC are equivalent and the - * driver chooses between RGB and YCbCr on its own. + * driver chooses between RGB and YCbCr based on the color format + * property. * * SMPTE_170M_YCC: * BT709_YCC: @@ -2935,6 +2961,149 @@ int drm_connector_attach_colorspace_property(struct drm_connector *connector) } EXPORT_SYMBOL(drm_connector_attach_colorspace_property); +/** + * DOC: Color format + * + * The connector "color format" property allows userspace to request a specific + * color model on the output of the connector. Not all values listed by the + * property are guaranteed to work for every sink; rather, it is an optimistic + * listing of color formats that the source could output depending on + * circumstances. + * + * Whether it actually can output a certain color format is determined during + * the atomic check phase. Consequently, a userspace application that sets the + * color format to a value other than "AUTO" should check whether its atomic + * commit succeeded. + * + * Possible values for "color format": + * + * "AUTO": + * The driver or display protocol helpers should pick a suitable color + * format. All implementations of a specific display protocol will behave + * the same way with "AUTO", but different display protocols do not + * necessarily have the same "AUTO" semantics. + * + * For HDMI connectors, "AUTO" picks RGB, but falls back to YUV 4:2:0 if + * the bandwidth required for full-scale RGB is not available, or the mode + * is YUV 4:2:0-only, as long as the mode, source, and sink all support + * YUV 4:2:0. + * "RGB": + * RGB output format. The quantization range (limited/full) depends on the + * value of the "Broadcast RGB" property if it is present on the connector. + * "YUV 4:4:4": + * YUV 4:4:4 (a.k.a. YCbCr 4:4:4) output format. Chroma is not subsampled. + * The quantization range defaults to limited. + * "YUV 4:2:2": + * YUV 4:2:2 (a.k.a. YCbCr 4:2:2) output format. Chroma has half the + * horizontal resolution of Luma. The quantization range defaults to + * limited. + * "YUV 4:2:0": + * YUV 4:2:0 (a.k.a. YCbCr 4:2:0) output format. Chroma has half the + * horizontal and vertical resolution of Luma. The quantization range + * defaults to limited. + * + * A sink may only support some color formats in specific modes and at specific + * bit depths. The atomic modesetting API should be used to set a working + * configuration in one go, as an unsupported combination of parameters is + * rejected. + */ + +/** + * drm_connector_attach_color_format_property - create and attach color format property + * @connector: connector to create the color format property on + * @supported_color_formats: bitmask of bit-shifted &enum drm_output_color_format + * values the connector supports + * + * Called by a driver to create a color format property. The property is + * attached to the connector automatically on success. + * + * @supported_color_formats should only include color formats the connector + * type can actually support. + * + * Returns: + * 0 on success, negative errno on error + */ +int drm_connector_attach_color_format_property(struct drm_connector *connector, + unsigned long supported_color_formats) +{ + struct drm_device *dev = connector->dev; + struct drm_prop_enum_list enum_list[DRM_CONNECTOR_COLOR_FORMAT_COUNT]; + unsigned int i = 0; + unsigned long fmt; + + if (connector->color_format_property) + return 0; + + if (!supported_color_formats) { + drm_err(dev, "No supported color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + if (supported_color_formats & ~GENMASK(DRM_OUTPUT_COLOR_FORMAT_COUNT - 1, 0)) { + drm_err(dev, "Unknown color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_HDMIB: + if (supported_color_formats & ~hdmi_colorformats) { + drm_err(dev, "Color formats not allowed for HDMI on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + if (supported_color_formats & ~dp_colorformats) { + drm_err(dev, "Color formats not allowed for DP on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + } + + enum_list[0].name = "AUTO"; + enum_list[0].type = DRM_CONNECTOR_COLOR_FORMAT_AUTO; + + for_each_set_bit(fmt, &supported_color_formats, DRM_OUTPUT_COLOR_FORMAT_COUNT) { + switch (fmt) { + case DRM_OUTPUT_COLOR_FORMAT_RGB444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_RGB444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR422: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR420: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + break; + default: + drm_warn(dev, "Unknown supported format %ld on [CONNECTOR:%d:%s]\n", + fmt, connector->base.id, connector->name); + continue; + } + enum_list[i].name = drm_hdmi_connector_get_output_format_name(fmt); + } + + connector->color_format_property = + drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "color format", + enum_list, i + 1); + + if (!connector->color_format_property) + return -ENOMEM; + + drm_object_attach_property(&connector->base, connector->color_format_property, + DRM_CONNECTOR_COLOR_FORMAT_AUTO); + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_color_format_property); + /** * drm_connector_atomic_hdr_metadata_equal - checks if the hdr metadata changed * @old_state: old connector state to compare @@ -3530,6 +3699,22 @@ void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode, } EXPORT_SYMBOL(drm_connector_oob_hotplug_event); +/** + * drm_connector_get_color_format - Return connector color format of @conn_state + * @conn_state: pointer to the &struct drm_connector_state to go check + * + */ +enum drm_connector_color_format +drm_connector_get_color_format(const struct drm_connector_state *conn_state) +{ + struct drm_connector *connector = conn_state->connector; + + if (connector->funcs->color_format) + return connector->funcs->color_format(conn_state); + + return conn_state->color_format; +} +EXPORT_SYMBOL(drm_connector_get_color_format); /** * DOC: Tile group diff --git a/drivers/gpu/drm/drm_draw_internal.h b/drivers/gpu/drm/drm_draw_internal.h index 261967145635..44ddcee4744c 100644 --- a/drivers/gpu/drm/drm_draw_internal.h +++ b/drivers/gpu/drm/drm_draw_internal.h @@ -7,7 +7,6 @@ #ifndef __DRM_DRAW_INTERNAL_H__ #define __DRM_DRAW_INTERNAL_H__ -#include #include struct iosys_map; @@ -18,12 +17,6 @@ static inline bool drm_draw_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, in return (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) != 0; } -static inline const u8 *drm_draw_get_char_bitmap(const struct font_desc *font, - char c, size_t font_pitch) -{ - return font->data + (c * font->height) * font_pitch; -} - bool drm_draw_can_convert_from_xrgb8888(u32 format); u32 drm_draw_color_from_xrgb8888(u32 color, u32 format); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 675675480da4..1ff0bf7cba6a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -342,7 +342,9 @@ void drm_minor_release(struct drm_minor *minor) * * platform_set_drvdata(pdev, drm); * - * drm_mode_config_reset(drm); + * ret = drm_mode_config_create_initial_state(drm); + * if (ret) + * return ret; * * ret = drm_dev_register(drm); * if (ret) diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index 2156dbe601c9..8e9ff17538e7 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -32,7 +32,6 @@ #include #include "drm_crtc_internal.h" -#include "drm_internal.h" /** * DOC: overview diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e3ed684ddcf2..018df97d590d 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -232,8 +232,7 @@ void drm_gem_private_object_init(struct drm_device *dev, if (!obj->resv) obj->resv = &obj->_resv; - if (drm_core_check_feature(dev, DRIVER_GEM_GPUVA)) - drm_gem_gpuva_init(obj); + drm_gem_gpuva_init(obj); drm_vma_node_reset(&obj->vma_node); INIT_LIST_HEAD(&obj->lru_node); diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 66f7dc37b597..f432f485a914 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -182,6 +183,87 @@ int drm_mode_getresources(struct drm_device *dev, void *data, return ret; } +static int drm_mode_config_plane_create_state(struct drm_plane *plane) +{ + struct drm_plane_state *plane_state; + + if (!plane->funcs->atomic_create_state) + return 0; + + plane_state = plane->funcs->atomic_create_state(plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + plane->state = plane_state; + + return 0; +} + +static int drm_mode_config_plane_reset_with_create_state(struct drm_plane *plane) +{ + if (plane->state) { + plane->funcs->atomic_destroy_state(plane, plane->state); + plane->state = NULL; + } + + return drm_mode_config_plane_create_state(plane); +} + +static int drm_mode_config_crtc_create_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *crtc_state; + + if (!crtc->funcs->atomic_create_state) + return 0; + + crtc_state = crtc->funcs->atomic_create_state(crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (drm_dev_has_vblank(crtc->dev)) + drm_crtc_vblank_reset(crtc); + + crtc->state = crtc_state; + + return 0; +} + +static int drm_mode_config_crtc_reset_with_create_state(struct drm_crtc *crtc) +{ + if (crtc->state) { + crtc->funcs->atomic_destroy_state(crtc, crtc->state); + crtc->state = NULL; + } + + return drm_mode_config_crtc_create_state(crtc); +} + +static int drm_mode_config_connector_create_state(struct drm_connector *connector) +{ + struct drm_connector_state *conn_state; + + if (!connector->funcs->atomic_create_state) + return 0; + + conn_state = connector->funcs->atomic_create_state(connector); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + + connector->state = conn_state; + + return 0; +} + +static int drm_mode_config_connector_reset_with_create_state(struct drm_connector *connector) +{ + if (connector->state) { + connector->funcs->atomic_destroy_state(connector, connector->state); + connector->state = NULL; + } + + return drm_mode_config_connector_create_state(connector); +} + /** * drm_mode_config_reset - call ->reset callbacks * @dev: drm device @@ -189,6 +271,10 @@ int drm_mode_getresources(struct drm_device *dev, void *data, * This functions calls all the crtc's, encoder's and connector's ->reset * callback. Drivers can use this in e.g. their driver load or resume code to * reset hardware and software state. + * + * Note that &drm_private_obj structures are expected to be stable across + * suspend/resume cycles, and drm_mode_config_reset() does not affect these + * structures. */ void drm_mode_config_reset(struct drm_device *dev) { @@ -202,26 +288,123 @@ void drm_mode_config_reset(struct drm_device *dev) drm_for_each_colorop(colorop, dev) drm_colorop_reset(colorop); - drm_for_each_plane(plane, dev) + drm_for_each_plane(plane, dev) { if (plane->funcs->reset) plane->funcs->reset(plane); + else if (plane->funcs->atomic_create_state) + drm_mode_config_plane_reset_with_create_state(plane); + } - drm_for_each_crtc(crtc, dev) + drm_for_each_crtc(crtc, dev) { if (crtc->funcs->reset) crtc->funcs->reset(crtc); + else if (crtc->funcs->atomic_create_state) + drm_mode_config_crtc_reset_with_create_state(crtc); + } drm_for_each_encoder(encoder, dev) if (encoder->funcs && encoder->funcs->reset) encoder->funcs->reset(encoder); drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->funcs->reset) connector->funcs->reset(connector); + else if (connector->funcs->atomic_create_state) + drm_mode_config_connector_reset_with_create_state(connector); + } drm_connector_list_iter_end(&conn_iter); } EXPORT_SYMBOL(drm_mode_config_reset); +/** + * drm_mode_config_create_initial_state - Allocates the initial state + * @dev: drm device + * + * This functions creates the initial state for all the objects. Drivers + * can use this in e.g. probe to initialize their software state. + * + * It has two main differences with drm_mode_config_reset(): the reset() + * hooks aren't called and thus the hardware will be left untouched, but + * also the &drm_private_obj structures will be initialized as opposed + * to drm_mode_config_reset() that skips them. + * + * Returns: 0 on success, negative error value on failure. + */ +int drm_mode_config_create_initial_state(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_colorop *colorop; + struct drm_plane *plane; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + struct drm_private_obj *privobj; + int ret; + + drm_for_each_privobj(privobj, dev) { + struct drm_private_state *privobj_state; + + if (privobj->state) + continue; + + if (!privobj->funcs->atomic_create_state) + continue; + + privobj_state = privobj->funcs->atomic_create_state(privobj); + if (IS_ERR(privobj_state)) + return PTR_ERR(privobj_state); + + privobj->state = privobj_state; + } + + drm_for_each_colorop(colorop, dev) { + struct drm_colorop_state *colorop_state; + + if (colorop->state) + continue; + + colorop_state = drm_atomic_helper_colorop_create_state(colorop); + if (IS_ERR(colorop_state)) + return PTR_ERR(colorop_state); + + colorop->state = colorop_state; + } + + drm_for_each_plane(plane, dev) { + if (plane->state) + continue; + + ret = drm_mode_config_plane_create_state(plane); + if (ret) + return ret; + } + + drm_for_each_crtc(crtc, dev) { + if (crtc->state) + continue; + + ret = drm_mode_config_crtc_create_state(crtc); + if (ret) + return ret; + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->state) + continue; + + ret = drm_mode_config_connector_create_state(connector); + if (ret) { + drm_connector_list_iter_end(&conn_iter); + return ret; + } + } + drm_connector_list_iter_end(&conn_iter); + + return 0; +} +EXPORT_SYMBOL(drm_mode_config_create_initial_state); + /* * Global properties */ diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index d6d3b8d85dea..e576c4791861 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -443,9 +443,11 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb, rec.x1 += (drm_rect_width(clip) - (line_len * font->width)) / 2; for (j = 0; j < line_len; j++) { - src = drm_draw_get_char_bitmap(font, msg[i].txt[j], font_pitch); + src = font_data_glyph_buf(font->data, font->width, font->height, + (unsigned char)msg[i].txt[j]); rec.x2 = rec.x1 + font->width; - drm_panic_blit(sb, &rec, src, font_pitch, 1, color); + if (src) + drm_panic_blit(sb, &rec, src, font_pitch, 1, color); rec.x1 += font->width; } } diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index ded9461df5f2..86cef1a37678 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -50,8 +50,10 @@ MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug cat "\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n" "\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n" "\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n" +"\t\tBit 6 (0x40) will enable STATE messages (atomic state code)\n" "\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n" -"\t\tBit 8 (0x100) will enable DP messages (displayport code)"); +"\t\tBit 8 (0x100) will enable DP messages (displayport code)\n" +"\t\tBit 9 (0x200) will enable DRMRES messages (managed resources code)"); #if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) module_param_named(debug, __drm_debug, ulong, 0600); diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c index 4da5d6094721..b4a002e6d043 100644 --- a/drivers/gpu/drm/drm_writeback.c +++ b/drivers/gpu/drm/drm_writeback.c @@ -58,6 +58,12 @@ * framebuffer to be written by the writeback connector. This property is * similar to the FB_ID property on planes, but will always read as zero * and is not preserved across commits. + * If the width and height of the framebuffer do not match those of the + * attached CRTC, the driver must either fail or scale (not crop) the + * content to exactly fit the framebuffer. + * If the driver is unable to exactly fill the framebuffer for any reason, + * such as hardware scaler constraints or an odd width for a sub-sampled + * format, the writeback must fail instead of partially filling the buffer. * Userspace must set this property to an output buffer every time it * wishes the buffer to get filled. * diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 947f7c4ff4e9..d17cb5b4a4bf 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -55,36 +55,36 @@ static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); */ static const struct pci_device_id pciidlist[] = { /* Poulsbo */ - { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, - { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, + { PCI_DEVICE(0x8086, 0x8108), .driver_data = (long)&psb_chip_ops }, + { PCI_DEVICE(0x8086, 0x8109), .driver_data = (long)&psb_chip_ops }, /* Oak Trail */ - { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4100), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4101), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4102), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4103), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4104), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4105), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4106), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4107), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4108), .driver_data = (long)&oaktrail_chip_ops }, /* Cedar Trail */ - { 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0, } + { PCI_DEVICE(0x8086, 0x0be0), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be1), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be2), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be3), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be4), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be5), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be6), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be7), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be8), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be9), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bea), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0beb), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bec), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bed), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bee), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bef), .driver_data = (long)&cdv_chip_ops }, + { } }; MODULE_DEVICE_TABLE(pci, pciidlist); diff --git a/drivers/gpu/drm/hyperv/hyperv_drm.h b/drivers/gpu/drm/hyperv/hyperv_drm.h index 9e776112c03e..78136ec2c2f4 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm.h +++ b/drivers/gpu/drm/hyperv/hyperv_drm.h @@ -8,7 +8,7 @@ #define VMBUS_MAX_PACKET_SIZE 0x4000 -struct hyperv_drm_device { +struct hv_drm_device { /* drm */ struct drm_device dev; struct drm_plane plane; @@ -39,17 +39,17 @@ struct hyperv_drm_device { struct hv_device *hdev; }; -#define to_hv(_dev) container_of(_dev, struct hyperv_drm_device, dev) +#define to_hv_drm(_dev) container_of(_dev, struct hv_drm_device, dev) /* hyperv_drm_modeset */ -int hyperv_mode_config_init(struct hyperv_drm_device *hv); +int hv_drm_mode_config_init(struct hv_drm_device *hv); /* hyperv_drm_proto */ -int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp); -int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, +int hv_drm_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp); +int hv_drm_update_situation(struct hv_device *hdev, u8 active, u32 bpp, u32 w, u32 h, u32 pitch); -int hyperv_hide_hw_ptr(struct hv_device *hdev); -int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect); -int hyperv_connect_vsp(struct hv_device *hdev); +int hv_drm_hide_hw_ptr(struct hv_device *hdev); +int hv_drm_update_dirt(struct hv_device *hdev, struct drm_rect *rect); +int hv_drm_connect_vsp(struct hv_device *hdev); #endif diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index b6bf6412ae34..20f35c48c0b8 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -24,9 +24,9 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 -DEFINE_DRM_GEM_FOPS(hv_fops); +DEFINE_DRM_GEM_FOPS(hv_drm_fops); -static struct drm_driver hyperv_driver = { +static struct drm_driver hv_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, .name = DRIVER_NAME, @@ -34,22 +34,22 @@ static struct drm_driver hyperv_driver = { .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, - .fops = &hv_fops, + .fops = &hv_drm_fops, DRM_GEM_SHMEM_DRIVER_OPS, DRM_FBDEV_SHMEM_DRIVER_OPS, }; -static int hyperv_pci_probe(struct pci_dev *pdev, +static int hv_drm_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { return 0; } -static void hyperv_pci_remove(struct pci_dev *pdev) +static void hv_drm_pci_remove(struct pci_dev *pdev) { } -static const struct pci_device_id hyperv_pci_tbl[] = { +static const struct pci_device_id hv_drm_pci_tbl[] = { { .vendor = PCI_VENDOR_ID_MICROSOFT, .device = PCI_DEVICE_ID_HYPERV_VIDEO, @@ -60,14 +60,14 @@ static const struct pci_device_id hyperv_pci_tbl[] = { /* * PCI stub to support gen1 VM. */ -static struct pci_driver hyperv_pci_driver = { +static struct pci_driver hv_drm_pci_driver = { .name = KBUILD_MODNAME, - .id_table = hyperv_pci_tbl, - .probe = hyperv_pci_probe, - .remove = hyperv_pci_remove, + .id_table = hv_drm_pci_tbl, + .probe = hv_drm_pci_probe, + .remove = hv_drm_pci_remove, }; -static int hyperv_setup_vram(struct hyperv_drm_device *hv, +static int hv_drm_setup_vram(struct hv_drm_device *hv, struct hv_device *hdev) { struct drm_device *dev = &hv->dev; @@ -102,15 +102,15 @@ static int hyperv_setup_vram(struct hyperv_drm_device *hv, return ret; } -static int hyperv_vmbus_probe(struct hv_device *hdev, +static int hv_drm_vmbus_probe(struct hv_device *hdev, const struct hv_vmbus_device_id *dev_id) { - struct hyperv_drm_device *hv; + struct hv_drm_device *hv; struct drm_device *dev; int ret; - hv = devm_drm_dev_alloc(&hdev->device, &hyperv_driver, - struct hyperv_drm_device, dev); + hv = devm_drm_dev_alloc(&hdev->device, &hv_drm_driver, + struct hv_drm_device, dev); if (IS_ERR(hv)) return PTR_ERR(hv); @@ -119,15 +119,15 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, hv_set_drvdata(hdev, hv); hv->hdev = hdev; - ret = hyperv_connect_vsp(hdev); + ret = hv_drm_connect_vsp(hdev); if (ret) { drm_err(dev, "Failed to connect to vmbus.\n"); goto err_hv_set_drv_data; } - aperture_remove_all_conflicting_devices(hyperv_driver.name); + aperture_remove_all_conflicting_devices(hv_drm_driver.name); - ret = hyperv_setup_vram(hv, hdev); + ret = hv_drm_setup_vram(hv, hdev); if (ret) goto err_vmbus_close; @@ -136,11 +136,11 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, * vram location is not fatal. Device will update dirty area till * preferred resolution only. */ - ret = hyperv_update_vram_location(hdev, hv->fb_base); + ret = hv_drm_update_vram_location(hdev, hv->fb_base); if (ret) drm_warn(dev, "Failed to update vram location.\n"); - ret = hyperv_mode_config_init(hv); + ret = hv_drm_mode_config_init(hv); if (ret) goto err_free_mmio; @@ -168,10 +168,10 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, return ret; } -static void hyperv_vmbus_remove(struct hv_device *hdev) +static void hv_drm_vmbus_remove(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); - struct hyperv_drm_device *hv = to_hv(dev); + struct hv_drm_device *hv = to_hv_drm(dev); vmbus_set_skip_unload(false); drm_dev_unplug(dev); @@ -183,12 +183,12 @@ static void hyperv_vmbus_remove(struct hv_device *hdev) vmbus_free_mmio(hv->mem->start, hv->fb_size); } -static void hyperv_vmbus_shutdown(struct hv_device *hdev) +static void hv_drm_vmbus_shutdown(struct hv_device *hdev) { drm_atomic_helper_shutdown(hv_get_drvdata(hdev)); } -static int hyperv_vmbus_suspend(struct hv_device *hdev) +static int hv_drm_vmbus_suspend(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); int ret; @@ -202,67 +202,67 @@ static int hyperv_vmbus_suspend(struct hv_device *hdev) return 0; } -static int hyperv_vmbus_resume(struct hv_device *hdev) +static int hv_drm_vmbus_resume(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); - struct hyperv_drm_device *hv = to_hv(dev); + struct hv_drm_device *hv = to_hv_drm(dev); int ret; - ret = hyperv_connect_vsp(hdev); + ret = hv_drm_connect_vsp(hdev); if (ret) return ret; - ret = hyperv_update_vram_location(hdev, hv->fb_base); + ret = hv_drm_update_vram_location(hdev, hv->fb_base); if (ret) return ret; return drm_mode_config_helper_resume(dev); } -static const struct hv_vmbus_device_id hyperv_vmbus_tbl[] = { +static const struct hv_vmbus_device_id hv_drm_vmbus_tbl[] = { /* Synthetic Video Device GUID */ {HV_SYNTHVID_GUID}, {} }; -static struct hv_driver hyperv_hv_driver = { +static struct hv_driver hv_drm_hv_driver = { .name = KBUILD_MODNAME, - .id_table = hyperv_vmbus_tbl, - .probe = hyperv_vmbus_probe, - .remove = hyperv_vmbus_remove, - .shutdown = hyperv_vmbus_shutdown, - .suspend = hyperv_vmbus_suspend, - .resume = hyperv_vmbus_resume, + .id_table = hv_drm_vmbus_tbl, + .probe = hv_drm_vmbus_probe, + .remove = hv_drm_vmbus_remove, + .shutdown = hv_drm_vmbus_shutdown, + .suspend = hv_drm_vmbus_suspend, + .resume = hv_drm_vmbus_resume, .driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; -static int __init hyperv_init(void) +static int __init hv_drm_init(void) { int ret; if (drm_firmware_drivers_only()) return -ENODEV; - ret = pci_register_driver(&hyperv_pci_driver); + ret = pci_register_driver(&hv_drm_pci_driver); if (ret != 0) return ret; - return vmbus_driver_register(&hyperv_hv_driver); + return vmbus_driver_register(&hv_drm_hv_driver); } -static void __exit hyperv_exit(void) +static void __exit hv_drm_exit(void) { - vmbus_driver_unregister(&hyperv_hv_driver); - pci_unregister_driver(&hyperv_pci_driver); + vmbus_driver_unregister(&hv_drm_hv_driver); + pci_unregister_driver(&hv_drm_pci_driver); } -module_init(hyperv_init); -module_exit(hyperv_exit); +module_init(hv_drm_init); +module_exit(hv_drm_exit); -MODULE_DEVICE_TABLE(pci, hyperv_pci_tbl); -MODULE_DEVICE_TABLE(vmbus, hyperv_vmbus_tbl); +MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl); +MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Deepak Rawat "); MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device"); diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 793dbbf61893..1855749c1e41 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -25,11 +25,11 @@ #include "hyperv_drm.h" -static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb, +static int hv_drm_blit_to_vram_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, struct drm_rect *rect) { - struct hyperv_drm_device *hv = to_hv(fb->dev); + struct hv_drm_device *hv = to_hv_drm(fb->dev); struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram); int idx; @@ -44,9 +44,9 @@ static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb, return 0; } -static int hyperv_connector_get_modes(struct drm_connector *connector) +static int hv_drm_connector_get_modes(struct drm_connector *connector) { - struct hyperv_drm_device *hv = to_hv(connector->dev); + struct hv_drm_device *hv = to_hv_drm(connector->dev); int count; count = drm_add_modes_noedid(connector, @@ -58,11 +58,11 @@ static int hyperv_connector_get_modes(struct drm_connector *connector) return count; } -static const struct drm_connector_helper_funcs hyperv_connector_helper_funcs = { - .get_modes = hyperv_connector_get_modes, +static const struct drm_connector_helper_funcs hv_drm_connector_helper_funcs = { + .get_modes = hv_drm_connector_get_modes, }; -static const struct drm_connector_funcs hyperv_connector_funcs = { +static const struct drm_connector_funcs hv_drm_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, @@ -70,15 +70,15 @@ static const struct drm_connector_funcs hyperv_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static inline int hyperv_conn_init(struct hyperv_drm_device *hv) +static inline int hv_drm_conn_init(struct hv_drm_device *hv) { - drm_connector_helper_add(&hv->connector, &hyperv_connector_helper_funcs); + drm_connector_helper_add(&hv->connector, &hv_drm_connector_helper_funcs); return drm_connector_init(&hv->dev, &hv->connector, - &hyperv_connector_funcs, + &hv_drm_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); } -static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h, +static int hv_drm_check_size(struct hv_drm_device *hv, int w, int h, struct drm_framebuffer *fb) { u32 pitch = w * (hv->screen_depth / 8); @@ -92,25 +92,25 @@ static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h, return 0; } -static const uint32_t hyperv_formats[] = { +static const uint32_t hv_drm_formats[] = { DRM_FORMAT_XRGB8888, }; -static const uint64_t hyperv_modifiers[] = { +static const uint64_t hv_drm_modifiers[] = { DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID }; -static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc, +static void hv_drm_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state) { - struct hyperv_drm_device *hv = to_hv(crtc->dev); + struct hv_drm_device *hv = to_hv_drm(crtc->dev); struct drm_plane *plane = &hv->plane; struct drm_plane_state *plane_state = plane->state; struct drm_crtc_state *crtc_state = crtc->state; - hyperv_hide_hw_ptr(hv->hdev); - hyperv_update_situation(hv->hdev, 1, hv->screen_depth, + hv_drm_hide_hw_ptr(hv->hdev); + hv_drm_update_situation(hv->hdev, 1, hv->screen_depth, crtc_state->mode.hdisplay, crtc_state->mode.vdisplay, plane_state->fb->pitches[0]); @@ -118,14 +118,14 @@ static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc, drm_crtc_vblank_on(crtc); } -static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = { +static const struct drm_crtc_helper_funcs hv_drm_crtc_helper_funcs = { .atomic_check = drm_crtc_helper_atomic_check, .atomic_flush = drm_crtc_vblank_atomic_flush, - .atomic_enable = hyperv_crtc_helper_atomic_enable, + .atomic_enable = hv_drm_crtc_helper_atomic_enable, .atomic_disable = drm_crtc_vblank_atomic_disable, }; -static const struct drm_crtc_funcs hyperv_crtc_funcs = { +static const struct drm_crtc_funcs hv_drm_crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, @@ -135,11 +135,11 @@ static const struct drm_crtc_funcs hyperv_crtc_funcs = { DRM_CRTC_VBLANK_TIMER_FUNCS, }; -static int hyperv_plane_atomic_check(struct drm_plane *plane, +static int hv_drm_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state) { struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct drm_framebuffer *fb = plane_state->fb; struct drm_crtc *crtc = plane_state->crtc; struct drm_crtc_state *crtc_state = NULL; @@ -167,10 +167,10 @@ static int hyperv_plane_atomic_check(struct drm_plane *plane, return 0; } -static void hyperv_plane_atomic_update(struct drm_plane *plane, +static void hv_drm_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state) { - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state); @@ -185,15 +185,15 @@ static void hyperv_plane_atomic_update(struct drm_plane *plane, if (!drm_rect_intersect(&dst_clip, &damage)) continue; - hyperv_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage); - hyperv_update_dirt(hv->hdev, &damage); + hv_drm_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage); + hv_drm_update_dirt(hv->hdev, &damage); } } -static int hyperv_plane_get_scanout_buffer(struct drm_plane *plane, +static int hv_drm_plane_get_scanout_buffer(struct drm_plane *plane, struct drm_scanout_buffer *sb) { - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram); if (plane->state && plane->state->fb) { @@ -207,9 +207,9 @@ static int hyperv_plane_get_scanout_buffer(struct drm_plane *plane, return -ENODEV; } -static void hyperv_plane_panic_flush(struct drm_plane *plane) +static void hv_drm_plane_panic_flush(struct drm_plane *plane) { - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct drm_rect rect; if (plane->state && plane->state->fb) { @@ -218,32 +218,32 @@ static void hyperv_plane_panic_flush(struct drm_plane *plane) rect.x2 = plane->state->fb->width; rect.y2 = plane->state->fb->height; - hyperv_update_dirt(hv->hdev, &rect); + hv_drm_update_dirt(hv->hdev, &rect); } vmbus_initiate_unload(true); } -static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = { +static const struct drm_plane_helper_funcs hv_drm_plane_helper_funcs = { DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, - .atomic_check = hyperv_plane_atomic_check, - .atomic_update = hyperv_plane_atomic_update, - .get_scanout_buffer = hyperv_plane_get_scanout_buffer, - .panic_flush = hyperv_plane_panic_flush, + .atomic_check = hv_drm_plane_atomic_check, + .atomic_update = hv_drm_plane_atomic_update, + .get_scanout_buffer = hv_drm_plane_get_scanout_buffer, + .panic_flush = hv_drm_plane_panic_flush, }; -static const struct drm_plane_funcs hyperv_plane_funcs = { +static const struct drm_plane_funcs hv_drm_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, DRM_GEM_SHADOW_PLANE_FUNCS, }; -static const struct drm_encoder_funcs hyperv_drm_simple_encoder_funcs_cleanup = { +static const struct drm_encoder_funcs hv_drm_simple_encoder_funcs_cleanup = { .destroy = drm_encoder_cleanup, }; -static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) +static inline int hv_drm_pipe_init(struct hv_drm_device *hv) { struct drm_device *dev = &hv->dev; struct drm_encoder *encoder = &hv->encoder; @@ -253,29 +253,29 @@ static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) int ret; ret = drm_universal_plane_init(dev, plane, 0, - &hyperv_plane_funcs, - hyperv_formats, ARRAY_SIZE(hyperv_formats), - hyperv_modifiers, + &hv_drm_plane_funcs, + hv_drm_formats, ARRAY_SIZE(hv_drm_formats), + hv_drm_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) return ret; - drm_plane_helper_add(plane, &hyperv_plane_helper_funcs); + drm_plane_helper_add(plane, &hv_drm_plane_helper_funcs); drm_plane_enable_fb_damage_clips(plane); ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, - &hyperv_crtc_funcs, NULL); + &hv_drm_crtc_funcs, NULL); if (ret) return ret; - drm_crtc_helper_add(crtc, &hyperv_crtc_helper_funcs); + drm_crtc_helper_add(crtc, &hv_drm_crtc_helper_funcs); encoder->possible_crtcs = drm_crtc_mask(crtc); ret = drm_encoder_init(dev, encoder, - &hyperv_drm_simple_encoder_funcs_cleanup, + &hv_drm_simple_encoder_funcs_cleanup, DRM_MODE_ENCODER_NONE, NULL); if (ret) return ret; - ret = hyperv_conn_init(hv); + ret = hv_drm_conn_init(hv); if (ret) { drm_err(dev, "Failed to initialized connector.\n"); return ret; @@ -285,25 +285,25 @@ static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) } static enum drm_mode_status -hyperv_mode_valid(struct drm_device *dev, +hv_drm_mode_valid(struct drm_device *dev, const struct drm_display_mode *mode) { - struct hyperv_drm_device *hv = to_hv(dev); + struct hv_drm_device *hv = to_hv_drm(dev); - if (hyperv_check_size(hv, mode->hdisplay, mode->vdisplay, NULL)) + if (hv_drm_check_size(hv, mode->hdisplay, mode->vdisplay, NULL)) return MODE_BAD; return MODE_OK; } -static const struct drm_mode_config_funcs hyperv_mode_config_funcs = { +static const struct drm_mode_config_funcs hv_drm_mode_config_funcs = { .fb_create = drm_gem_fb_create_with_dirty, - .mode_valid = hyperv_mode_valid, + .mode_valid = hv_drm_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; -int hyperv_mode_config_init(struct hyperv_drm_device *hv) +int hv_drm_mode_config_init(struct hv_drm_device *hv) { struct drm_device *dev = &hv->dev; int ret; @@ -322,9 +322,9 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv) dev->mode_config.preferred_depth = hv->screen_depth; dev->mode_config.prefer_shadow = 0; - dev->mode_config.funcs = &hyperv_mode_config_funcs; + dev->mode_config.funcs = &hv_drm_mode_config_funcs; - ret = hyperv_pipe_init(hv); + ret = hv_drm_pipe_init(hv); if (ret) { drm_err(dev, "Failed to initialized pipe.\n"); return ret; diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index 6e09b0218df4..f0ef627b4898 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -181,7 +181,7 @@ struct synthvid_msg { }; } __packed; -static inline bool hyperv_version_ge(u32 ver1, u32 ver2) +static inline bool hv_drm_version_ge(u32 ver1, u32 ver2) { if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && @@ -191,10 +191,10 @@ static inline bool hyperv_version_ge(u32 ver1, u32 ver2) return false; } -static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg) +static inline int hv_drm_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg) { static atomic64_t request_id = ATOMIC64_INIT(0); - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); int ret; msg->pipe_hdr.type = PIPE_MSG_DATA; @@ -211,9 +211,9 @@ static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg return ret; } -static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) +static int hv_drm_negotiate_version(struct hv_device *hdev, u32 ver) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; struct drm_device *dev = &hv->dev; unsigned long t; @@ -223,7 +223,7 @@ static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + sizeof(struct synthvid_version_req); msg->ver_req.version = ver; - hyperv_sendpacket(hdev, msg); + hv_drm_sendpacket(hdev, msg); t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); if (!t) { @@ -243,9 +243,9 @@ static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) return 0; } -int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) +int hv_drm_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; struct drm_device *dev = &hv->dev; unsigned long t; @@ -257,7 +257,7 @@ int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) msg->vram.user_ctx = vram_pp; msg->vram.vram_gpa = vram_pp; msg->vram.is_vram_gpa_specified = 1; - hyperv_sendpacket(hdev, msg); + hv_drm_sendpacket(hdev, msg); t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); if (!t) { @@ -272,7 +272,7 @@ int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) return 0; } -int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, +int hv_drm_update_situation(struct hv_device *hdev, u8 active, u32 bpp, u32 w, u32 h, u32 pitch) { struct synthvid_msg msg; @@ -292,7 +292,7 @@ int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, msg.situ.video_output[0].height_pixels = h; msg.situ.video_output[0].pitch_bytes = pitch; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); return 0; } @@ -306,11 +306,11 @@ int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, * the msg.ptr_shape.data. Note: setting msg.ptr_pos.is_visible to 0 doesn't * work in tests. * - * The hyperv_hide_hw_ptr() is also called in the handler of the + * The hv_drm_hide_hw_ptr() is also called in the handler of the * SYNTHVID_FEATURE_CHANGE event, otherwise the host still draws an extra * unwanted mouse pointer after the VM Connection window is closed and reopened. */ -int hyperv_hide_hw_ptr(struct hv_device *hdev) +int hv_drm_hide_hw_ptr(struct hv_device *hdev) { struct synthvid_msg msg; @@ -322,7 +322,7 @@ int hyperv_hide_hw_ptr(struct hv_device *hdev) msg.ptr_pos.video_output = 0; msg.ptr_pos.image_x = 0; msg.ptr_pos.image_y = 0; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); memset(&msg, 0, sizeof(struct synthvid_msg)); msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE; @@ -338,14 +338,14 @@ int hyperv_hide_hw_ptr(struct hv_device *hdev) msg.ptr_shape.data[1] = 1; msg.ptr_shape.data[2] = 1; msg.ptr_shape.data[3] = 1; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); return 0; } -int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect) +int hv_drm_update_dirt(struct hv_device *hdev, struct drm_rect *rect) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg msg; if (!hv->dirt_needed) @@ -363,14 +363,14 @@ int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect) msg.dirt.rect[0].x2 = rect->x2; msg.dirt.rect[0].y2 = rect->y2; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); return 0; } -static int hyperv_get_supported_resolution(struct hv_device *hdev) +static int hv_drm_get_supported_resolution(struct hv_device *hdev) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; struct drm_device *dev = &hv->dev; unsigned long t; @@ -383,7 +383,7 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) sizeof(struct synthvid_supported_resolution_req); msg->resolution_req.maximum_resolution_count = SYNTHVID_MAX_RESOLUTION_COUNT; - hyperv_sendpacket(hdev, msg); + hv_drm_sendpacket(hdev, msg); t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); if (!t) { @@ -420,9 +420,9 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return 0; } -static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) +static void hv_drm_receive_sub(struct hv_device *hdev, u32 bytes_recvd) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg; size_t hdr_size; size_t need; @@ -486,7 +486,7 @@ static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) } hv->dirt_needed = msg->feature_chg.is_dirt_needed; if (hv->dirt_needed) - hyperv_hide_hw_ptr(hv->hdev); + hv_drm_hide_hw_ptr(hv->hdev); return; default: return; @@ -508,10 +508,10 @@ static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) complete(&hv->wait); } -static void hyperv_receive(void *ctx) +static void hv_drm_receive(void *ctx) { struct hv_device *hdev = ctx; - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *recv_buf; u32 bytes_recvd; u64 req_id; @@ -539,19 +539,19 @@ static void hyperv_receive(void *ctx) ret, bytes_recvd); } else if (bytes_recvd > 0 && recv_buf->pipe_hdr.type == PIPE_MSG_DATA) { - hyperv_receive_sub(hdev, bytes_recvd); + hv_drm_receive_sub(hdev, bytes_recvd); } } while (bytes_recvd > 0 && ret == 0); } -int hyperv_connect_vsp(struct hv_device *hdev) +int hv_drm_connect_vsp(struct hv_device *hdev) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct drm_device *dev = &hv->dev; int ret; ret = vmbus_open(hdev->channel, VMBUS_RING_BUFSIZE, VMBUS_RING_BUFSIZE, - NULL, 0, hyperv_receive, hdev); + NULL, 0, hv_drm_receive, hdev); if (ret) { drm_err(dev, "Unable to open vmbus channel\n"); return ret; @@ -561,16 +561,16 @@ int hyperv_connect_vsp(struct hv_device *hdev) switch (vmbus_proto_version) { case VERSION_WIN10: case VERSION_WIN10_V5: - ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); + ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); if (!ret) break; fallthrough; case VERSION_WIN8: case VERSION_WIN8_1: - ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8); + ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN8); break; default: - ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); + ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); break; } @@ -581,8 +581,8 @@ int hyperv_connect_vsp(struct hv_device *hdev) hv->screen_depth = SYNTHVID_DEPTH_WIN8; - if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) { - ret = hyperv_get_supported_resolution(hdev); + if (hv_drm_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) { + ret = hv_drm_get_supported_resolution(hdev); if (ret) drm_err(dev, "Failed to get supported resolution from host, use default\n"); } diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index 7ce2b52297d1..10ed9bdfee76 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -182,7 +182,7 @@ void intel_crtc_state_reset(struct intel_crtc_state *crtc_state, { memset(crtc_state, 0, sizeof(*crtc_state)); - __drm_atomic_helper_crtc_state_reset(&crtc_state->uapi, &crtc->base); + __drm_atomic_helper_crtc_state_init(&crtc_state->uapi, &crtc->base); crtc_state->cpu_transcoder = INVALID_TRANSCODER; crtc_state->master_transcoder = INVALID_TRANSCODER; diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index e087554488b4..7017ed6cb9d1 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -67,7 +67,7 @@ static void intel_plane_state_reset(struct intel_plane_state *plane_state, { memset(plane_state, 0, sizeof(*plane_state)); - __drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base); + __drm_atomic_helper_plane_state_init(&plane_state->uapi, &plane->base); plane_state->scaler_id = -1; plane_state->fence_id = -1; diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index 58087e8517d6..8897b3bdeb4a 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -1378,7 +1378,7 @@ pvr_drm_driver_postclose(__always_unused struct drm_device *drm_dev, DEFINE_DRM_GEM_FOPS(pvr_drm_driver_fops); static struct drm_driver pvr_drm_driver = { - .driver_features = DRIVER_GEM | DRIVER_GEM_GPUVA | DRIVER_RENDER | + .driver_features = DRIVER_GEM | DRIVER_RENDER | DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE, .open = pvr_drm_driver_open, .postclose = pvr_drm_driver_postclose, diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c index 34405073c4d4..3fd828d23271 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.c +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -444,8 +444,8 @@ static const struct dev_pm_ops lsdc_pm_ops = { }; static const struct pci_device_id lsdc_pciid_list[] = { - {PCI_VDEVICE(LOONGSON, 0x7a06), CHIP_LS7A1000}, - {PCI_VDEVICE(LOONGSON, 0x7a36), CHIP_LS7A2000}, + { PCI_VDEVICE(LOONGSON, 0x7a06), .driver_data = CHIP_LS7A1000 }, + { PCI_VDEVICE(LOONGSON, 0x7a36), .driver_data = CHIP_LS7A2000 }, { } }; diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index d7b5dfdb28aa..dfae5812284d 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -204,18 +204,18 @@ int mgag200_device_init(struct mga_device *mdev, */ static const struct pci_device_id mgag200_pciidlist[] = { - { PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI }, - { PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP }, - { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A }, - { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B }, - { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV }, - { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB }, - { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH }, - { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER }, - { PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 }, - { PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 }, - { PCI_VENDOR_ID_MATROX, 0x53a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH5 }, - {0,} + { PCI_VDEVICE(MATROX, 0x0520), .driver_data = G200_PCI }, + { PCI_VDEVICE(MATROX, 0x0521), .driver_data = G200_AGP }, + { PCI_VDEVICE(MATROX, 0x0522), .driver_data = G200_SE_A }, + { PCI_VDEVICE(MATROX, 0x0524), .driver_data = G200_SE_B }, + { PCI_VDEVICE(MATROX, 0x0530), .driver_data = G200_EV }, + { PCI_VDEVICE(MATROX, 0x0532), .driver_data = G200_WB }, + { PCI_VDEVICE(MATROX, 0x0533), .driver_data = G200_EH }, + { PCI_VDEVICE(MATROX, 0x0534), .driver_data = G200_ER }, + { PCI_VDEVICE(MATROX, 0x0536), .driver_data = G200_EW3 }, + { PCI_VDEVICE(MATROX, 0x0538), .driver_data = G200_EH3 }, + { PCI_VDEVICE(MATROX, 0x053a), .driver_data = G200_EH5 }, + { } }; MODULE_DEVICE_TABLE(pci, mgag200_pciidlist); diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 982abaaac00d..de1ba11987fc 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -166,6 +166,7 @@ struct msm_dsi_host { struct drm_display_mode *mode; struct drm_dsc_config *dsc; + unsigned int dsc_slice_per_pkt; /* connected device info */ unsigned int channel; @@ -938,17 +939,10 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod slice_per_intf = dsc->slice_count; total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; - bytes_per_pkt = dsc->slice_chunk_size; /* * slice_per_pkt; */ + bytes_per_pkt = dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt; eol_byte_num = total_bytes_per_intf % 3; - - /* - * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. - * - * Since the current driver only supports slice_per_pkt = 1, - * pkt_per_line will be equal to slice per intf for now. - */ - pkt_per_line = slice_per_intf; + pkt_per_line = slice_per_intf / msm_host->dsc_slice_per_pkt; if (is_cmd_mode) /* packet data type */ reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); @@ -1104,12 +1098,8 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) else /* * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. - * Currently, the driver only supports default value of slice_per_pkt = 1 - * - * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info - * and adjust DSC math to account for slice_per_pkt. */ - wc = msm_host->dsc->slice_chunk_size + 1; + wc = msm_host->dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt + 1; dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | @@ -1718,8 +1708,13 @@ static int dsi_host_attach(struct mipi_dsi_host *host, msm_host->lanes = dsi->lanes; msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; - if (dsi->dsc) + if (dsi->dsc) { msm_host->dsc = dsi->dsc; + if (dsi->mode_flags & MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT) + msm_host->dsc_slice_per_pkt = dsi->dsc->slice_count; + else + msm_host->dsc_slice_per_pkt = 1; + } if (msm_host->format == MIPI_DSI_FMT_RGB101010) { if (!msm_dsi_host_version_geq(msm_host, MSM_DSI_VER_MAJOR_6G, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 32d5ebea2596..ac86b427c0e5 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -824,7 +824,6 @@ static const struct file_operations fops = { #define DRIVER_FEATURES_GPU ( \ DRIVER_GEM | \ - DRIVER_GEM_GPUVA | \ DRIVER_RENDER | \ DRIVER_SYNCOBJ | \ DRIVER_SYNCOBJ_TIMELINE | \ @@ -832,7 +831,6 @@ static const struct file_operations fops = { #define DRIVER_FEATURES_KMS ( \ DRIVER_GEM | \ - DRIVER_GEM_GPUVA | \ DRIVER_ATOMIC | \ DRIVER_MODESET | \ 0 ) diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c index ade76c3f4e4e..79e0f483ca20 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_kms.c +++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c @@ -338,7 +338,8 @@ static void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags) * Downstream set it to 256B burst size to improve the memory * efficiency so set it here too. */ - ctrl = CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) | + ctrl = CTRLDESCL0_3_STATE_CLEAR_VSYNC | + CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) | CTRLDESCL0_3_PITCH(lcdif->crtc.primary->state->fb->pitches[0]); writel(ctrl, lcdif->base + LCDC_V8_CTRLDESCL0_3); } @@ -374,14 +375,23 @@ static void lcdif_disable_controller(struct lcdif_drm_private *lcdif) int ret; reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5); + /* Disable the layer for DMA. */ reg &= ~CTRLDESCL0_5_EN; + /* + * It is necessary to wait for the full frame to finish streaming + * through the DMA engine before we can safely disable it by removing + * the DISP_PARA_DISP_ON bit. Disabling it in-flight can leave the + * hardware confused and unable to resume streaming for the next frame. + */ + reg |= CTRLDESCL0_5_SHADOW_LOAD_EN; writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5); + /* Wait for the frame to finish or timeout after 50 ms. */ ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5, - reg, !(reg & CTRLDESCL0_5_EN), - 0, 36000); /* Wait ~2 frame times max */ + reg, !(reg & CTRLDESCL0_5_SHADOW_LOAD_EN), + 200, 50000); if (ret) - drm_err(lcdif->drm, "Failed to disable controller!\n"); + drm_err(lcdif->drm, "Timed out waiting for final vblank!\n"); reg = readl(lcdif->base + LCDC_V8_DISP_PARA); reg &= ~DISP_PARA_DISP_ON; diff --git a/drivers/gpu/drm/mxsfb/lcdif_regs.h b/drivers/gpu/drm/mxsfb/lcdif_regs.h index c55dfb236c1d..17882c593d27 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_regs.h +++ b/drivers/gpu/drm/mxsfb/lcdif_regs.h @@ -190,6 +190,7 @@ #define CTRLDESCL0_1_WIDTH(n) ((n) & 0xffff) #define CTRLDESCL0_1_WIDTH_MASK GENMASK(15, 0) +#define CTRLDESCL0_3_STATE_CLEAR_VSYNC BIT(23) #define CTRLDESCL0_3_P_SIZE(n) (((n) << 20) & CTRLDESCL0_3_P_SIZE_MASK) #define CTRLDESCL0_3_P_SIZE_MASK GENMASK(22, 20) #define CTRLDESCL0_3_T_SIZE(n) (((n) << 16) & CTRLDESCL0_3_T_SIZE_MASK) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index e16f59b00f6f..42a81166f3a9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1364,7 +1364,6 @@ static struct drm_driver driver_stub = { .driver_features = DRIVER_GEM | DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE | - DRIVER_GEM_GPUVA | DRIVER_MODESET | DRIVER_RENDER, .open = nouveau_drm_open, diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 7450b27622a2..c0cc2bb4a3eb 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -17,6 +17,16 @@ config DRM_PANEL_ABT_Y030XX067A Y030XX067A 320x480 3.0" panel as found in the YLM RG-280M, RG-300 and RG-99 handheld gaming consoles. +config DRM_PANEL_ANBERNIC_TD4310 + tristate "Anbernic TD4310 LCD panel" + depends on GPIOLIB && OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here to enable support for Anbernic designed panels with the + TD4310 panel controller such as the ones used on the Anbernic RG + Vita Pro. + config DRM_PANEL_ARM_VERSATILE tristate "ARM Versatile panel driver" depends on OF @@ -105,6 +115,17 @@ config DRM_PANEL_BOE_TV101WUM_LL2 Say Y here if you want to support for BOE TV101WUM-LL2 WUXGA PANEL DSI Video Mode panel +config DRM_PANEL_CHIPONE_ICNA35XX + tristate "Chipone ICNA35XX panel driver" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER + help + Say Y here if you want to enable support for the panels built + around the Chipone ICNA3512 and ICNA3520 display controllers, + such as some Tianma panels used in AYN Odin2 Portal and Thor. + config DRM_PANEL_CHIPWEALTH_CH13726A tristate "CHIPWEALTH CH13726A-based DSI panel" depends on OF @@ -284,6 +305,15 @@ config DRM_PANEL_ILITEK_ILI9341 QVGA (240x320) RGB panels. support serial & parallel rgb interface. +config DRM_PANEL_ILITEK_ILI9488 + tristate "Ilitek ILI9488-based panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y if you want to enable support for panels based on the + Ilitek ILI9488 controller. + config DRM_PANEL_ILITEK_ILI9805 tristate "Ilitek ILI9805-based panels" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index c2c5cf817116..6926ef28ab8d 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o +obj-$(CONFIG_DRM_PANEL_ANBERNIC_TD4310) += panel-anbernic-td4310.o obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o @@ -9,6 +10,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o +obj-$(CONFIG_DRM_PANEL_CHIPONE_ICNA35XX) += panel-chipone-icna35xx.o obj-$(CONFIG_DRM_PANEL_CHIPWEALTH_CH13726A) += panel-chipwealth-ch13726a.o obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o @@ -28,6 +30,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o +obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9488) += panel-ilitek-ili9488.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9805) += panel-ilitek-ili9805.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_CORE) += panel-ilitek-ili9806e-core.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_DSI) += panel-ilitek-ili9806e-dsi.o diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c new file mode 100644 index 000000000000..9a1b4525423c --- /dev/null +++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Anbernic panels with TD4310 panel controller. + * + * Copyright (C) 2026 Chris Morgan + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include