drm-misc-next for $kernel-version: UAPI Changes: Cross-subsystem Changes: - dma-buf: Avoid a warning with some allocations, Remove DMA_FENCE_TRACE macros Core Changes: - bridge: New helper to git rid of panels in drivers - fence: Improve dma_fence_add_callback documentation, Improve dma_fence_ops->wait documentation - ioctl: Unexport drm_ioctl_permit - lease: Documentation improvements - fourcc: Add new macro to determine the modifier vendor - quirks: Add the Steam Deck, Chuwi HiBook, Chuwi Hi10 Pro, Samsung Galaxy Book 10.6, KD Kurio Smart C15200 2-in-1, Lenovo Ideapad D330 - resv: Improve the documentation - shmem-helpers: Allocate WC pages on x86, Switch to vmf_insert_pfn - sched: Fix for a timer being canceled too soon, Avoid null pointer derefence if the fence is null in drm_sched_fence_free, Convert drivers to rely on its dependency tracking - ttm: Switch to kerneldoc, new helper to clear all DMA mappings, pool shrinker optitimization, Remove ttm_tt_destroy_common, Fix for unbinding on multiple drivers Driver Changes: - bochs: New PCI IDs - msm: Fence ordering impromevemnts - stm: Add layer alpha support, zpos - v3d: Fix for a Vulkan CTS failure - vc4: Conversion to the new bridge helpers - vgem: Use shmem helpers - virtio: Support mapping exported vram - zte: Remove obsolete driver - bridge: Probe improvements for it66121, enable DSI EOTP for anx7625, errors propagation improvements for anx7625 - panels: 60fps mode for otm8009a, New driver for Samsung S6D27A1 Signed-off-by: Dave Airlie <airlied@redhat.com> # gpg: Signature made Thu 16 Sep 2021 17:30:50 AEST # gpg: using EDDSA key 5C1337A45ECA9AEB89060E9EE3EF0D6F671851C5 # gpg: Can't check signature: No public key From: Maxime Ripard <maxime@cerno.tech> Link: https://patchwork.freedesktop.org/patch/msgid/20210916073132.ptbbmjetm7v3ufq3@gilmour
Unified · Split
| @@ -22,7 +22,7 @@ properties: · 21 unmodified lines | ||
| 22 | 22 | items: |
| 23 | 23 | - enum: |
| 24 | 24 | # ili9341 240*320 Color on stm32f429-disco board |
| 25 | − - st,sf-tc240t-9370-t | |
| 25 | + - st,sf-tc240t-9370-t | |
| 26 | 26 | - const: ilitek,ili9341 |
| 27 | 27 | |
| 28 | 28 | reg: true |
| @@ -0,0 +1,98 @@ | ||
| 1 | +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) | |
| 2 | +%YAML 1.2 | |
| 3 | +--- | |
| 4 | +$id: http://devicetree.org/schemas/display/panel/samsung,s6d27a1.yaml# | |
| 5 | +$schema: http://devicetree.org/meta-schemas/core.yaml# | |
| 6 | + | |
| 7 | +title: Samsung S6D27A1 display panel | |
| 8 | + | |
| 9 | +description: The S6D27A1 is a 480x800 DPI display panel from Samsung Mobile | |
| 10 | + Displays (SMD). The panel must obey the rules for a SPI slave device | |
| 11 | + as specified in spi/spi-controller.yaml | |
| 12 | + | |
| 13 | +maintainers: | |
| 14 | + - Markuss Broks <markuss.broks@gmail.com> | |
| 15 | + | |
| 16 | +allOf: | |
| 17 | + - $ref: panel-common.yaml# | |
| 18 | + | |
| 19 | +properties: | |
| 20 | + compatible: | |
| 21 | + const: samsung,s6d27a1 | |
| 22 | + | |
| 23 | + reg: true | |
| 24 | + | |
| 25 | + interrupts: | |
| 26 | + description: provides an optional ESD (electrostatic discharge) | |
| 27 | + interrupt that signals abnormalities in the display hardware. | |
| 28 | + This can also be raised for other reasons like erroneous | |
| 29 | + configuration. | |
| 30 | + maxItems: 1 | |
| 31 | + | |
| 32 | + reset-gpios: true | |
| 33 | + | |
| 34 | + vci-supply: | |
| 35 | + description: regulator that supplies the VCI analog voltage | |
| 36 | + usually around 3.0 V | |
| 37 | + | |
| 38 | + vccio-supply: | |
| 39 | + description: regulator that supplies the VCCIO voltage usually | |
| 40 | + around 1.8 V | |
| 41 | + | |
| 42 | + backlight: true | |
| 43 | + | |
| 44 | + spi-cpha: true | |
| 45 | + | |
| 46 | + spi-cpol: true | |
| 47 | + | |
| 48 | + spi-max-frequency: | |
| 49 | + maximum: 1200000 | |
| 50 | + | |
| 51 | + port: true | |
| 52 | + | |
| 53 | +required: | |
| 54 | + - compatible | |
| 55 | + - reg | |
| 56 | + - vci-supply | |
| 57 | + - vccio-supply | |
| 58 | + - spi-cpha | |
| 59 | + - spi-cpol | |
| 60 | + - port | |
| 61 | + | |
| 62 | +unevaluatedProperties: false | |
| 63 | + | |
| 64 | +examples: | |
| 65 | + - | | |
| 66 | + #include <dt-bindings/gpio/gpio.h> | |
| 67 | + #include <dt-bindings/interrupt-controller/irq.h> | |
| 68 | + | |
| 69 | + spi { | |
| 70 | + compatible = "spi-gpio"; | |
| 71 | + sck-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>; | |
| 72 | + miso-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>; | |
| 73 | + mosi-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>; | |
| 74 | + cs-gpios = <&gpio 3 GPIO_ACTIVE_HIGH>; | |
| 75 | + num-chipselects = <1>; | |
| 76 | + #address-cells = <1>; | |
| 77 | + #size-cells = <0>; | |
| 78 | + panel@0 { | |
| 79 | + compatible = "samsung,s6d27a1"; | |
| 80 | + spi-max-frequency = <1200000>; | |
| 81 | + spi-cpha; | |
| 82 | + spi-cpol; | |
| 83 | + reg = <0>; | |
| 84 | + vci-supply = <&lcd_3v0_reg>; | |
| 85 | + vccio-supply = <&lcd_1v8_reg>; | |
| 86 | + reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>; | |
| 87 | + interrupt-parent = <&gpio>; | |
| 88 | + interrupts = <5 IRQ_TYPE_EDGE_RISING>; | |
| 89 | + | |
| 90 | + port { | |
| 91 | + panel_in: endpoint { | |
| 92 | + remote-endpoint = <&display_out>; | |
| 93 | + }; | |
| 94 | + }; | |
| 95 | + }; | |
| 96 | + }; | |
| 97 | + | |
| 98 | +... | |
| @@ -176,12 +176,6 @@ DMA Fences Functions Reference · 175 unmodified lines | ||
| 176 | 176 | .. kernel-doc:: include/linux/dma-fence.h |
| 177 | 177 | :internal: |
| 178 | 178 | |
| 179 | −Seqno Hardware Fences | |
| 180 | −~~~~~~~~~~~~~~~~~~~~~ | |
| 181 | − | |
| 182 | −.. kernel-doc:: include/linux/seqno-fence.h | |
| 183 | − :internal: | |
| 184 | − | |
| 185 | 179 | DMA Fence Array |
| 186 | 180 | ~~~~~~~~~~~~~~~ |
| 187 | 181 | |
| @@ -28,56 +28,53 @@ UMA devices. · 27 unmodified lines | ||
| 28 | 28 | The Translation Table Manager (TTM) |
| 29 | 29 | =================================== |
| 30 | 30 | |
| 31 | −TTM design background and information belongs here. | |
| 31 | +.. kernel-doc:: drivers/gpu/drm/ttm/ttm_module.c | |
| 32 | + :doc: TTM | |
| 32 | 33 | |
| 33 | −TTM initialization | |
| 34 | −------------------ | |
| 34 | +.. kernel-doc:: include/drm/ttm/ttm_caching.h | |
| 35 | + :internal: | |
| 35 | 36 | |
| 36 | − **Warning** | |
| 37 | − This section is outdated. | |
| 37 | +TTM device object reference | |
| 38 | +--------------------------- | |
| 38 | 39 | |
| 39 | −Drivers wishing to support TTM must pass a filled :c:type:`ttm_bo_driver | |
| 40 | −<ttm_bo_driver>` structure to ttm_device_init, together with an | |
| 41 | −initialized global reference to the memory manager. The ttm_bo_driver | |
| 42 | −structure contains several fields with function pointers for | |
| 43 | −initializing the TTM, allocating and freeing memory, waiting for command | |
| 44 | −completion and fence synchronization, and memory migration. | |
| 40 | +.. kernel-doc:: include/drm/ttm/ttm_device.h | |
| 41 | + :internal: | |
| 45 | 42 | |
| 46 | −The :c:type:`struct drm_global_reference <drm_global_reference>` is made | |
| 47 | −up of several fields: | |
| 43 | +.. kernel-doc:: drivers/gpu/drm/ttm/ttm_device.c | |
| 44 | + :export: | |
| 48 | 45 | |
| 49 | −.. code-block:: c | |
| 46 | +TTM resource placement reference | |
| 47 | +-------------------------------- | |
| 50 | 48 | |
| 51 | − struct drm_global_reference { | |
| 52 | − enum ttm_global_types global_type; | |
| 53 | − size_t size; | |
| 54 | − void *object; | |
| 55 | − int (*init) (struct drm_global_reference *); | |
| 56 | − void (*release) (struct drm_global_reference *); | |
| 57 | − }; | |
| 58 | − | |
| 59 | − | |
| 60 | −There should be one global reference structure for your memory manager | |
| 61 | −as a whole, and there will be others for each object created by the | |
| 62 | −memory manager at runtime. Your global TTM should have a type of | |
| 63 | −TTM_GLOBAL_TTM_MEM. The size field for the global object should be | |
| 64 | −sizeof(struct ttm_mem_global), and the init and release hooks should | |
| 65 | −point at your driver-specific init and release routines, which probably | |
| 66 | −eventually call ttm_mem_global_init and ttm_mem_global_release, | |
| 67 | −respectively. | |
| 49 | +.. kernel-doc:: include/drm/ttm/ttm_placement.h | |
| 50 | + :internal: | |
| 51 | + | |
| 52 | +TTM resource object reference | |
| 53 | +----------------------------- | |
| 54 | + | |
| 55 | +.. kernel-doc:: include/drm/ttm/ttm_resource.h | |
| 56 | + :internal: | |
| 68 | 57 | |
| 69 | −Once your global TTM accounting structure is set up and initialized by | |
| 70 | −calling ttm_global_item_ref() on it, you need to create a buffer | |
| 71 | −object TTM to provide a pool for buffer object allocation by clients and | |
| 72 | −the kernel itself. The type of this object should be | |
| 73 | −TTM_GLOBAL_TTM_BO, and its size should be sizeof(struct | |
| 74 | −ttm_bo_global). Again, driver-specific init and release functions may | |
| 75 | −be provided, likely eventually calling ttm_bo_global_ref_init() and | |
| 76 | −ttm_bo_global_ref_release(), respectively. Also, like the previous | |
| 77 | −object, ttm_global_item_ref() is used to create an initial reference | |
| 78 | −count for the TTM, which will call your initialization function. | |
| 58 | +.. kernel-doc:: drivers/gpu/drm/ttm/ttm_resource.c | |
| 59 | + :export: | |
| 60 | + | |
| 61 | +TTM TT object reference | |
| 62 | +----------------------- | |
| 63 | + | |
| 64 | +.. kernel-doc:: include/drm/ttm/ttm_tt.h | |
| 65 | + :internal: | |
| 66 | + | |
| 67 | +.. kernel-doc:: drivers/gpu/drm/ttm/ttm_tt.c | |
| 68 | + :export: | |
| 79 | 69 | |
| 80 | −See the radeon_ttm.c file for an example of usage. | |
| 70 | +TTM page pool reference | |
| 71 | +----------------------- | |
| 72 | + | |
| 73 | +.. kernel-doc:: include/drm/ttm/ttm_pool.h | |
| 74 | + :internal: | |
| 75 | + | |
| 76 | +.. kernel-doc:: drivers/gpu/drm/ttm/ttm_pool.c | |
| 77 | + :export: | |
| 81 | 78 | |
| 82 | 79 | The Graphics Execution Manager (GEM) |
| 83 | 80 | ==================================== |
| @@ -504,3 +501,6 @@ Scheduler Function References · 420 unmodified lines | ||
| 504 | 501 | |
| 505 | 502 | .. kernel-doc:: drivers/gpu/drm/scheduler/sched_main.c |
| 506 | 503 | :export: |
| 504 | + | |
| 505 | +.. kernel-doc:: drivers/gpu/drm/scheduler/sched_entity.c | |
| 506 | + :export: | |
| @@ -6018,6 +6018,12 @@ T: git git://anongit.freedesktop.org/drm/drm-misc · 6017 unmodified lines | ||
| 6018 | 6018 | F: Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml |
| 6019 | 6019 | F: drivers/gpu/drm/panel/panel-samsung-db7430.c |
| 6020 | 6020 | |
| 6021 | +DRM DRIVER FOR SAMSUNG S6D27A1 PANELS | |
| 6022 | +M: Markuss Broks <markuss.broks@gmail.com> | |
| 6023 | +S: Maintained | |
| 6024 | +F: Documentation/devicetree/bindings/display/panel/samsung,s6d27a1.yaml | |
| 6025 | +F: driver/gpu/drm/panel/panel-samsung-s6d27a1.c | |
| 6026 | + | |
| 6021 | 6027 | DRM DRIVER FOR SITRONIX ST7703 PANELS |
| 6022 | 6028 | M: Guido Günther <agx@sigxcpu.org> |
| 6023 | 6029 | R: Purism Kernel Team <kernel@puri.sm> |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ |
| 3 | − dma-resv.o seqno-fence.o | |
| 3 | + dma-resv.o | |
| 4 | 4 | obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o |
| 5 | 5 | obj-$(CONFIG_DMABUF_HEAPS) += heaps/ |
| 6 | 6 | obj-$(CONFIG_SYNC_FILE) += sync_file.o |
| @@ -82,6 +82,7 @@ static void dma_buf_release(struct dentry *dentry) · 81 unmodified lines | ||
| 82 | 82 | if (dmabuf->resv == (struct dma_resv *)&dmabuf[1]) |
| 83 | 83 | dma_resv_fini(dmabuf->resv); |
| 84 | 84 | |
| 85 | + WARN_ON(!list_empty(&dmabuf->attachments)); | |
| 85 | 86 | module_put(dmabuf->owner); |
| 86 | 87 | kfree(dmabuf->name); |
| 87 | 88 | kfree(dmabuf); |
| @@ -616,20 +616,17 @@ EXPORT_SYMBOL(dma_fence_enable_sw_signaling); · 615 unmodified lines | ||
| 616 | 616 | * @cb: the callback to register |
| 617 | 617 | * @func: the function to call |
| 618 | 618 | * |
| 619 | + * Add a software callback to the fence. The caller should keep a reference to | |
| 620 | + * the fence. | |
| 621 | + * | |
| 619 | 622 | * @cb will be initialized by dma_fence_add_callback(), no initialization |
| 620 | 623 | * by the caller is required. Any number of callbacks can be registered |
| 621 | 624 | * to a fence, but a callback can only be registered to one fence at a time. |
| 622 | 625 | * |
| 623 | − * Note that the callback can be called from an atomic context. If | |
| 624 | − * fence is already signaled, this function will return -ENOENT (and | |
| 626 | + * If fence is already signaled, this function will return -ENOENT (and | |
| 625 | 627 | * *not* call the callback). |
| 626 | 628 | * |
| 627 | − * Add a software callback to the fence. Same restrictions apply to | |
| 628 | − * refcount as it does to dma_fence_wait(), however the caller doesn't need to | |
| 629 | − * keep a refcount to fence afterward dma_fence_add_callback() has returned: | |
| 630 | − * when software access is enabled, the creator of the fence is required to keep | |
| 631 | − * the fence alive until after it signals with dma_fence_signal(). The callback | |
| 632 | − * itself can be called from irq context. | |
| 629 | + * Note that the callback can be called from an atomic context or irq context. | |
| 633 | 630 | * |
| 634 | 631 | * Returns 0 in case of success, -ENOENT if the fence is already signaled |
| 635 | 632 | * and -EINVAL in case of error. |
| @@ -48,6 +48,8 @@ · 47 unmodified lines | ||
| 48 | 48 | * write operations) or N shared fences (read operations). The RCU |
| 49 | 49 | * mechanism is used to protect read access to fences from locked |
| 50 | 50 | * write-side updates. |
| 51 | + * | |
| 52 | + * See struct dma_resv for more details. | |
| 51 | 53 | */ |
| 52 | 54 | |
| 53 | 55 | DEFINE_WD_CLASS(reservation_ww_class); |
| @@ -137,7 +139,11 @@ EXPORT_SYMBOL(dma_resv_fini); · 83 unmodified lines | ||
| 137 | 139 | * @num_fences: number of fences we want to add |
| 138 | 140 | * |
| 139 | 141 | * Should be called before dma_resv_add_shared_fence(). Must |
| 140 | − * be called with obj->lock held. | |
| 142 | + * be called with @obj locked through dma_resv_lock(). | |
| 143 | + * | |
| 144 | + * Note that the preallocated slots need to be re-reserved if @obj is unlocked | |
| 145 | + * at any time before calling dma_resv_add_shared_fence(). This is validated | |
| 146 | + * when CONFIG_DEBUG_MUTEXES is enabled. | |
| 141 | 147 | * |
| 142 | 148 | * RETURNS |
| 143 | 149 | * Zero for success, or -errno |
| @@ -234,8 +240,10 @@ EXPORT_SYMBOL(dma_resv_reset_shared_max); · 90 unmodified lines | ||
| 234 | 240 | * @obj: the reservation object |
| 235 | 241 | * @fence: the shared fence to add |
| 236 | 242 | * |
| 237 | − * Add a fence to a shared slot, obj->lock must be held, and | |
| 243 | + * Add a fence to a shared slot, @obj must be locked with dma_resv_lock(), and | |
| 238 | 244 | * dma_resv_reserve_shared() has been called. |
| 245 | + * | |
| 246 | + * See also &dma_resv.fence for a discussion of the semantics. | |
| 239 | 247 | */ |
| 240 | 248 | void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence) |
| 241 | 249 | { |
| @@ -278,9 +286,11 @@ EXPORT_SYMBOL(dma_resv_add_shared_fence); · 36 unmodified lines | ||
| 278 | 286 | /** |
| 279 | 287 | * dma_resv_add_excl_fence - Add an exclusive fence. |
| 280 | 288 | * @obj: the reservation object |
| 281 | − * @fence: the shared fence to add | |
| 289 | + * @fence: the exclusive fence to add | |
| 282 | 290 | * |
| 283 | − * Add a fence to the exclusive slot. The obj->lock must be held. | |
| 291 | + * Add a fence to the exclusive slot. @obj must be locked with dma_resv_lock(). | |
| 292 | + * Note that this function replaces all fences attached to @obj, see also | |
| 293 | + * &dma_resv.fence_excl for a discussion of the semantics. | |
| 284 | 294 | */ |
| 285 | 295 | void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence) |
| 286 | 296 | { |
| @@ -609,9 +619,11 @@ static inline int dma_resv_test_signaled_single(struct dma_fence *passed_fence) · 322 unmodified lines | ||
| 609 | 619 | * fence |
| 610 | 620 | * |
| 611 | 621 | * Callers are not required to hold specific locks, but maybe hold |
| 612 | − * dma_resv_lock() already | |
| 622 | + * dma_resv_lock() already. | |
| 623 | + * | |
| 613 | 624 | * RETURNS |
| 614 | − * true if all fences signaled, else false | |
| 625 | + * | |
| 626 | + * True if all fences signaled, else false. | |
| 615 | 627 | */ |
| 616 | 628 | bool dma_resv_test_signaled(struct dma_resv *obj, bool test_all) |
| 617 | 629 | { |
| @@ -40,11 +40,12 @@ struct dma_heap_attachment { · 39 unmodified lines | ||
| 40 | 40 | bool mapped; |
| 41 | 41 | }; |
| 42 | 42 | |
| 43 | +#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO | __GFP_COMP) | |
| 44 | +#define MID_ORDER_GFP (LOW_ORDER_GFP | __GFP_NOWARN) | |
| 43 | 45 | #define HIGH_ORDER_GFP (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \ |
| 44 | 46 | | __GFP_NORETRY) & ~__GFP_RECLAIM) \ |
| 45 | 47 | | __GFP_COMP) |
| 46 | −#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO | __GFP_COMP) | |
| 47 | −static gfp_t order_flags[] = {HIGH_ORDER_GFP, LOW_ORDER_GFP, LOW_ORDER_GFP}; | |
| 48 | +static gfp_t order_flags[] = {HIGH_ORDER_GFP, MID_ORDER_GFP, LOW_ORDER_GFP}; | |
| 48 | 49 | /* |
| 49 | 50 | * The selection of the orders used for allocation (1MB, 64K, 4K) is designed |
| 50 | 51 | * to match with the sizes often found in IOMMUs. Using order 4 pages instead |
| @@ -1,71 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* | |
| 3 | − * seqno-fence, using a dma-buf to synchronize fencing | |
| 4 | − * | |
| 5 | − * Copyright (C) 2012 Texas Instruments | |
| 6 | − * Copyright (C) 2012-2014 Canonical Ltd | |
| 7 | − * Authors: | |
| 8 | − * Rob Clark <robdclark@gmail.com> | |
| 9 | − * Maarten Lankhorst <maarten.lankhorst@canonical.com> | |
| 10 | − */ | |
| 11 | − | |
| 12 | −#include <linux/slab.h> | |
| 13 | −#include <linux/export.h> | |
| 14 | −#include <linux/seqno-fence.h> | |
| 15 | − | |
| 16 | −static const char *seqno_fence_get_driver_name(struct dma_fence *fence) | |
| 17 | −{ | |
| 18 | − struct seqno_fence *seqno_fence = to_seqno_fence(fence); | |
| 19 | − | |
| 20 | − return seqno_fence->ops->get_driver_name(fence); | |
| 21 | −} | |
| 22 | − | |
| 23 | −static const char *seqno_fence_get_timeline_name(struct dma_fence *fence) | |
| 24 | −{ | |
| 25 | − struct seqno_fence *seqno_fence = to_seqno_fence(fence); | |
| 26 | − | |
| 27 | − return seqno_fence->ops->get_timeline_name(fence); | |
| 28 | −} | |
| 29 | − | |
| 30 | −static bool seqno_enable_signaling(struct dma_fence *fence) | |
| 31 | −{ | |
| 32 | − struct seqno_fence *seqno_fence = to_seqno_fence(fence); | |
| 33 | − | |
| 34 | − return seqno_fence->ops->enable_signaling(fence); | |
| 35 | −} | |
| 36 | − | |
| 37 | −static bool seqno_signaled(struct dma_fence *fence) | |
| 38 | −{ | |
| 39 | − struct seqno_fence *seqno_fence = to_seqno_fence(fence); | |
| 40 | − | |
| 41 | − return seqno_fence->ops->signaled && seqno_fence->ops->signaled(fence); | |
| 42 | −} | |
| 43 | − | |
| 44 | −static void seqno_release(struct dma_fence *fence) | |
| 45 | −{ | |
| 46 | − struct seqno_fence *f = to_seqno_fence(fence); | |
| 47 | − | |
| 48 | − dma_buf_put(f->sync_buf); | |
| 49 | − if (f->ops->release) | |
| 50 | − f->ops->release(fence); | |
| 51 | − else | |
| 52 | − dma_fence_free(&f->base); | |
| 53 | −} | |
| 54 | − | |
| 55 | −static signed long seqno_wait(struct dma_fence *fence, bool intr, | |
| 56 | − signed long timeout) | |
| 57 | −{ | |
| 58 | − struct seqno_fence *f = to_seqno_fence(fence); | |
| 59 | − | |
| 60 | − return f->ops->wait(fence, intr, timeout); | |
| 61 | −} | |
| 62 | − | |
| 63 | −const struct dma_fence_ops seqno_fence_ops = { | |
| 64 | − .get_driver_name = seqno_fence_get_driver_name, | |
| 65 | − .get_timeline_name = seqno_fence_get_timeline_name, | |
| 66 | − .enable_signaling = seqno_enable_signaling, | |
| 67 | − .signaled = seqno_signaled, | |
| 68 | − .wait = seqno_wait, | |
| 69 | − .release = seqno_release, | |
| 70 | −}; | |
| 71 | −EXPORT_SYMBOL(seqno_fence_ops); | |
| @@ -211,7 +211,7 @@ config DRM_KMS_CMA_HELPER · 210 unmodified lines | ||
| 211 | 211 | |
| 212 | 212 | config DRM_GEM_SHMEM_HELPER |
| 213 | 213 | bool |
| 214 | − depends on DRM | |
| 214 | + depends on DRM && MMU | |
| 215 | 215 | help |
| 216 | 216 | Choose this if you need the GEM shmem helper functions |
| 217 | 217 | |
| @@ -271,7 +271,8 @@ source "drivers/gpu/drm/kmb/Kconfig" · 53 unmodified lines | ||
| 271 | 271 | |
| 272 | 272 | config DRM_VGEM |
| 273 | 273 | tristate "Virtual GEM provider" |
| 274 | − depends on DRM | |
| 274 | + depends on DRM && MMU | |
| 275 | + select DRM_GEM_SHMEM_HELPER | |
| 275 | 276 | help |
| 276 | 277 | Choose this option to get a virtual graphics memory manager, |
| 277 | 278 | as used by Mesa's software renderer for enhanced performance. |
| @@ -279,7 +280,7 @@ config DRM_VGEM · 1 unmodified line | ||
| 279 | 280 | |
| 280 | 281 | config DRM_VKMS |
| 281 | 282 | tristate "Virtual KMS (EXPERIMENTAL)" |
| 282 | − depends on DRM | |
| 283 | + depends on DRM && MMU | |
| 283 | 284 | select DRM_KMS_HELPER |
| 284 | 285 | select DRM_GEM_SHMEM_HELPER |
| 285 | 286 | select CRC32 |
| @@ -351,8 +352,6 @@ source "drivers/gpu/drm/hisilicon/Kconfig" · 65 unmodified lines | ||
| 351 | 352 | |
| 352 | 353 | source "drivers/gpu/drm/mediatek/Kconfig" |
| 353 | 354 | |
| 354 | −source "drivers/gpu/drm/zte/Kconfig" | |
| 355 | − | |
| 356 | 355 | source "drivers/gpu/drm/mxsfb/Kconfig" |
| 357 | 356 | |
| 358 | 357 | source "drivers/gpu/drm/meson/Kconfig" |
| @@ -113,7 +113,6 @@ obj-y += bridge/ · 112 unmodified lines | ||
| 113 | 113 | obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ |
| 114 | 114 | obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ |
| 115 | 115 | obj-y += hisilicon/ |
| 116 | −obj-$(CONFIG_DRM_ZTE) += zte/ | |
| 117 | 116 | obj-$(CONFIG_DRM_MXSFB) += mxsfb/ |
| 118 | 117 | obj-y += tiny/ |
| 119 | 118 | obj-$(CONFIG_DRM_PL111) += pl111/ |
| @@ -1222,6 +1222,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, · 1221 unmodified lines | ||
| 1222 | 1222 | if (r) |
| 1223 | 1223 | goto error_unlock; |
| 1224 | 1224 | |
| 1225 | + drm_sched_job_arm(&job->base); | |
| 1226 | + | |
| 1225 | 1227 | /* No memory allocation is allowed while holding the notifier lock. |
| 1226 | 1228 | * The lock is held until amdgpu_cs_submit is finished and fence is |
| 1227 | 1229 | * added to BOs. |
| @@ -1259,7 +1261,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, · 31 unmodified lines | ||
| 1259 | 1261 | |
| 1260 | 1262 | trace_amdgpu_cs_ioctl(job); |
| 1261 | 1263 | amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->ticket); |
| 1262 | − drm_sched_entity_push_job(&job->base, entity); | |
| 1264 | + drm_sched_entity_push_job(&job->base); | |
| 1263 | 1265 | |
| 1264 | 1266 | amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm); |
| 1265 | 1267 | |
| @@ -266,7 +266,6 @@ bool amdgpu_fence_process(struct amdgpu_ring *ring) · 265 unmodified lines | ||
| 266 | 266 | struct amdgpu_fence_driver *drv = &ring->fence_drv; |
| 267 | 267 | struct amdgpu_device *adev = ring->adev; |
| 268 | 268 | uint32_t seq, last_seq; |
| 269 | − int r; | |
| 270 | 269 | |
| 271 | 270 | do { |
| 272 | 271 | last_seq = atomic_read(&ring->fence_drv.last_seq); |
| @@ -298,12 +297,7 @@ bool amdgpu_fence_process(struct amdgpu_ring *ring) · 25 unmodified lines | ||
| 298 | 297 | if (!fence) |
| 299 | 298 | continue; |
| 300 | 299 | |
| 301 | − r = dma_fence_signal(fence); | |
| 302 | − if (!r) | |
| 303 | − DMA_FENCE_TRACE(fence, "signaled from irq context\n"); | |
| 304 | − else | |
| 305 | − BUG(); | |
| 306 | − | |
| 300 | + dma_fence_signal(fence); | |
| 307 | 301 | dma_fence_put(fence); |
| 308 | 302 | pm_runtime_mark_last_busy(adev_to_drm(adev)->dev); |
| 309 | 303 | pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); |
| @@ -684,8 +678,6 @@ static bool amdgpu_fence_enable_signaling(struct dma_fence *f) · 374 unmodified lines | ||
| 684 | 678 | if (!timer_pending(&ring->fence_drv.fallback_timer)) |
| 685 | 679 | amdgpu_fence_schedule_fallback(ring); |
| 686 | 680 | |
| 687 | − DMA_FENCE_TRACE(f, "armed on ring %i!\n", ring->idx); | |
| 688 | − | |
| 689 | 681 | return true; |
| 690 | 682 | } |
| 691 | 683 | |
| @@ -182,9 +182,11 @@ int amdgpu_job_submit(struct amdgpu_job *job, struct drm_sched_entity *entity, · 181 unmodified lines | ||
| 182 | 182 | if (r) |
| 183 | 183 | return r; |
| 184 | 184 | |
| 185 | + drm_sched_job_arm(&job->base); | |
| 186 | + | |
| 185 | 187 | *f = dma_fence_get(&job->base.s_fence->finished); |
| 186 | 188 | amdgpu_job_free_resources(job); |
| 187 | − drm_sched_entity_push_job(&job->base, entity); | |
| 189 | + drm_sched_entity_push_job(&job->base); | |
| 188 | 190 | |
| 189 | 191 | return 0; |
| 190 | 192 | } |
| @@ -1066,8 +1066,6 @@ static void amdgpu_ttm_backend_destroy(struct ttm_device *bdev, · 1065 unmodified lines | ||
| 1066 | 1066 | { |
| 1067 | 1067 | struct amdgpu_ttm_tt *gtt = (void *)ttm; |
| 1068 | 1068 | |
| 1069 | − amdgpu_ttm_backend_unbind(bdev, ttm); | |
| 1070 | − ttm_tt_destroy_common(bdev, ttm); | |
| 1071 | 1069 | if (gtt->usertask) |
| 1072 | 1070 | put_task_struct(gtt->usertask); |
| 1073 | 1071 | |
| @@ -1148,6 +1146,8 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_device *bdev, · 74 unmodified lines | ||
| 1148 | 1146 | struct amdgpu_ttm_tt *gtt = (void *)ttm; |
| 1149 | 1147 | struct amdgpu_device *adev; |
| 1150 | 1148 | |
| 1149 | + amdgpu_ttm_backend_unbind(bdev, ttm); | |
| 1150 | + | |
| 1151 | 1151 | if (gtt->userptr) { |
| 1152 | 1152 | amdgpu_ttm_tt_set_user_pages(ttm, NULL); |
| 1153 | 1153 | kfree(ttm->sg); |
| @@ -165,7 +165,7 @@ bool malidp_format_mod_supported(struct drm_device *drm, · 164 unmodified lines | ||
| 165 | 165 | return !malidp_hw_format_is_afbc_only(format); |
| 166 | 166 | } |
| 167 | 167 | |
| 168 | − if ((modifier >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) { | |
| 168 | + if (!fourcc_mod_is_vendor(modifier, ARM)) { | |
| 169 | 169 | DRM_ERROR("Unknown modifier (not Arm)\n"); |
| 170 | 170 | return false; |
| 171 | 171 | } |
| @@ -1,21 +1,8 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0-only | |
| 1 | 2 | /* |
| 2 | 3 | * adv7511_cec.c - Analog Devices ADV7511/33 cec driver |
| 3 | 4 | * |
| 4 | 5 | * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
| 5 | − * | |
| 6 | − * This program is free software; you may redistribute it and/or modify | |
| 7 | − * it under the terms of the GNU General Public License as published by | |
| 8 | − * the Free Software Foundation; version 2 of the License. | |
| 9 | − * | |
| 10 | − * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 11 | − * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 12 | − * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 13 | − * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
| 14 | − * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
| 15 | − * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| 16 | − * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 17 | − * SOFTWARE. | |
| 18 | − * | |
| 19 | 6 | */ |
| 20 | 7 | |
| 21 | 8 | #include <linux/device.h> |
| @@ -720,7 +720,7 @@ static int edid_read(struct anx7625_data *ctx, · 719 unmodified lines | ||
| 720 | 720 | ret = sp_tx_aux_rd(ctx, 0xf1); |
| 721 | 721 | |
| 722 | 722 | if (ret) { |
| 723 | − sp_tx_rst_aux(ctx); | |
| 723 | + ret = sp_tx_rst_aux(ctx); | |
| 724 | 724 | DRM_DEV_DEBUG_DRIVER(dev, "edid read fail, reset!\n"); |
| 725 | 725 | } else { |
| 726 | 726 | ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client, |
| @@ -735,7 +735,7 @@ static int edid_read(struct anx7625_data *ctx, · 8 unmodified lines | ||
| 735 | 735 | if (cnt > EDID_TRY_CNT) |
| 736 | 736 | return -EIO; |
| 737 | 737 | |
| 738 | − return 0; | |
| 738 | + return ret; | |
| 739 | 739 | } |
| 740 | 740 | |
| 741 | 741 | static int segments_edid_read(struct anx7625_data *ctx, |
| @@ -785,7 +785,7 @@ static int segments_edid_read(struct anx7625_data *ctx, · 43 unmodified lines | ||
| 785 | 785 | if (cnt > EDID_TRY_CNT) |
| 786 | 786 | return -EIO; |
| 787 | 787 | |
| 788 | − return 0; | |
| 788 | + return ret; | |
| 789 | 789 | } |
| 790 | 790 | |
| 791 | 791 | static int sp_tx_edid_read(struct anx7625_data *ctx, |
| @@ -845,8 +845,11 @@ static int sp_tx_edid_read(struct anx7625_data *ctx, · 53 unmodified lines | ||
| 845 | 845 | if (g_edid_break == 1) |
| 846 | 846 | break; |
| 847 | 847 | |
| 848 | − segments_edid_read(ctx, count / 2, | |
| 849 | − pblock_buf, offset); | |
| 848 | + ret = segments_edid_read(ctx, count / 2, | |
| 849 | + pblock_buf, offset); | |
| 850 | + if (ret < 0) | |
| 851 | + return ret; | |
| 852 | + | |
| 850 | 853 | memcpy(&pedid_blocks_buf[edid_pos], |
| 851 | 854 | pblock_buf, |
| 852 | 855 | MAX_DPCD_BUFFER_SIZE); |
| @@ -863,8 +866,11 @@ static int sp_tx_edid_read(struct anx7625_data *ctx, · 10 unmodified lines | ||
| 863 | 866 | if (g_edid_break == 1) |
| 864 | 867 | break; |
| 865 | 868 | |
| 866 | − segments_edid_read(ctx, count / 2, | |
| 867 | − pblock_buf, offset); | |
| 869 | + ret = segments_edid_read(ctx, count / 2, | |
| 870 | + pblock_buf, offset); | |
| 871 | + if (ret < 0) | |
| 872 | + return ret; | |
| 873 | + | |
| 868 | 874 | memcpy(&pedid_blocks_buf[edid_pos], |
| 869 | 875 | pblock_buf, |
| 870 | 876 | MAX_DPCD_BUFFER_SIZE); |
| @@ -887,7 +893,11 @@ static int sp_tx_edid_read(struct anx7625_data *ctx, · 16 unmodified lines | ||
| 887 | 893 | } |
| 888 | 894 | |
| 889 | 895 | /* Reset aux channel */ |
| 890 | − sp_tx_rst_aux(ctx); | |
| 896 | + ret = sp_tx_rst_aux(ctx); | |
| 897 | + if (ret < 0) { | |
| 898 | + DRM_DEV_ERROR(dev, "Failed to reset aux channel!\n"); | |
| 899 | + return ret; | |
| 900 | + } | |
| 891 | 901 | |
| 892 | 902 | return (blocks_num + 1); |
| 893 | 903 | } |
| @@ -1325,7 +1335,6 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx) · 431 unmodified lines | ||
| 1325 | 1335 | dsi->format = MIPI_DSI_FMT_RGB888; |
| 1326 | 1336 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | |
| 1327 | 1337 | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | |
| 1328 | − MIPI_DSI_MODE_NO_EOT_PACKET | | |
| 1329 | 1338 | MIPI_DSI_MODE_VIDEO_HSE; |
| 1330 | 1339 | |
| 1331 | 1340 | if (mipi_dsi_attach(dsi) < 0) { |
| @@ -1171,7 +1171,6 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev) · 1170 unmodified lines | ||
| 1171 | 1171 | { |
| 1172 | 1172 | struct cdns_dsi *dsi; |
| 1173 | 1173 | struct cdns_dsi_input *input; |
| 1174 | − struct resource *res; | |
| 1175 | 1174 | int ret, irq; |
| 1176 | 1175 | u32 val; |
| 1177 | 1176 | |
| @@ -1183,8 +1182,7 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 1183 | 1182 | |
| 1184 | 1183 | input = &dsi->input; |
| 1185 | 1184 | |
| 1186 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 1187 | − dsi->regs = devm_ioremap_resource(&pdev->dev, res); | |
| 1185 | + dsi->regs = devm_platform_ioremap_resource(pdev, 0); | |
| 1188 | 1186 | if (IS_ERR(dsi->regs)) |
| 1189 | 1187 | return PTR_ERR(dsi->regs); |
| 1190 | 1188 | |
| @@ -889,7 +889,7 @@ unlock: · 888 unmodified lines | ||
| 889 | 889 | static int it66121_probe(struct i2c_client *client, |
| 890 | 890 | const struct i2c_device_id *id) |
| 891 | 891 | { |
| 892 | − u32 vendor_ids[2], device_ids[2], revision_id; | |
| 892 | + u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; | |
| 893 | 893 | struct device_node *ep; |
| 894 | 894 | int ret; |
| 895 | 895 | struct it66121_ctx *ctx; |
| @@ -924,6 +924,9 @@ static int it66121_probe(struct i2c_client *client, · 28 unmodified lines | ||
| 924 | 924 | ctx->next_bridge = of_drm_find_bridge(ep); |
| 925 | 925 | of_node_put(ep); |
| 926 | 926 | |
| 927 | + if (!ctx->next_bridge) | |
| 928 | + return -EPROBE_DEFER; | |
| 929 | + | |
| 927 | 930 | i2c_set_clientdata(client, ctx); |
| 928 | 931 | mutex_init(&ctx->lock); |
| 929 | 932 | |
| @@ -18,16 +18,18 @@ · 17 unmodified lines | ||
| 18 | 18 | #include <drm/drm_print.h> |
| 19 | 19 | |
| 20 | 20 | #define PAGE2_GPIO_H 0xa7 |
| 21 | −#define PS_GPIO9 BIT(1) | |
| 21 | +#define PS_GPIO9 BIT(1) | |
| 22 | 22 | #define PAGE2_I2C_BYPASS 0xea |
| 23 | −#define I2C_BYPASS_EN 0xd0 | |
| 23 | +#define I2C_BYPASS_EN 0xd0 | |
| 24 | 24 | #define PAGE2_MCS_EN 0xf3 |
| 25 | −#define MCS_EN BIT(0) | |
| 25 | +#define MCS_EN BIT(0) | |
| 26 | + | |
| 26 | 27 | #define PAGE3_SET_ADD 0xfe |
| 27 | −#define VDO_CTL_ADD 0x13 | |
| 28 | −#define VDO_DIS 0x18 | |
| 29 | −#define VDO_EN 0x1c | |
| 30 | −#define DP_NUM_LANES 4 | |
| 28 | +#define VDO_CTL_ADD 0x13 | |
| 29 | +#define VDO_DIS 0x18 | |
| 30 | +#define VDO_EN 0x1c | |
| 31 | + | |
| 32 | +#define NUM_MIPI_LANES 4 | |
| 31 | 33 | |
| 32 | 34 | /* |
| 33 | 35 | * PS8640 uses multiple addresses: |
| @@ -254,7 +256,7 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge, · 220 unmodified lines | ||
| 254 | 256 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | |
| 255 | 257 | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; |
| 256 | 258 | dsi->format = MIPI_DSI_FMT_RGB888; |
| 257 | − dsi->lanes = DP_NUM_LANES; | |
| 259 | + dsi->lanes = NUM_MIPI_LANES; | |
| 258 | 260 | ret = mipi_dsi_attach(dsi); |
| 259 | 261 | if (ret) |
| 260 | 262 | goto err_dsi_attach; |
| @@ -28,6 +28,7 @@ · 27 unmodified lines | ||
| 28 | 28 | #include <drm/drm_atomic_state_helper.h> |
| 29 | 29 | #include <drm/drm_bridge.h> |
| 30 | 30 | #include <drm/drm_encoder.h> |
| 31 | +#include <drm/drm_of.h> | |
| 31 | 32 | #include <drm/drm_print.h> |
| 32 | 33 | |
| 33 | 34 | #include "drm_crtc_internal.h" |
| @@ -51,10 +52,8 @@ · 17 unmodified lines | ||
| 51 | 52 | * |
| 52 | 53 | * Display drivers are responsible for linking encoders with the first bridge |
| 53 | 54 | * in the chains. This is done by acquiring the appropriate bridge with |
| 54 | − * of_drm_find_bridge() or drm_of_find_panel_or_bridge(), or creating it for a | |
| 55 | − * panel with drm_panel_bridge_add_typed() (or the managed version | |
| 56 | − * devm_drm_panel_bridge_add_typed()). Once acquired, the bridge shall be | |
| 57 | − * attached to the encoder with a call to drm_bridge_attach(). | |
| 55 | + * devm_drm_of_get_bridge(). Once acquired, the bridge shall be attached to the | |
| 56 | + * encoder with a call to drm_bridge_attach(). | |
| 58 | 57 | * |
| 59 | 58 | * Bridges are responsible for linking themselves with the next bridge in the |
| 60 | 59 | * chain, if any. This is done the same way as for encoders, with the call to |
| @@ -1233,6 +1232,40 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np) · 1172 unmodified lines | ||
| 1233 | 1232 | return NULL; |
| 1234 | 1233 | } |
| 1235 | 1234 | EXPORT_SYMBOL(of_drm_find_bridge); |
| 1235 | + | |
| 1236 | +/** | |
| 1237 | + * devm_drm_of_get_bridge - Return next bridge in the chain | |
| 1238 | + * @dev: device to tie the bridge lifetime to | |
| 1239 | + * @np: device tree node containing encoder output ports | |
| 1240 | + * @port: port in the device tree node | |
| 1241 | + * @endpoint: endpoint in the device tree node | |
| 1242 | + * | |
| 1243 | + * Given a DT node's port and endpoint number, finds the connected node | |
| 1244 | + * and returns the associated bridge if any, or creates and returns a | |
| 1245 | + * drm panel bridge instance if a panel is connected. | |
| 1246 | + * | |
| 1247 | + * Returns a pointer to the bridge if successful, or an error pointer | |
| 1248 | + * otherwise. | |
| 1249 | + */ | |
| 1250 | +struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, | |
| 1251 | + struct device_node *np, | |
| 1252 | + u32 port, u32 endpoint) | |
| 1253 | +{ | |
| 1254 | + struct drm_bridge *bridge; | |
| 1255 | + struct drm_panel *panel; | |
| 1256 | + int ret; | |
| 1257 | + | |
| 1258 | + ret = drm_of_find_panel_or_bridge(np, port, endpoint, | |
| 1259 | + &panel, &bridge); | |
| 1260 | + if (ret) | |
| 1261 | + return ERR_PTR(ret); | |
| 1262 | + | |
| 1263 | + if (panel) | |
| 1264 | + bridge = devm_drm_panel_bridge_add(dev, panel); | |
| 1265 | + | |
| 1266 | + return bridge; | |
| 1267 | +} | |
| 1268 | +EXPORT_SYMBOL(devm_drm_of_get_bridge); | |
| 1236 | 1269 | #endif |
| 1237 | 1270 | |
| 1238 | 1271 | MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>"); |
| @@ -65,6 +65,14 @@ · 64 unmodified lines | ||
| 65 | 65 | * support can instead use e.g. drm_helper_hpd_irq_event(). |
| 66 | 66 | */ |
| 67 | 67 | |
| 68 | +/* | |
| 69 | + * Global connector list for drm_connector_find_by_fwnode(). | |
| 70 | + * Note drm_connector_[un]register() first take connector->lock and then | |
| 71 | + * take the connector_list_lock. | |
| 72 | + */ | |
| 73 | +static DEFINE_MUTEX(connector_list_lock); | |
| 74 | +static LIST_HEAD(connector_list); | |
| 75 | + | |
| 68 | 76 | struct drm_conn_prop_enum_list { |
| 69 | 77 | int type; |
| 70 | 78 | const char *name; |
| @@ -267,6 +275,7 @@ int drm_connector_init(struct drm_device *dev, · 196 unmodified lines | ||
| 267 | 275 | goto out_put_type_id; |
| 268 | 276 | } |
| 269 | 277 | |
| 278 | + INIT_LIST_HEAD(&connector->global_connector_list_entry); | |
| 270 | 279 | INIT_LIST_HEAD(&connector->probed_modes); |
| 271 | 280 | INIT_LIST_HEAD(&connector->modes); |
| 272 | 281 | mutex_init(&connector->mutex); |
| @@ -474,6 +483,8 @@ void drm_connector_cleanup(struct drm_connector *connector) · 201 unmodified lines | ||
| 474 | 483 | drm_mode_object_unregister(dev, &connector->base); |
| 475 | 484 | kfree(connector->name); |
| 476 | 485 | connector->name = NULL; |
| 486 | + fwnode_handle_put(connector->fwnode); | |
| 487 | + connector->fwnode = NULL; | |
| 477 | 488 | spin_lock_irq(&dev->mode_config.connector_list_lock); |
| 478 | 489 | list_del(&connector->head); |
| 479 | 490 | dev->mode_config.num_connector--; |
| @@ -532,6 +543,9 @@ int drm_connector_register(struct drm_connector *connector) · 52 unmodified lines | ||
| 532 | 543 | /* Let userspace know we have a new connector */ |
| 533 | 544 | drm_sysfs_hotplug_event(connector->dev); |
| 534 | 545 | |
| 546 | + mutex_lock(&connector_list_lock); | |
| 547 | + list_add_tail(&connector->global_connector_list_entry, &connector_list); | |
| 548 | + mutex_unlock(&connector_list_lock); | |
| 535 | 549 | goto unlock; |
| 536 | 550 | |
| 537 | 551 | err_debugfs: |
| @@ -560,6 +574,10 @@ void drm_connector_unregister(struct drm_connector *connector) · 22 unmodified lines | ||
| 560 | 574 | return; |
| 561 | 575 | } |
| 562 | 576 | |
| 577 | + mutex_lock(&connector_list_lock); | |
| 578 | + list_del_init(&connector->global_connector_list_entry); | |
| 579 | + mutex_unlock(&connector_list_lock); | |
| 580 | + | |
| 563 | 581 | if (connector->funcs->early_unregister) |
| 564 | 582 | connector->funcs->early_unregister(connector); |
| 565 | 583 | |
| @@ -2543,6 +2561,67 @@ out: · 1977 unmodified lines | ||
| 2543 | 2561 | return ret; |
| 2544 | 2562 | } |
| 2545 | 2563 | |
| 2564 | +/** | |
| 2565 | + * drm_connector_find_by_fwnode - Find a connector based on the associated fwnode | |
| 2566 | + * @fwnode: fwnode for which to find the matching drm_connector | |
| 2567 | + * | |
| 2568 | + * This functions looks up a drm_connector based on its associated fwnode. When | |
| 2569 | + * a connector is found a reference to the connector is returned. The caller must | |
| 2570 | + * call drm_connector_put() to release this reference when it is done with the | |
| 2571 | + * connector. | |
| 2572 | + * | |
| 2573 | + * Returns: A reference to the found connector or an ERR_PTR(). | |
| 2574 | + */ | |
| 2575 | +struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode) | |
| 2576 | +{ | |
| 2577 | + struct drm_connector *connector, *found = ERR_PTR(-ENODEV); | |
| 2578 | + | |
| 2579 | + if (!fwnode) | |
| 2580 | + return ERR_PTR(-ENODEV); | |
| 2581 | + | |
| 2582 | + mutex_lock(&connector_list_lock); | |
| 2583 | + | |
| 2584 | + list_for_each_entry(connector, &connector_list, global_connector_list_entry) { | |
| 2585 | + if (connector->fwnode == fwnode || | |
| 2586 | + (connector->fwnode && connector->fwnode->secondary == fwnode)) { | |
| 2587 | + drm_connector_get(connector); | |
| 2588 | + found = connector; | |
| 2589 | + break; | |
| 2590 | + } | |
| 2591 | + } | |
| 2592 | + | |
| 2593 | + mutex_unlock(&connector_list_lock); | |
| 2594 | + | |
| 2595 | + return found; | |
| 2596 | +} | |
| 2597 | + | |
| 2598 | +/** | |
| 2599 | + * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector | |
| 2600 | + * @connector: connector to report the event on | |
| 2601 | + * | |
| 2602 | + * On some hardware a hotplug event notification may come from outside the display | |
| 2603 | + * driver / device. An example of this is some USB Type-C setups where the hardware | |
| 2604 | + * muxes the DisplayPort data and aux-lines but does not pass the altmode HPD | |
| 2605 | + * status bit to the GPU's DP HPD pin. | |
| 2606 | + * | |
| 2607 | + * This function can be used to report these out-of-band events after obtaining | |
| 2608 | + * a drm_connector reference through calling drm_connector_find_by_fwnode(). | |
| 2609 | + */ | |
| 2610 | +void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode) | |
| 2611 | +{ | |
| 2612 | + struct drm_connector *connector; | |
| 2613 | + | |
| 2614 | + connector = drm_connector_find_by_fwnode(connector_fwnode); | |
| 2615 | + if (IS_ERR(connector)) | |
| 2616 | + return; | |
| 2617 | + | |
| 2618 | + if (connector->funcs->oob_hotplug_event) | |
| 2619 | + connector->funcs->oob_hotplug_event(connector); | |
| 2620 | + | |
| 2621 | + drm_connector_put(connector); | |
| 2622 | +} | |
| 2623 | +EXPORT_SYMBOL(drm_connector_oob_hotplug_event); | |
| 2624 | + | |
| 2546 | 2625 | |
| 2547 | 2626 | /** |
| 2548 | 2627 | * DOC: Tile group |
| @@ -58,6 +58,7 @@ struct drm_property; · 57 unmodified lines | ||
| 58 | 58 | struct edid; |
| 59 | 59 | struct kref; |
| 60 | 60 | struct work_struct; |
| 61 | +struct fwnode_handle; | |
| 61 | 62 | |
| 62 | 63 | /* drm_crtc.c */ |
| 63 | 64 | int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, |
| @@ -186,6 +187,7 @@ int drm_connector_set_obj_prop(struct drm_mode_object *obj, · 122 unmodified lines | ||
| 186 | 187 | int drm_connector_create_standard_properties(struct drm_device *dev); |
| 187 | 188 | const char *drm_get_connector_force_name(enum drm_connector_force force); |
| 188 | 189 | void drm_connector_free_work_fn(struct work_struct *work); |
| 190 | +struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode); | |
| 189 | 191 | |
| 190 | 192 | /* IOCTL */ |
| 191 | 193 | int drm_connector_property_set_ioctl(struct drm_device *dev, |
| @@ -10,6 +10,10 @@ · 9 unmodified lines | ||
| 10 | 10 | #include <linux/slab.h> |
| 11 | 11 | #include <linux/vmalloc.h> |
| 12 | 12 | |
| 13 | +#ifdef CONFIG_X86 | |
| 14 | +#include <asm/set_memory.h> | |
| 15 | +#endif | |
| 16 | + | |
| 13 | 17 | #include <drm/drm.h> |
| 14 | 18 | #include <drm/drm_device.h> |
| 15 | 19 | #include <drm/drm_drv.h> |
| @@ -162,6 +166,16 @@ static int drm_gem_shmem_get_pages_locked(struct drm_gem_shmem_object *shmem) · 146 unmodified lines | ||
| 162 | 166 | return PTR_ERR(pages); |
| 163 | 167 | } |
| 164 | 168 | |
| 169 | + /* | |
| 170 | + * TODO: Allocating WC pages which are correctly flushed is only | |
| 171 | + * supported on x86. Ideal solution would be a GFP_WC flag, which also | |
| 172 | + * ttm_pool.c could use. | |
| 173 | + */ | |
| 174 | +#ifdef CONFIG_X86 | |
| 175 | + if (shmem->map_wc) | |
| 176 | + set_pages_array_wc(pages, obj->size >> PAGE_SHIFT); | |
| 177 | +#endif | |
| 178 | + | |
| 165 | 179 | shmem->pages = pages; |
| 166 | 180 | |
| 167 | 181 | return 0; |
| @@ -203,6 +217,11 @@ static void drm_gem_shmem_put_pages_locked(struct drm_gem_shmem_object *shmem) · 35 unmodified lines | ||
| 203 | 217 | if (--shmem->pages_use_count > 0) |
| 204 | 218 | return; |
| 205 | 219 | |
| 220 | +#ifdef CONFIG_X86 | |
| 221 | + if (shmem->map_wc) | |
| 222 | + set_pages_array_wb(shmem->pages, obj->size >> PAGE_SHIFT); | |
| 223 | +#endif | |
| 224 | + | |
| 206 | 225 | drm_gem_put_pages(obj, shmem->pages, |
| 207 | 226 | shmem->pages_mark_dirty_on_put, |
| 208 | 227 | shmem->pages_mark_accessed_on_put); |
| @@ -542,7 +561,7 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf) · 333 unmodified lines | ||
| 542 | 561 | } else { |
| 543 | 562 | page = shmem->pages[page_offset]; |
| 544 | 563 | |
| 545 | − ret = vmf_insert_page(vma, vmf->address, page); | |
| 564 | + ret = vmf_insert_pfn(vma, vmf->address, page_to_pfn(page)); | |
| 546 | 565 | } |
| 547 | 566 | |
| 548 | 567 | mutex_unlock(&shmem->pages_lock); |
| @@ -612,7 +631,7 @@ int drm_gem_shmem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) · 63 unmodified lines | ||
| 612 | 631 | return ret; |
| 613 | 632 | } |
| 614 | 633 | |
| 615 | − vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND; | |
| 634 | + vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND; | |
| 616 | 635 | vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); |
| 617 | 636 | if (shmem->map_wc) |
| 618 | 637 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); |
| @@ -846,7 +846,6 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs = { · 845 unmodified lines | ||
| 846 | 846 | |
| 847 | 847 | static void bo_driver_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *tt) |
| 848 | 848 | { |
| 849 | − ttm_tt_destroy_common(bdev, tt); | |
| 850 | 849 | ttm_tt_fini(tt); |
| 851 | 850 | kfree(tt); |
| 852 | 851 | } |
| @@ -522,19 +522,7 @@ int drm_version(struct drm_device *dev, void *data, · 521 unmodified lines | ||
| 522 | 522 | return err; |
| 523 | 523 | } |
| 524 | 524 | |
| 525 | −/** | |
| 526 | − * drm_ioctl_permit - Check ioctl permissions against caller | |
| 527 | − * | |
| 528 | − * @flags: ioctl permission flags. | |
| 529 | − * @file_priv: Pointer to struct drm_file identifying the caller. | |
| 530 | − * | |
| 531 | − * Checks whether the caller is allowed to run an ioctl with the | |
| 532 | − * indicated permissions. | |
| 533 | − * | |
| 534 | − * Returns: | |
| 535 | − * Zero if allowed, -EACCES otherwise. | |
| 536 | − */ | |
| 537 | −int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) | |
| 525 | +static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) | |
| 538 | 526 | { |
| 539 | 527 | /* ROOT_ONLY is only for CAP_SYS_ADMIN */ |
| 540 | 528 | if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN))) |
| @@ -557,7 +545,6 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) · 16 unmodified lines | ||
| 557 | 545 | |
| 558 | 546 | return 0; |
| 559 | 547 | } |
| 560 | −EXPORT_SYMBOL(drm_ioctl_permit); | |
| 561 | 548 | |
| 562 | 549 | #define DRM_IOCTL_DEF(ioctl, _func, _flags) \ |
| 563 | 550 | [DRM_IOCTL_NR(ioctl)] = { \ |
| @@ -725,7 +712,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { · 161 unmodified lines | ||
| 725 | 712 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER), |
| 726 | 713 | }; |
| 727 | 714 | |
| 728 | −#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) | |
| 715 | +#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE(drm_ioctls) | |
| 729 | 716 | |
| 730 | 717 | /** |
| 731 | 718 | * DOC: driver specific ioctls |
| @@ -834,8 +821,8 @@ long drm_ioctl(struct file *filp, · 102 unmodified lines | ||
| 834 | 821 | if (drm_dev_is_unplugged(dev)) |
| 835 | 822 | return -ENODEV; |
| 836 | 823 | |
| 837 | − if (DRM_IOCTL_TYPE(cmd) != DRM_IOCTL_BASE) | |
| 838 | − return -ENOTTY; | |
| 824 | + if (DRM_IOCTL_TYPE(cmd) != DRM_IOCTL_BASE) | |
| 825 | + return -ENOTTY; | |
| 839 | 826 | |
| 840 | 827 | is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END; |
| 841 | 828 | |
| @@ -64,17 +64,6 @@ MODULE_PARM_DESC(edid_firmware, · 63 unmodified lines | ||
| 64 | 64 | |
| 65 | 65 | static int __init drm_kms_helper_init(void) |
| 66 | 66 | { |
| 67 | − /* | |
| 68 | − * The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) | |
| 69 | − * but the module doesn't depend on any fb console symbols. At least | |
| 70 | − * attempt to load fbcon to avoid leaving the system without a usable | |
| 71 | − * console. | |
| 72 | − */ | |
| 73 | − if (IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) && | |
| 74 | − IS_MODULE(CONFIG_FRAMEBUFFER_CONSOLE) && | |
| 75 | − !IS_ENABLED(CONFIG_EXPERT)) | |
| 76 | − request_module_nowait("fbcon"); | |
| 77 | − | |
| 78 | 67 | return drm_dp_aux_dev_init(); |
| 79 | 68 | } |
| 80 | 69 | |
| @@ -231,6 +231,9 @@ EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); · 230 unmodified lines | ||
| 231 | 231 | * return either the associated struct drm_panel or drm_bridge device. Either |
| 232 | 232 | * @panel or @bridge must not be NULL. |
| 233 | 233 | * |
| 234 | + * This function is deprecated and should not be used in new drivers. Use | |
| 235 | + * devm_drm_of_get_bridge() instead. | |
| 236 | + * | |
| 234 | 237 | * Returns zero if successful, or one of the standard error codes if it fails. |
| 235 | 238 | */ |
| 236 | 239 | int drm_of_find_panel_or_bridge(const struct device_node *np, |
| @@ -109,6 +109,12 @@ static const struct drm_dmi_panel_orientation_data lcd1200x1920_rightside_up = { · 108 unmodified lines | ||
| 109 | 109 | .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, |
| 110 | 110 | }; |
| 111 | 111 | |
| 112 | +static const struct drm_dmi_panel_orientation_data lcd1280x1920_rightside_up = { | |
| 113 | + .width = 1280, | |
| 114 | + .height = 1920, | |
| 115 | + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, | |
| 116 | +}; | |
| 117 | + | |
| 112 | 118 | static const struct dmi_system_id orientation_data[] = { |
| 113 | 119 | { /* Acer One 10 (S1003) */ |
| 114 | 120 | .matches = { |
| @@ -134,6 +140,20 @@ static const struct dmi_system_id orientation_data[] = { · 19 unmodified lines | ||
| 134 | 140 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"), |
| 135 | 141 | }, |
| 136 | 142 | .driver_data = (void *)&lcd800x1280_rightside_up, |
| 143 | + }, { /* Chuwi HiBook (CWI514) */ | |
| 144 | + .matches = { | |
| 145 | + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), | |
| 146 | + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), | |
| 147 | + /* Above matches are too generic, add bios-date match */ | |
| 148 | + DMI_MATCH(DMI_BIOS_DATE, "05/07/2016"), | |
| 149 | + }, | |
| 150 | + .driver_data = (void *)&lcd1200x1920_rightside_up, | |
| 151 | + }, { /* Chuwi Hi10 Pro (CWI529) */ | |
| 152 | + .matches = { | |
| 153 | + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Hampoo"), | |
| 154 | + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Hi10 pro tablet"), | |
| 155 | + }, | |
| 156 | + .driver_data = (void *)&lcd1200x1920_rightside_up, | |
| 137 | 157 | }, { /* GPD MicroPC (generic strings, also match on bios date) */ |
| 138 | 158 | .matches = { |
| 139 | 159 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), |
| @@ -193,6 +213,13 @@ static const struct dmi_system_id orientation_data[] = { · 53 unmodified lines | ||
| 193 | 213 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"), |
| 194 | 214 | }, |
| 195 | 215 | .driver_data = (void *)&itworks_tw891, |
| 216 | + }, { /* KD Kurio Smart C15200 2-in-1 */ | |
| 217 | + .matches = { | |
| 218 | + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "KD Interactive"), | |
| 219 | + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Kurio Smart"), | |
| 220 | + DMI_EXACT_MATCH(DMI_BOARD_NAME, "KDM960BCP"), | |
| 221 | + }, | |
| 222 | + .driver_data = (void *)&lcd800x1280_rightside_up, | |
| 196 | 223 | }, { /* |
| 197 | 224 | * Lenovo Ideapad Miix 310 laptop, only some production batches |
| 198 | 225 | * have a portrait screen, the resolution checks makes the quirk |
| @@ -211,10 +238,15 @@ static const struct dmi_system_id orientation_data[] = { · 12 unmodified lines | ||
| 211 | 238 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), |
| 212 | 239 | }, |
| 213 | 240 | .driver_data = (void *)&lcd800x1280_rightside_up, |
| 214 | − }, { /* Lenovo Ideapad D330 */ | |
| 241 | + }, { /* Lenovo Ideapad D330-10IGM (HD) */ | |
| 242 | + .matches = { | |
| 243 | + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), | |
| 244 | + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"), | |
| 245 | + }, | |
| 246 | + .driver_data = (void *)&lcd800x1280_rightside_up, | |
| 247 | + }, { /* Lenovo Ideapad D330-10IGM (FHD) */ | |
| 215 | 248 | .matches = { |
| 216 | 249 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
| 217 | − DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81H3"), | |
| 218 | 250 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"), |
| 219 | 251 | }, |
| 220 | 252 | .driver_data = (void *)&lcd1200x1920_rightside_up, |
| @@ -225,6 +257,19 @@ static const struct dmi_system_id orientation_data[] = { · 4 unmodified lines | ||
| 225 | 257 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Default string"), |
| 226 | 258 | }, |
| 227 | 259 | .driver_data = (void *)&onegx1_pro, |
| 260 | + }, { /* Samsung GalaxyBook 10.6 */ | |
| 261 | + .matches = { | |
| 262 | + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | |
| 263 | + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galaxy Book 10.6"), | |
| 264 | + }, | |
| 265 | + .driver_data = (void *)&lcd1280x1920_rightside_up, | |
| 266 | + }, { /* Valve Steam Deck */ | |
| 267 | + .matches = { | |
| 268 | + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"), | |
| 269 | + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"), | |
| 270 | + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"), | |
| 271 | + }, | |
| 272 | + .driver_data = (void *)&lcd800x1280_rightside_up, | |
| 228 | 273 | }, { /* VIOS LTH17 */ |
| 229 | 274 | .matches = { |
| 230 | 275 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"), |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | * Copyright (c) 2003-2004 IBM Corp. |
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | +#include <linux/acpi.h> | |
| 13 | 14 | #include <linux/device.h> |
| 14 | 15 | #include <linux/err.h> |
| 15 | 16 | #include <linux/export.h> |
| @@ -50,8 +51,45 @@ static struct device_type drm_sysfs_device_minor = { · 34 unmodified lines | ||
| 50 | 51 | .name = "drm_minor" |
| 51 | 52 | }; |
| 52 | 53 | |
| 54 | +static struct device_type drm_sysfs_device_connector = { | |
| 55 | + .name = "drm_connector", | |
| 56 | +}; | |
| 57 | + | |
| 53 | 58 | struct class *drm_class; |
| 54 | 59 | |
| 60 | +#ifdef CONFIG_ACPI | |
| 61 | +static bool drm_connector_acpi_bus_match(struct device *dev) | |
| 62 | +{ | |
| 63 | + return dev->type == &drm_sysfs_device_connector; | |
| 64 | +} | |
| 65 | + | |
| 66 | +static struct acpi_device *drm_connector_acpi_find_companion(struct device *dev) | |
| 67 | +{ | |
| 68 | + struct drm_connector *connector = to_drm_connector(dev); | |
| 69 | + | |
| 70 | + return to_acpi_device_node(connector->fwnode); | |
| 71 | +} | |
| 72 | + | |
| 73 | +static struct acpi_bus_type drm_connector_acpi_bus = { | |
| 74 | + .name = "drm_connector", | |
| 75 | + .match = drm_connector_acpi_bus_match, | |
| 76 | + .find_companion = drm_connector_acpi_find_companion, | |
| 77 | +}; | |
| 78 | + | |
| 79 | +static void drm_sysfs_acpi_register(void) | |
| 80 | +{ | |
| 81 | + register_acpi_bus_type(&drm_connector_acpi_bus); | |
| 82 | +} | |
| 83 | + | |
| 84 | +static void drm_sysfs_acpi_unregister(void) | |
| 85 | +{ | |
| 86 | + unregister_acpi_bus_type(&drm_connector_acpi_bus); | |
| 87 | +} | |
| 88 | +#else | |
| 89 | +static void drm_sysfs_acpi_register(void) { } | |
| 90 | +static void drm_sysfs_acpi_unregister(void) { } | |
| 91 | +#endif | |
| 92 | + | |
| 55 | 93 | static char *drm_devnode(struct device *dev, umode_t *mode) |
| 56 | 94 | { |
| 57 | 95 | return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); |
| @@ -85,6 +123,8 @@ int drm_sysfs_init(void) · 27 unmodified lines | ||
| 85 | 123 | } |
| 86 | 124 | |
| 87 | 125 | drm_class->devnode = drm_devnode; |
| 126 | + | |
| 127 | + drm_sysfs_acpi_register(); | |
| 88 | 128 | return 0; |
| 89 | 129 | } |
| 90 | 130 | |
| @@ -97,11 +137,17 @@ void drm_sysfs_destroy(void) · 6 unmodified lines | ||
| 97 | 137 | { |
| 98 | 138 | if (IS_ERR_OR_NULL(drm_class)) |
| 99 | 139 | return; |
| 140 | + drm_sysfs_acpi_unregister(); | |
| 100 | 141 | class_remove_file(drm_class, &class_attr_version.attr); |
| 101 | 142 | class_destroy(drm_class); |
| 102 | 143 | drm_class = NULL; |
| 103 | 144 | } |
| 104 | 145 | |
| 146 | +static void drm_sysfs_release(struct device *dev) | |
| 147 | +{ | |
| 148 | + kfree(dev); | |
| 149 | +} | |
| 150 | + | |
| 105 | 151 | /* |
| 106 | 152 | * Connector properties |
| 107 | 153 | */ |
| @@ -273,27 +319,47 @@ static const struct attribute_group *connector_dev_groups[] = { · 165 unmodified lines | ||
| 273 | 319 | int drm_sysfs_connector_add(struct drm_connector *connector) |
| 274 | 320 | { |
| 275 | 321 | struct drm_device *dev = connector->dev; |
| 322 | + struct device *kdev; | |
| 323 | + int r; | |
| 276 | 324 | |
| 277 | 325 | if (connector->kdev) |
| 278 | 326 | return 0; |
| 279 | 327 | |
| 280 | − connector->kdev = | |
| 281 | − device_create_with_groups(drm_class, dev->primary->kdev, 0, | |
| 282 | − connector, connector_dev_groups, | |
| 283 | − "card%d-%s", dev->primary->index, | |
| 284 | − connector->name); | |
| 328 | + kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); | |
| 329 | + if (!kdev) | |
| 330 | + return -ENOMEM; | |
| 331 | + | |
| 332 | + device_initialize(kdev); | |
| 333 | + kdev->class = drm_class; | |
| 334 | + kdev->type = &drm_sysfs_device_connector; | |
| 335 | + kdev->parent = dev->primary->kdev; | |
| 336 | + kdev->groups = connector_dev_groups; | |
| 337 | + kdev->release = drm_sysfs_release; | |
| 338 | + dev_set_drvdata(kdev, connector); | |
| 339 | + | |
| 340 | + r = dev_set_name(kdev, "card%d-%s", dev->primary->index, connector->name); | |
| 341 | + if (r) | |
| 342 | + goto err_free; | |
| 343 | + | |
| 285 | 344 | DRM_DEBUG("adding \"%s\" to sysfs\n", |
| 286 | 345 | connector->name); |
| 287 | 346 | |
| 288 | − if (IS_ERR(connector->kdev)) { | |
| 289 | − DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev)); | |
| 290 | − return PTR_ERR(connector->kdev); | |
| 347 | + r = device_add(kdev); | |
| 348 | + if (r) { | |
| 349 | + drm_err(dev, "failed to register connector device: %d\n", r); | |
| 350 | + goto err_free; | |
| 291 | 351 | } |
| 292 | 352 | |
| 353 | + connector->kdev = kdev; | |
| 354 | + | |
| 293 | 355 | if (connector->ddc) |
| 294 | 356 | return sysfs_create_link(&connector->kdev->kobj, |
| 295 | 357 | &connector->ddc->dev.kobj, "ddc"); |
| 296 | 358 | return 0; |
| 359 | + | |
| 360 | +err_free: | |
| 361 | + put_device(kdev); | |
| 362 | + return r; | |
| 297 | 363 | } |
| 298 | 364 | |
| 299 | 365 | void drm_sysfs_connector_remove(struct drm_connector *connector) |
| @@ -374,11 +440,6 @@ void drm_sysfs_connector_status_event(struct drm_connector *connector, · 74 unmodified lines | ||
| 374 | 440 | } |
| 375 | 441 | EXPORT_SYMBOL(drm_sysfs_connector_status_event); |
| 376 | 442 | |
| 377 | −static void drm_sysfs_release(struct device *dev) | |
| 378 | −{ | |
| 379 | − kfree(dev); | |
| 380 | −} | |
| 381 | − | |
| 382 | 443 | struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) |
| 383 | 444 | { |
| 384 | 445 | const char *minor_str; |
| @@ -163,6 +163,8 @@ int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity, · 162 unmodified lines | ||
| 163 | 163 | if (ret) |
| 164 | 164 | goto out_unlock; |
| 165 | 165 | |
| 166 | + drm_sched_job_arm(&submit->sched_job); | |
| 167 | + | |
| 166 | 168 | submit->out_fence = dma_fence_get(&submit->sched_job.s_fence->finished); |
| 167 | 169 | submit->out_fence_id = idr_alloc_cyclic(&submit->gpu->fence_idr, |
| 168 | 170 | submit->out_fence, 0, |
| @@ -176,7 +178,7 @@ int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity, · 7 unmodified lines | ||
| 176 | 178 | /* the scheduler holds on to the job now */ |
| 177 | 179 | kref_get(&submit->refcount); |
| 178 | 180 | |
| 179 | − drm_sched_entity_push_job(&submit->sched_job, sched_entity); | |
| 181 | + drm_sched_entity_push_job(&submit->sched_job); | |
| 180 | 182 | |
| 181 | 183 | out_unlock: |
| 182 | 184 | mutex_unlock(&submit->gpu->fence_lock); |
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | |
| 3 | 3 | config DRM_GUD |
| 4 | 4 | tristate "GUD USB Display" |
| 5 | − depends on DRM && USB | |
| 5 | + depends on DRM && USB && MMU | |
| 6 | 6 | select LZ4_COMPRESS |
| 7 | 7 | select DRM_KMS_HELPER |
| 8 | 8 | select DRM_GEM_SHMEM_HELPER |
| @@ -214,7 +214,6 @@ static void i915_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) · 213 unmodified lines | ||
| 214 | 214 | { |
| 215 | 215 | struct i915_ttm_tt *i915_tt = container_of(ttm, typeof(*i915_tt), ttm); |
| 216 | 216 | |
| 217 | − ttm_tt_destroy_common(bdev, ttm); | |
| 218 | 217 | ttm_tt_fini(ttm); |
| 219 | 218 | kfree(i915_tt); |
| 220 | 219 | } |
| @@ -267,7 +267,9 @@ static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo, · 266 unmodified lines | ||
| 267 | 267 | if (explicit) |
| 268 | 268 | return 0; |
| 269 | 269 | |
| 270 | − return drm_gem_fence_array_add_implicit(&task->deps, &bo->base.base, write); | |
| 270 | + return drm_sched_job_add_implicit_dependencies(&task->base, | |
| 271 | + &bo->base.base, | |
| 272 | + write); | |
| 271 | 273 | } |
| 272 | 274 | |
| 273 | 275 | static int lima_gem_add_deps(struct drm_file *file, struct lima_submit *submit) |
| @@ -285,7 +287,7 @@ static int lima_gem_add_deps(struct drm_file *file, struct lima_submit *submit) · 11 unmodified lines | ||
| 285 | 287 | if (err) |
| 286 | 288 | return err; |
| 287 | 289 | |
| 288 | − err = drm_gem_fence_array_add(&submit->task->deps, fence); | |
| 290 | + err = drm_sched_job_add_dependency(&submit->task->base, fence); | |
| 289 | 291 | if (err) { |
| 290 | 292 | dma_fence_put(fence); |
| 291 | 293 | return err; |
| @@ -359,8 +361,7 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) · 67 unmodified lines | ||
| 359 | 361 | goto err_out2; |
| 360 | 362 | } |
| 361 | 363 | |
| 362 | − fence = lima_sched_context_queue_task( | |
| 363 | − submit->ctx->context + submit->pipe, submit->task); | |
| 364 | + fence = lima_sched_context_queue_task(submit->task); | |
| 364 | 365 | |
| 365 | 366 | for (i = 0; i < submit->nr_bos; i++) { |
| 366 | 367 | if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE) |
| @@ -129,27 +129,20 @@ int lima_sched_task_init(struct lima_sched_task *task, · 128 unmodified lines | ||
| 129 | 129 | return err; |
| 130 | 130 | } |
| 131 | 131 | |
| 132 | + drm_sched_job_arm(&task->base); | |
| 133 | + | |
| 132 | 134 | task->num_bos = num_bos; |
| 133 | 135 | task->vm = lima_vm_get(vm); |
| 134 | 136 | |
| 135 | − xa_init_flags(&task->deps, XA_FLAGS_ALLOC); | |
| 136 | − | |
| 137 | 137 | return 0; |
| 138 | 138 | } |
| 139 | 139 | |
| 140 | 140 | void lima_sched_task_fini(struct lima_sched_task *task) |
| 141 | 141 | { |
| 142 | − struct dma_fence *fence; | |
| 143 | − unsigned long index; | |
| 144 | 142 | int i; |
| 145 | 143 | |
| 146 | 144 | drm_sched_job_cleanup(&task->base); |
| 147 | 145 | |
| 148 | − xa_for_each(&task->deps, index, fence) { | |
| 149 | − dma_fence_put(fence); | |
| 150 | − } | |
| 151 | − xa_destroy(&task->deps); | |
| 152 | − | |
| 153 | 146 | if (task->bos) { |
| 154 | 147 | for (i = 0; i < task->num_bos; i++) |
| 155 | 148 | drm_gem_object_put(&task->bos[i]->base.base); |
| @@ -175,27 +168,15 @@ void lima_sched_context_fini(struct lima_sched_pipe *pipe, · 19 unmodified lines | ||
| 175 | 168 | drm_sched_entity_fini(&context->base); |
| 176 | 169 | } |
| 177 | 170 | |
| 178 | −struct dma_fence *lima_sched_context_queue_task(struct lima_sched_context *context, | |
| 179 | − struct lima_sched_task *task) | |
| 171 | +struct dma_fence *lima_sched_context_queue_task(struct lima_sched_task *task) | |
| 180 | 172 | { |
| 181 | 173 | struct dma_fence *fence = dma_fence_get(&task->base.s_fence->finished); |
| 182 | 174 | |
| 183 | 175 | trace_lima_task_submit(task); |
| 184 | − drm_sched_entity_push_job(&task->base, &context->base); | |
| 176 | + drm_sched_entity_push_job(&task->base); | |
| 185 | 177 | return fence; |
| 186 | 178 | } |
| 187 | 179 | |
| 188 | −static struct dma_fence *lima_sched_dependency(struct drm_sched_job *job, | |
| 189 | − struct drm_sched_entity *entity) | |
| 190 | −{ | |
| 191 | − struct lima_sched_task *task = to_lima_task(job); | |
| 192 | − | |
| 193 | − if (!xa_empty(&task->deps)) | |
| 194 | − return xa_erase(&task->deps, task->last_dep++); | |
| 195 | − | |
| 196 | − return NULL; | |
| 197 | −} | |
| 198 | − | |
| 199 | 180 | static int lima_pm_busy(struct lima_device *ldev) |
| 200 | 181 | { |
| 201 | 182 | int ret; |
| @@ -471,7 +452,6 @@ static void lima_sched_free_job(struct drm_sched_job *job) · 269 unmodified lines | ||
| 471 | 452 | } |
| 472 | 453 | |
| 473 | 454 | static const struct drm_sched_backend_ops lima_sched_ops = { |
| 474 | − .dependency = lima_sched_dependency, | |
| 475 | 455 | .run_job = lima_sched_run_job, |
| 476 | 456 | .timedout_job = lima_sched_timedout_job, |
| 477 | 457 | .free_job = lima_sched_free_job, |
| @@ -23,9 +23,6 @@ struct lima_sched_task { · 22 unmodified lines | ||
| 23 | 23 | struct lima_vm *vm; |
| 24 | 24 | void *frame; |
| 25 | 25 | |
| 26 | − struct xarray deps; | |
| 27 | − unsigned long last_dep; | |
| 28 | − | |
| 29 | 26 | struct lima_bo **bos; |
| 30 | 27 | int num_bos; |
| 31 | 28 | |
| @@ -98,8 +95,7 @@ int lima_sched_context_init(struct lima_sched_pipe *pipe, · 66 unmodified lines | ||
| 98 | 95 | atomic_t *guilty); |
| 99 | 96 | void lima_sched_context_fini(struct lima_sched_pipe *pipe, |
| 100 | 97 | struct lima_sched_context *context); |
| 101 | −struct dma_fence *lima_sched_context_queue_task(struct lima_sched_context *context, | |
| 102 | − struct lima_sched_task *task); | |
| 98 | +struct dma_fence *lima_sched_context_queue_task(struct lima_sched_task *task); | |
| 103 | 99 | |
| 104 | 100 | int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name); |
| 105 | 101 | void lima_sched_pipe_fini(struct lima_sched_pipe *pipe); |
| @@ -276,7 +276,6 @@ static int mcde_probe(struct platform_device *pdev) · 275 unmodified lines | ||
| 276 | 276 | struct drm_device *drm; |
| 277 | 277 | struct mcde *mcde; |
| 278 | 278 | struct component_match *match = NULL; |
| 279 | − struct resource *res; | |
| 280 | 279 | u32 pid; |
| 281 | 280 | int irq; |
| 282 | 281 | int ret; |
| @@ -344,8 +343,7 @@ static int mcde_probe(struct platform_device *pdev) · 61 unmodified lines | ||
| 344 | 343 | goto clk_disable; |
| 345 | 344 | } |
| 346 | 345 | |
| 347 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 348 | − mcde->regs = devm_ioremap_resource(dev, res); | |
| 346 | + mcde->regs = devm_platform_ioremap_resource(pdev, 0); | |
| 349 | 347 | if (IS_ERR(mcde->regs)) { |
| 350 | 348 | dev_err(dev, "no MCDE regs\n"); |
| 351 | 349 | ret = -EINVAL; |
| @@ -1169,7 +1169,6 @@ static int mcde_dsi_probe(struct platform_device *pdev) · 1168 unmodified lines | ||
| 1169 | 1169 | struct device *dev = &pdev->dev; |
| 1170 | 1170 | struct mcde_dsi *d; |
| 1171 | 1171 | struct mipi_dsi_host *host; |
| 1172 | − struct resource *res; | |
| 1173 | 1172 | u32 dsi_id; |
| 1174 | 1173 | int ret; |
| 1175 | 1174 | |
| @@ -1187,8 +1186,7 @@ static int mcde_dsi_probe(struct platform_device *pdev) · 11 unmodified lines | ||
| 1187 | 1186 | return PTR_ERR(d->prcmu); |
| 1188 | 1187 | } |
| 1189 | 1188 | |
| 1190 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 1191 | − d->regs = devm_ioremap_resource(dev, res); | |
| 1189 | + d->regs = devm_platform_ioremap_resource(pdev, 0); | |
| 1192 | 1190 | if (IS_ERR(d->regs)) |
| 1193 | 1191 | return PTR_ERR(d->regs); |
| 1194 | 1192 | |
| @@ -206,8 +206,7 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) · 205 unmodified lines | ||
| 206 | 206 | priv->compat = match->compat; |
| 207 | 207 | priv->afbcd.ops = match->afbcd_ops; |
| 208 | 208 | |
| 209 | − res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); | |
| 210 | − regs = devm_ioremap_resource(dev, res); | |
| 209 | + regs = devm_platform_ioremap_resource_byname(pdev, "vpu"); | |
| 211 | 210 | if (IS_ERR(regs)) { |
| 212 | 211 | ret = PTR_ERR(regs); |
| 213 | 212 | goto free_drm; |
| @@ -978,7 +978,6 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master, · 977 unmodified lines | ||
| 978 | 978 | struct dw_hdmi_plat_data *dw_plat_data; |
| 979 | 979 | struct drm_bridge *next_bridge; |
| 980 | 980 | struct drm_encoder *encoder; |
| 981 | − struct resource *res; | |
| 982 | 981 | int irq; |
| 983 | 982 | int ret; |
| 984 | 983 | |
| @@ -1042,8 +1041,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master, · 57 unmodified lines | ||
| 1042 | 1041 | return PTR_ERR(meson_dw_hdmi->hdmitx_phy); |
| 1043 | 1042 | } |
| 1044 | 1043 | |
| 1045 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 1046 | − meson_dw_hdmi->hdmitx = devm_ioremap_resource(dev, res); | |
| 1044 | + meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0); | |
| 1047 | 1045 | if (IS_ERR(meson_dw_hdmi->hdmitx)) |
| 1048 | 1046 | return PTR_ERR(meson_dw_hdmi->hdmitx); |
| 1049 | 1047 | |
| @@ -309,11 +309,6 @@ struct msm_gem_submit { · 308 unmodified lines | ||
| 309 | 309 | struct ww_acquire_ctx ticket; |
| 310 | 310 | uint32_t seqno; /* Sequence number of the submit on the ring */ |
| 311 | 311 | |
| 312 | − /* Array of struct dma_fence * to block on before submitting this job. | |
| 313 | − */ | |
| 314 | − struct xarray deps; | |
| 315 | − unsigned long last_dep; | |
| 316 | − | |
| 317 | 312 | /* Hw fence, which is created when the scheduler executes the job, and |
| 318 | 313 | * is signaled when the hw finishes (via seqno write from cmdstream) |
| 319 | 314 | */ |
| @@ -52,8 +52,6 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, · 51 unmodified lines | ||
| 52 | 52 | return ERR_PTR(ret); |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | − xa_init_flags(&submit->deps, XA_FLAGS_ALLOC); | |
| 56 | − | |
| 57 | 55 | kref_init(&submit->ref); |
| 58 | 56 | submit->dev = dev; |
| 59 | 57 | submit->aspace = queue->ctx->aspace; |
| @@ -72,8 +70,6 @@ void __msm_gem_submit_destroy(struct kref *kref) · 12 unmodified lines | ||
| 72 | 70 | { |
| 73 | 71 | struct msm_gem_submit *submit = |
| 74 | 72 | container_of(kref, struct msm_gem_submit, ref); |
| 75 | − unsigned long index; | |
| 76 | − struct dma_fence *fence; | |
| 77 | 73 | unsigned i; |
| 78 | 74 | |
| 79 | 75 | if (submit->fence_id) { |
| @@ -82,12 +78,6 @@ void __msm_gem_submit_destroy(struct kref *kref) · 2 unmodified lines | ||
| 82 | 78 | mutex_unlock(&submit->queue->lock); |
| 83 | 79 | } |
| 84 | 80 | |
| 85 | − xa_for_each (&submit->deps, index, fence) { | |
| 86 | − dma_fence_put(fence); | |
| 87 | − } | |
| 88 | − | |
| 89 | − xa_destroy(&submit->deps); | |
| 90 | − | |
| 91 | 81 | dma_fence_put(submit->user_fence); |
| 92 | 82 | dma_fence_put(submit->hw_fence); |
| 93 | 83 | |
| @@ -340,11 +330,13 @@ static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit) · 246 unmodified lines | ||
| 340 | 330 | return ret; |
| 341 | 331 | } |
| 342 | 332 | |
| 343 | − if (no_implicit) | |
| 333 | + /* exclusive fences must be ordered */ | |
| 334 | + if (no_implicit && !write) | |
| 344 | 335 | continue; |
| 345 | 336 | |
| 346 | − ret = drm_gem_fence_array_add_implicit(&submit->deps, obj, | |
| 347 | − write); | |
| 337 | + ret = drm_sched_job_add_implicit_dependencies(&submit->base, | |
| 338 | + obj, | |
| 339 | + write); | |
| 348 | 340 | if (ret) |
| 349 | 341 | break; |
| 350 | 342 | } |
| @@ -588,7 +580,7 @@ static struct drm_syncobj **msm_parse_deps(struct msm_gem_submit *submit, · 237 unmodified lines | ||
| 588 | 580 | if (ret) |
| 589 | 581 | break; |
| 590 | 582 | |
| 591 | − ret = drm_gem_fence_array_add(&submit->deps, fence); | |
| 583 | + ret = drm_sched_job_add_dependency(&submit->base, fence); | |
| 592 | 584 | if (ret) |
| 593 | 585 | break; |
| 594 | 586 | |
| @@ -798,7 +790,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, · 203 unmodified lines | ||
| 798 | 790 | goto out_unlock; |
| 799 | 791 | } |
| 800 | 792 | |
| 801 | − ret = drm_gem_fence_array_add(&submit->deps, in_fence); | |
| 793 | + ret = drm_sched_job_add_dependency(&submit->base, in_fence); | |
| 802 | 794 | if (ret) |
| 803 | 795 | goto out_unlock; |
| 804 | 796 | } |
| @@ -878,6 +870,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, · 73 unmodified lines | ||
| 878 | 870 | |
| 879 | 871 | submit->nr_cmds = i; |
| 880 | 872 | |
| 873 | + drm_sched_job_arm(&submit->base); | |
| 874 | + | |
| 881 | 875 | submit->user_fence = dma_fence_get(&submit->base.s_fence->finished); |
| 882 | 876 | |
| 883 | 877 | /* |
| @@ -889,17 +883,16 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, · 5 unmodified lines | ||
| 889 | 883 | if (submit->fence_id < 0) { |
| 890 | 884 | ret = submit->fence_id = 0; |
| 891 | 885 | submit->fence_id = 0; |
| 892 | − goto out; | |
| 893 | 886 | } |
| 894 | 887 | |
| 895 | − if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) { | |
| 888 | + if (ret == 0 && args->flags & MSM_SUBMIT_FENCE_FD_OUT) { | |
| 896 | 889 | struct sync_file *sync_file = sync_file_create(submit->user_fence); |
| 897 | 890 | if (!sync_file) { |
| 898 | 891 | ret = -ENOMEM; |
| 899 | − goto out; | |
| 892 | + } else { | |
| 893 | + fd_install(out_fence_fd, sync_file->file); | |
| 894 | + args->fence_fd = out_fence_fd; | |
| 900 | 895 | } |
| 901 | − fd_install(out_fence_fd, sync_file->file); | |
| 902 | − args->fence_fd = out_fence_fd; | |
| 903 | 896 | } |
| 904 | 897 | |
| 905 | 898 | submit_attach_object_fences(submit); |
| @@ -907,7 +900,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, · 1 unmodified line | ||
| 907 | 900 | /* The scheduler owns a ref now: */ |
| 908 | 901 | msm_gem_submit_get(submit); |
| 909 | 902 | |
| 910 | − drm_sched_entity_push_job(&submit->base, &queue->entity); | |
| 903 | + drm_sched_entity_push_job(&submit->base); | |
| 911 | 904 | |
| 912 | 905 | args->fence = submit->fence_id; |
| 913 | 906 | |
| @@ -11,17 +11,6 @@ static uint num_hw_submissions = 8; · 10 unmodified lines | ||
| 11 | 11 | MODULE_PARM_DESC(num_hw_submissions, "The max # of jobs to write into ringbuffer (default 8)"); |
| 12 | 12 | module_param(num_hw_submissions, uint, 0600); |
| 13 | 13 | |
| 14 | −static struct dma_fence *msm_job_dependency(struct drm_sched_job *job, | |
| 15 | − struct drm_sched_entity *s_entity) | |
| 16 | −{ | |
| 17 | − struct msm_gem_submit *submit = to_msm_submit(job); | |
| 18 | − | |
| 19 | − if (!xa_empty(&submit->deps)) | |
| 20 | − return xa_erase(&submit->deps, submit->last_dep++); | |
| 21 | − | |
| 22 | − return NULL; | |
| 23 | −} | |
| 24 | − | |
| 25 | 14 | static struct dma_fence *msm_job_run(struct drm_sched_job *job) |
| 26 | 15 | { |
| 27 | 16 | struct msm_gem_submit *submit = to_msm_submit(job); |
| @@ -52,7 +41,6 @@ static void msm_job_free(struct drm_sched_job *job) · 24 unmodified lines | ||
| 52 | 41 | } |
| 53 | 42 | |
| 54 | 43 | const struct drm_sched_backend_ops msm_sched_ops = { |
| 55 | − .dependency = msm_job_dependency, | |
| 56 | 44 | .run_job = msm_job_run, |
| 57 | 45 | .free_job = msm_job_free |
| 58 | 46 | }; |
| @@ -1277,6 +1277,8 @@ nouveau_ttm_tt_unpopulate(struct ttm_device *bdev, · 1276 unmodified lines | ||
| 1277 | 1277 | if (slave) |
| 1278 | 1278 | return; |
| 1279 | 1279 | |
| 1280 | + nouveau_ttm_tt_unbind(bdev, ttm); | |
| 1281 | + | |
| 1280 | 1282 | drm = nouveau_bdev(bdev); |
| 1281 | 1283 | dev = drm->dev->dev; |
| 1282 | 1284 | |
| @@ -1290,8 +1292,6 @@ nouveau_ttm_tt_destroy(struct ttm_device *bdev, · 7 unmodified lines | ||
| 1290 | 1292 | #if IS_ENABLED(CONFIG_AGP) |
| 1291 | 1293 | struct nouveau_drm *drm = nouveau_bdev(bdev); |
| 1292 | 1294 | if (drm->agp.bridge) { |
| 1293 | − ttm_agp_unbind(ttm); | |
| 1294 | − ttm_tt_destroy_common(bdev, ttm); | |
| 1295 | 1295 | ttm_agp_destroy(ttm); |
| 1296 | 1296 | return; |
| 1297 | 1297 | } |
| @@ -21,8 +21,6 @@ nouveau_sgdma_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) · 20 unmodified lines | ||
| 21 | 21 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; |
| 22 | 22 | |
| 23 | 23 | if (ttm) { |
| 24 | − nouveau_sgdma_unbind(bdev, ttm); | |
| 25 | − ttm_tt_destroy_common(bdev, ttm); | |
| 26 | 24 | ttm_tt_fini(&nvbe->ttm); |
| 27 | 25 | kfree(nvbe); |
| 28 | 26 | } |
| @@ -3,7 +3,6 @@ config DRM_OMAP · 2 unmodified lines | ||
| 3 | 3 | tristate "OMAP DRM" |
| 4 | 4 | depends on DRM |
| 5 | 5 | depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM |
| 6 | − select OMAP2_DSS | |
| 7 | 6 | select DRM_KMS_HELPER |
| 8 | 7 | select VIDEOMODE_HELPERS |
| 9 | 8 | select HDMI |
| @@ -392,6 +392,17 @@ config DRM_PANEL_SAMSUNG_S6D16D0 · 391 unmodified lines | ||
| 392 | 392 | depends on DRM_MIPI_DSI |
| 393 | 393 | select VIDEOMODE_HELPERS |
| 394 | 394 | |
| 395 | +config DRM_PANEL_SAMSUNG_S6D27A1 | |
| 396 | + tristate "Samsung S6D27A1 DPI panel driver" | |
| 397 | + depends on OF && SPI && GPIOLIB | |
| 398 | + select DRM_MIPI_DBI | |
| 399 | + help | |
| 400 | + Say Y here if you want to enable support for the Samsung | |
| 401 | + S6D27A1 DPI 480x800 panel. | |
| 402 | + | |
| 403 | + This panel can be found in Samsung Galaxy Ace 2 | |
| 404 | + GT-I8160 mobile phone. | |
| 405 | + | |
| 395 | 406 | config DRM_PANEL_SAMSUNG_S6E3HA2 |
| 396 | 407 | tristate "Samsung S6E3HA2 DSI video mode panel" |
| 397 | 408 | depends on OF |
| @@ -39,6 +39,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o · 38 unmodified lines | ||
| 39 | 39 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o |
| 40 | 40 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o |
| 41 | 41 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o |
| 42 | +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D27A1) += panel-samsung-s6d27a1.o | |
| 42 | 43 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o |
| 43 | 44 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o |
| 44 | 45 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o |
| @@ -60,6 +60,9 @@ · 59 unmodified lines | ||
| 60 | 60 | #define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */ |
| 61 | 61 | #define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */ |
| 62 | 62 | |
| 63 | +#define OTM8009A_HDISPLAY 480 | |
| 64 | +#define OTM8009A_VDISPLAY 800 | |
| 65 | + | |
| 63 | 66 | struct otm8009a { |
| 64 | 67 | struct device *dev; |
| 65 | 68 | struct drm_panel panel; |
| @@ -70,19 +73,35 @@ struct otm8009a { · 4 unmodified lines | ||
| 70 | 73 | bool enabled; |
| 71 | 74 | }; |
| 72 | 75 | |
| 73 | −static const struct drm_display_mode default_mode = { | |
| 74 | − .clock = 29700, | |
| 75 | − .hdisplay = 480, | |
| 76 | − .hsync_start = 480 + 98, | |
| 77 | − .hsync_end = 480 + 98 + 32, | |
| 78 | − .htotal = 480 + 98 + 32 + 98, | |
| 79 | − .vdisplay = 800, | |
| 80 | − .vsync_start = 800 + 15, | |
| 81 | − .vsync_end = 800 + 15 + 10, | |
| 82 | − .vtotal = 800 + 15 + 10 + 14, | |
| 83 | − .flags = 0, | |
| 84 | − .width_mm = 52, | |
| 85 | − .height_mm = 86, | |
| 76 | +static const struct drm_display_mode modes[] = { | |
| 77 | + { /* 50 Hz, preferred */ | |
| 78 | + .clock = 29700, | |
| 79 | + .hdisplay = 480, | |
| 80 | + .hsync_start = 480 + 98, | |
| 81 | + .hsync_end = 480 + 98 + 32, | |
| 82 | + .htotal = 480 + 98 + 32 + 98, | |
| 83 | + .vdisplay = 800, | |
| 84 | + .vsync_start = 800 + 15, | |
| 85 | + .vsync_end = 800 + 15 + 10, | |
| 86 | + .vtotal = 800 + 15 + 10 + 14, | |
| 87 | + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, | |
| 88 | + .width_mm = 52, | |
| 89 | + .height_mm = 86, | |
| 90 | + }, | |
| 91 | + { /* 60 Hz */ | |
| 92 | + .clock = 33000, | |
| 93 | + .hdisplay = 480, | |
| 94 | + .hsync_start = 480 + 70, | |
| 95 | + .hsync_end = 480 + 70 + 32, | |
| 96 | + .htotal = 480 + 70 + 32 + 72, | |
| 97 | + .vdisplay = 800, | |
| 98 | + .vsync_start = 800 + 15, | |
| 99 | + .vsync_end = 800 + 15 + 10, | |
| 100 | + .vtotal = 800 + 15 + 10 + 16, | |
| 101 | + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, | |
| 102 | + .width_mm = 52, | |
| 103 | + .height_mm = 86, | |
| 104 | + }, | |
| 86 | 105 | }; |
| 87 | 106 | |
| 88 | 107 | static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel) |
| @@ -208,12 +227,11 @@ static int otm8009a_init_sequence(struct otm8009a *ctx) · 119 unmodified lines | ||
| 208 | 227 | /* Default portrait 480x800 rgb24 */ |
| 209 | 228 | dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); |
| 210 | 229 | |
| 211 | − ret = mipi_dsi_dcs_set_column_address(dsi, 0, | |
| 212 | − default_mode.hdisplay - 1); | |
| 230 | + ret = mipi_dsi_dcs_set_column_address(dsi, 0, OTM8009A_HDISPLAY - 1); | |
| 213 | 231 | if (ret) |
| 214 | 232 | return ret; |
| 215 | 233 | |
| 216 | − ret = mipi_dsi_dcs_set_page_address(dsi, 0, default_mode.vdisplay - 1); | |
| 234 | + ret = mipi_dsi_dcs_set_page_address(dsi, 0, OTM8009A_VDISPLAY - 1); | |
| 217 | 235 | if (ret) |
| 218 | 236 | return ret; |
| 219 | 237 | |
| @@ -337,24 +355,33 @@ static int otm8009a_get_modes(struct drm_panel *panel, · 117 unmodified lines | ||
| 337 | 355 | struct drm_connector *connector) |
| 338 | 356 | { |
| 339 | 357 | struct drm_display_mode *mode; |
| 340 | − | |
| 341 | − mode = drm_mode_duplicate(connector->dev, &default_mode); | |
| 342 | − if (!mode) { | |
| 343 | − dev_err(panel->dev, "failed to add mode %ux%u@%u\n", | |
| 344 | − default_mode.hdisplay, default_mode.vdisplay, | |
| 345 | − drm_mode_vrefresh(&default_mode)); | |
| 346 | − return -ENOMEM; | |
| 358 | + unsigned int num_modes = ARRAY_SIZE(modes); | |
| 359 | + unsigned int i; | |
| 360 | + | |
| 361 | + for (i = 0; i < num_modes; i++) { | |
| 362 | + mode = drm_mode_duplicate(connector->dev, &modes[i]); | |
| 363 | + if (!mode) { | |
| 364 | + dev_err(panel->dev, "failed to add mode %ux%u@%u\n", | |
| 365 | + modes[i].hdisplay, | |
| 366 | + modes[i].vdisplay, | |
| 367 | + drm_mode_vrefresh(&modes[i])); | |
| 368 | + return -ENOMEM; | |
| 369 | + } | |
| 370 | + | |
| 371 | + mode->type = DRM_MODE_TYPE_DRIVER; | |
| 372 | + | |
| 373 | + /* Setting first mode as preferred */ | |
| 374 | + if (!i) | |
| 375 | + mode->type |= DRM_MODE_TYPE_PREFERRED; | |
| 376 | + | |
| 377 | + drm_mode_set_name(mode); | |
| 378 | + drm_mode_probed_add(connector, mode); | |
| 347 | 379 | } |
| 348 | 380 | |
| 349 | − drm_mode_set_name(mode); | |
| 350 | − | |
| 351 | − mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | |
| 352 | − drm_mode_probed_add(connector, mode); | |
| 353 | − | |
| 354 | 381 | connector->display_info.width_mm = mode->width_mm; |
| 355 | 382 | connector->display_info.height_mm = mode->height_mm; |
| 356 | 383 | |
| 357 | − return 1; | |
| 384 | + return num_modes; | |
| 358 | 385 | } |
| 359 | 386 | |
| 360 | 387 | static const struct drm_panel_funcs otm8009a_drm_funcs = { |
| @@ -0,0 +1,320 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Panel driver for the Samsung S6D27A1 480x800 DPI RGB panel. | |
| 4 | + * Found in the Samsung Galaxy Ace 2 GT-I8160 mobile phone. | |
| 5 | + */ | |
| 6 | + | |
| 7 | +#include <drm/drm_mipi_dbi.h> | |
| 8 | +#include <drm/drm_modes.h> | |
| 9 | +#include <drm/drm_panel.h> | |
| 10 | + | |
| 11 | +#include <linux/delay.h> | |
| 12 | +#include <linux/gpio/consumer.h> | |
| 13 | +#include <linux/init.h> | |
| 14 | +#include <linux/kernel.h> | |
| 15 | +#include <linux/media-bus-format.h> | |
| 16 | +#include <linux/module.h> | |
| 17 | +#include <linux/of.h> | |
| 18 | +#include <linux/regulator/consumer.h> | |
| 19 | +#include <linux/spi/spi.h> | |
| 20 | + | |
| 21 | +#include <video/mipi_display.h> | |
| 22 | + | |
| 23 | +#define S6D27A1_PASSWD_L2 0xF0 /* Password Command for Level 2 Control */ | |
| 24 | +#define S6D27A1_RESCTL 0xB3 /* Resolution Select Control */ | |
| 25 | +#define S6D27A1_PANELCTL2 0xB4 /* ASG Signal Control */ | |
| 26 | +#define S6D27A1_READID1 0xDA /* Read panel ID 1 */ | |
| 27 | +#define S6D27A1_READID2 0xDB /* Read panel ID 2 */ | |
| 28 | +#define S6D27A1_READID3 0xDC /* Read panel ID 3 */ | |
| 29 | +#define S6D27A1_DISPCTL 0xF2 /* Display Control */ | |
| 30 | +#define S6D27A1_MANPWR 0xF3 /* Manual Control */ | |
| 31 | +#define S6D27A1_PWRCTL1 0xF4 /* Power Control */ | |
| 32 | +#define S6D27A1_SRCCTL 0xF6 /* Source Control */ | |
| 33 | +#define S6D27A1_PANELCTL 0xF7 /* Panel Control*/ | |
| 34 | + | |
| 35 | +static const u8 s6d27a1_dbi_read_commands[] = { | |
| 36 | + S6D27A1_READID1, | |
| 37 | + S6D27A1_READID2, | |
| 38 | + S6D27A1_READID3, | |
| 39 | + 0, /* sentinel */ | |
| 40 | +}; | |
| 41 | + | |
| 42 | +struct s6d27a1 { | |
| 43 | + struct device *dev; | |
| 44 | + struct mipi_dbi dbi; | |
| 45 | + struct drm_panel panel; | |
| 46 | + struct gpio_desc *reset; | |
| 47 | + struct regulator_bulk_data regulators[2]; | |
| 48 | +}; | |
| 49 | + | |
| 50 | +static const struct drm_display_mode s6d27a1_480_800_mode = { | |
| 51 | + /* | |
| 52 | + * The vendor driver states that the S6D27A1 panel | |
| 53 | + * has a pixel clock frequency of 49920000 Hz / 2 = 24960000 Hz. | |
| 54 | + */ | |
| 55 | + .clock = 24960, | |
| 56 | + .hdisplay = 480, | |
| 57 | + .hsync_start = 480 + 63, | |
| 58 | + .hsync_end = 480 + 63 + 2, | |
| 59 | + .htotal = 480 + 63 + 2 + 63, | |
| 60 | + .vdisplay = 800, | |
| 61 | + .vsync_start = 800 + 11, | |
| 62 | + .vsync_end = 800 + 11 + 2, | |
| 63 | + .vtotal = 800 + 11 + 2 + 10, | |
| 64 | + .width_mm = 50, | |
| 65 | + .height_mm = 84, | |
| 66 | + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, | |
| 67 | +}; | |
| 68 | + | |
| 69 | +static inline struct s6d27a1 *to_s6d27a1(struct drm_panel *panel) | |
| 70 | +{ | |
| 71 | + return container_of(panel, struct s6d27a1, panel); | |
| 72 | +} | |
| 73 | + | |
| 74 | +static void s6d27a1_read_mtp_id(struct s6d27a1 *ctx) | |
| 75 | +{ | |
| 76 | + struct mipi_dbi *dbi = &ctx->dbi; | |
| 77 | + u8 id1, id2, id3; | |
| 78 | + int ret; | |
| 79 | + | |
| 80 | + ret = mipi_dbi_command_read(dbi, S6D27A1_READID1, &id1); | |
| 81 | + if (ret) { | |
| 82 | + dev_err(ctx->dev, "unable to read MTP ID 1\n"); | |
| 83 | + return; | |
| 84 | + } | |
| 85 | + ret = mipi_dbi_command_read(dbi, S6D27A1_READID2, &id2); | |
| 86 | + if (ret) { | |
| 87 | + dev_err(ctx->dev, "unable to read MTP ID 2\n"); | |
| 88 | + return; | |
| 89 | + } | |
| 90 | + ret = mipi_dbi_command_read(dbi, S6D27A1_READID3, &id3); | |
| 91 | + if (ret) { | |
| 92 | + dev_err(ctx->dev, "unable to read MTP ID 3\n"); | |
| 93 | + return; | |
| 94 | + } | |
| 95 | + dev_info(ctx->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3); | |
| 96 | +} | |
| 97 | + | |
| 98 | +static int s6d27a1_power_on(struct s6d27a1 *ctx) | |
| 99 | +{ | |
| 100 | + struct mipi_dbi *dbi = &ctx->dbi; | |
| 101 | + int ret; | |
| 102 | + | |
| 103 | + /* Power up */ | |
| 104 | + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->regulators), | |
| 105 | + ctx->regulators); | |
| 106 | + if (ret) { | |
| 107 | + dev_err(ctx->dev, "failed to enable regulators: %d\n", ret); | |
| 108 | + return ret; | |
| 109 | + } | |
| 110 | + | |
| 111 | + msleep(20); | |
| 112 | + | |
| 113 | + /* Assert reset >=1 ms */ | |
| 114 | + gpiod_set_value_cansleep(ctx->reset, 1); | |
| 115 | + usleep_range(1000, 5000); | |
| 116 | + /* De-assert reset */ | |
| 117 | + gpiod_set_value_cansleep(ctx->reset, 0); | |
| 118 | + /* Wait >= 10 ms */ | |
| 119 | + msleep(20); | |
| 120 | + | |
| 121 | + /* | |
| 122 | + * Exit sleep mode and initialize display - some hammering is | |
| 123 | + * necessary. | |
| 124 | + */ | |
| 125 | + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); | |
| 126 | + mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); | |
| 127 | + msleep(120); | |
| 128 | + | |
| 129 | + /* Magic to unlock level 2 control of the display */ | |
| 130 | + mipi_dbi_command(dbi, S6D27A1_PASSWD_L2, 0x5A, 0x5A); | |
| 131 | + | |
| 132 | + /* Configure resolution to 480RGBx800 */ | |
| 133 | + mipi_dbi_command(dbi, S6D27A1_RESCTL, 0x22); | |
| 134 | + | |
| 135 | + mipi_dbi_command(dbi, S6D27A1_PANELCTL2, 0x00, 0x02, 0x03, 0x04, 0x05, 0x08, 0x00, 0x0c); | |
| 136 | + | |
| 137 | + mipi_dbi_command(dbi, S6D27A1_MANPWR, 0x01, 0x00, 0x00, 0x08, 0x08, 0x02, 0x00); | |
| 138 | + | |
| 139 | + mipi_dbi_command(dbi, S6D27A1_DISPCTL, 0x19, 0x00, 0x08, 0x0D, 0x03, 0x41, 0x3F); | |
| 140 | + | |
| 141 | + mipi_dbi_command(dbi, S6D27A1_PWRCTL1, 0x00, 0x00, 0x00, 0x00, 0x55, | |
| 142 | + 0x44, 0x05, 0x88, 0x4B, 0x50); | |
| 143 | + | |
| 144 | + mipi_dbi_command(dbi, S6D27A1_SRCCTL, 0x03, 0x09, 0x8A, 0x00, 0x01, 0x16); | |
| 145 | + | |
| 146 | + mipi_dbi_command(dbi, S6D27A1_PANELCTL, 0x00, 0x05, 0x06, 0x07, 0x08, | |
| 147 | + 0x01, 0x09, 0x0D, 0x0A, 0x0E, | |
| 148 | + 0x0B, 0x0F, 0x0C, 0x10, 0x01, | |
| 149 | + 0x11, 0x12, 0x13, 0x14, 0x05, | |
| 150 | + 0x06, 0x07, 0x08, 0x01, 0x09, | |
| 151 | + 0x0D, 0x0A, 0x0E, 0x0B, 0x0F, | |
| 152 | + 0x0C, 0x10, 0x01, 0x11, 0x12, | |
| 153 | + 0x13, 0x14); | |
| 154 | + | |
| 155 | + /* lock the level 2 control */ | |
| 156 | + mipi_dbi_command(dbi, S6D27A1_PASSWD_L2, 0xA5, 0xA5); | |
| 157 | + | |
| 158 | + s6d27a1_read_mtp_id(ctx); | |
| 159 | + | |
| 160 | + return 0; | |
| 161 | +} | |
| 162 | + | |
| 163 | +static int s6d27a1_power_off(struct s6d27a1 *ctx) | |
| 164 | +{ | |
| 165 | + /* Go into RESET and disable regulators */ | |
| 166 | + gpiod_set_value_cansleep(ctx->reset, 1); | |
| 167 | + return regulator_bulk_disable(ARRAY_SIZE(ctx->regulators), | |
| 168 | + ctx->regulators); | |
| 169 | +} | |
| 170 | + | |
| 171 | +static int s6d27a1_unprepare(struct drm_panel *panel) | |
| 172 | +{ | |
| 173 | + struct s6d27a1 *ctx = to_s6d27a1(panel); | |
| 174 | + struct mipi_dbi *dbi = &ctx->dbi; | |
| 175 | + | |
| 176 | + mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); | |
| 177 | + msleep(120); | |
| 178 | + return s6d27a1_power_off(to_s6d27a1(panel)); | |
| 179 | +} | |
| 180 | + | |
| 181 | +static int s6d27a1_disable(struct drm_panel *panel) | |
| 182 | +{ | |
| 183 | + struct s6d27a1 *ctx = to_s6d27a1(panel); | |
| 184 | + struct mipi_dbi *dbi = &ctx->dbi; | |
| 185 | + | |
| 186 | + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); | |
| 187 | + msleep(25); | |
| 188 | + | |
| 189 | + return 0; | |
| 190 | +} | |
| 191 | + | |
| 192 | +static int s6d27a1_prepare(struct drm_panel *panel) | |
| 193 | +{ | |
| 194 | + return s6d27a1_power_on(to_s6d27a1(panel)); | |
| 195 | +} | |
| 196 | + | |
| 197 | +static int s6d27a1_enable(struct drm_panel *panel) | |
| 198 | +{ | |
| 199 | + struct s6d27a1 *ctx = to_s6d27a1(panel); | |
| 200 | + struct mipi_dbi *dbi = &ctx->dbi; | |
| 201 | + | |
| 202 | + mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); | |
| 203 | + | |
| 204 | + return 0; | |
| 205 | +} | |
| 206 | + | |
| 207 | +static int s6d27a1_get_modes(struct drm_panel *panel, | |
| 208 | + struct drm_connector *connector) | |
| 209 | +{ | |
| 210 | + struct s6d27a1 *ctx = to_s6d27a1(panel); | |
| 211 | + struct drm_display_mode *mode; | |
| 212 | + static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; | |
| 213 | + | |
| 214 | + mode = drm_mode_duplicate(connector->dev, &s6d27a1_480_800_mode); | |
| 215 | + if (!mode) { | |
| 216 | + dev_err(ctx->dev, "failed to add mode\n"); | |
| 217 | + return -ENOMEM; | |
| 218 | + } | |
| 219 | + | |
| 220 | + connector->display_info.bpc = 8; | |
| 221 | + connector->display_info.width_mm = mode->width_mm; | |
| 222 | + connector->display_info.height_mm = mode->height_mm; | |
| 223 | + connector->display_info.bus_flags = | |
| 224 | + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; | |
| 225 | + drm_display_info_set_bus_formats(&connector->display_info, | |
| 226 | + &bus_format, 1); | |
| 227 | + | |
| 228 | + drm_mode_set_name(mode); | |
| 229 | + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | |
| 230 | + | |
| 231 | + drm_mode_probed_add(connector, mode); | |
| 232 | + | |
| 233 | + return 1; | |
| 234 | +} | |
| 235 | + | |
| 236 | +static const struct drm_panel_funcs s6d27a1_drm_funcs = { | |
| 237 | + .disable = s6d27a1_disable, | |
| 238 | + .unprepare = s6d27a1_unprepare, | |
| 239 | + .prepare = s6d27a1_prepare, | |
| 240 | + .enable = s6d27a1_enable, | |
| 241 | + .get_modes = s6d27a1_get_modes, | |
| 242 | +}; | |
| 243 | + | |
| 244 | +static int s6d27a1_probe(struct spi_device *spi) | |
| 245 | +{ | |
| 246 | + struct device *dev = &spi->dev; | |
| 247 | + struct s6d27a1 *ctx; | |
| 248 | + int ret; | |
| 249 | + | |
| 250 | + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | |
| 251 | + if (!ctx) | |
| 252 | + return -ENOMEM; | |
| 253 | + | |
| 254 | + ctx->dev = dev; | |
| 255 | + | |
| 256 | + /* | |
| 257 | + * VCI is the analog voltage supply | |
| 258 | + * VCCIO is the digital I/O voltage supply | |
| 259 | + */ | |
| 260 | + ctx->regulators[0].supply = "vci"; | |
| 261 | + ctx->regulators[1].supply = "vccio"; | |
| 262 | + ret = devm_regulator_bulk_get(dev, | |
| 263 | + ARRAY_SIZE(ctx->regulators), | |
| 264 | + ctx->regulators); | |
| 265 | + if (ret) | |
| 266 | + return dev_err_probe(dev, ret, "failed to get regulators\n"); | |
| 267 | + | |
| 268 | + ctx->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); | |
| 269 | + if (IS_ERR(ctx->reset)) { | |
| 270 | + ret = PTR_ERR(ctx->reset); | |
| 271 | + return dev_err_probe(dev, ret, "no RESET GPIO\n"); | |
| 272 | + } | |
| 273 | + | |
| 274 | + ret = mipi_dbi_spi_init(spi, &ctx->dbi, NULL); | |
| 275 | + if (ret) | |
| 276 | + return dev_err_probe(dev, ret, "MIPI DBI init failed\n"); | |
| 277 | + | |
| 278 | + ctx->dbi.read_commands = s6d27a1_dbi_read_commands; | |
| 279 | + | |
| 280 | + drm_panel_init(&ctx->panel, dev, &s6d27a1_drm_funcs, | |
| 281 | + DRM_MODE_CONNECTOR_DPI); | |
| 282 | + | |
| 283 | + ret = drm_panel_of_backlight(&ctx->panel); | |
| 284 | + if (ret) | |
| 285 | + return dev_err_probe(dev, ret, "failed to add backlight\n"); | |
| 286 | + | |
| 287 | + spi_set_drvdata(spi, ctx); | |
| 288 | + | |
| 289 | + drm_panel_add(&ctx->panel); | |
| 290 | + | |
| 291 | + return 0; | |
| 292 | +} | |
| 293 | + | |
| 294 | +static int s6d27a1_remove(struct spi_device *spi) | |
| 295 | +{ | |
| 296 | + struct s6d27a1 *ctx = spi_get_drvdata(spi); | |
| 297 | + | |
| 298 | + drm_panel_remove(&ctx->panel); | |
| 299 | + return 0; | |
| 300 | +} | |
| 301 | + | |
| 302 | +static const struct of_device_id s6d27a1_match[] = { | |
| 303 | + { .compatible = "samsung,s6d27a1", }, | |
| 304 | + { /* sentinel */ }, | |
| 305 | +}; | |
| 306 | +MODULE_DEVICE_TABLE(of, s6d27a1_match); | |
| 307 | + | |
| 308 | +static struct spi_driver s6d27a1_driver = { | |
| 309 | + .probe = s6d27a1_probe, | |
| 310 | + .remove = s6d27a1_remove, | |
| 311 | + .driver = { | |
| 312 | + .name = "s6d27a1-panel", | |
| 313 | + .of_match_table = s6d27a1_match, | |
| 314 | + }, | |
| 315 | +}; | |
| 316 | +module_spi_driver(s6d27a1_driver); | |
| 317 | + | |
| 318 | +MODULE_AUTHOR("Markuss Broks <markuss.broks@gmail.com>"); | |
| 319 | +MODULE_DESCRIPTION("Samsung S6D27A1 panel driver"); | |
| 320 | +MODULE_LICENSE("GPL v2"); | |
| @@ -3158,19 +3158,6 @@ static const struct panel_desc logictechno_lttd800480070_l6wh_rt = { · 3157 unmodified lines | ||
| 3158 | 3158 | .connector_type = DRM_MODE_CONNECTOR_DPI, |
| 3159 | 3159 | }; |
| 3160 | 3160 | |
| 3161 | −static const struct drm_display_mode mitsubishi_aa070mc01_mode = { | |
| 3162 | − .clock = 30400, | |
| 3163 | − .hdisplay = 800, | |
| 3164 | − .hsync_start = 800 + 0, | |
| 3165 | − .hsync_end = 800 + 1, | |
| 3166 | − .htotal = 800 + 0 + 1 + 160, | |
| 3167 | − .vdisplay = 480, | |
| 3168 | − .vsync_start = 480 + 0, | |
| 3169 | − .vsync_end = 480 + 48 + 1, | |
| 3170 | − .vtotal = 480 + 48 + 1 + 0, | |
| 3171 | − .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, | |
| 3172 | −}; | |
| 3173 | − | |
| 3174 | 3161 | static const struct drm_display_mode logicpd_type_28_mode = { |
| 3175 | 3162 | .clock = 9107, |
| 3176 | 3163 | .hdisplay = 480, |
| @@ -3205,6 +3192,19 @@ static const struct panel_desc logicpd_type_28 = { · 28 unmodified lines | ||
| 3205 | 3192 | .connector_type = DRM_MODE_CONNECTOR_DPI, |
| 3206 | 3193 | }; |
| 3207 | 3194 | |
| 3195 | +static const struct drm_display_mode mitsubishi_aa070mc01_mode = { | |
| 3196 | + .clock = 30400, | |
| 3197 | + .hdisplay = 800, | |
| 3198 | + .hsync_start = 800 + 0, | |
| 3199 | + .hsync_end = 800 + 1, | |
| 3200 | + .htotal = 800 + 0 + 1 + 160, | |
| 3201 | + .vdisplay = 480, | |
| 3202 | + .vsync_start = 480 + 0, | |
| 3203 | + .vsync_end = 480 + 48 + 1, | |
| 3204 | + .vtotal = 480 + 48 + 1 + 0, | |
| 3205 | + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, | |
| 3206 | +}; | |
| 3207 | + | |
| 3208 | 3208 | static const struct panel_desc mitsubishi_aa070mc01 = { |
| 3209 | 3209 | .modes = &mitsubishi_aa070mc01_mode, |
| 3210 | 3210 | .num_modes = 1, |
| @@ -198,7 +198,6 @@ err: · 197 unmodified lines | ||
| 198 | 198 | int panfrost_device_init(struct panfrost_device *pfdev) |
| 199 | 199 | { |
| 200 | 200 | int err; |
| 201 | − struct resource *res; | |
| 202 | 201 | |
| 203 | 202 | mutex_init(&pfdev->sched_lock); |
| 204 | 203 | INIT_LIST_HEAD(&pfdev->scheduled_jobs); |
| @@ -236,8 +235,7 @@ int panfrost_device_init(struct panfrost_device *pfdev) · 31 unmodified lines | ||
| 236 | 235 | if (err) |
| 237 | 236 | goto out_reset; |
| 238 | 237 | |
| 239 | − res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0); | |
| 240 | − pfdev->iomem = devm_ioremap_resource(pfdev->dev, res); | |
| 238 | + pfdev->iomem = devm_platform_ioremap_resource(pfdev->pdev, 0); | |
| 241 | 239 | if (IS_ERR(pfdev->iomem)) { |
| 242 | 240 | err = PTR_ERR(pfdev->iomem); |
| 243 | 241 | goto out_pm_domain; |
| @@ -218,7 +218,7 @@ panfrost_copy_in_sync(struct drm_device *dev, · 217 unmodified lines | ||
| 218 | 218 | if (ret) |
| 219 | 219 | goto fail; |
| 220 | 220 | |
| 221 | − ret = drm_gem_fence_array_add(&job->deps, fence); | |
| 221 | + ret = drm_sched_job_add_dependency(&job->base, fence); | |
| 222 | 222 | |
| 223 | 223 | if (ret) |
| 224 | 224 | goto fail; |
| @@ -236,7 +236,7 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, · 11 unmodified lines | ||
| 236 | 236 | struct drm_panfrost_submit *args = data; |
| 237 | 237 | struct drm_syncobj *sync_out = NULL; |
| 238 | 238 | struct panfrost_job *job; |
| 239 | − int ret = 0; | |
| 239 | + int ret = 0, slot; | |
| 240 | 240 | |
| 241 | 241 | if (!args->jc) |
| 242 | 242 | return -EINVAL; |
| @@ -253,38 +253,47 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, · 10 unmodified lines | ||
| 253 | 253 | job = kzalloc(sizeof(*job), GFP_KERNEL); |
| 254 | 254 | if (!job) { |
| 255 | 255 | ret = -ENOMEM; |
| 256 | − goto fail_out_sync; | |
| 256 | + goto out_put_syncout; | |
| 257 | 257 | } |
| 258 | 258 | |
| 259 | 259 | kref_init(&job->refcount); |
| 260 | 260 | |
| 261 | − xa_init_flags(&job->deps, XA_FLAGS_ALLOC); | |
| 262 | − | |
| 263 | 261 | job->pfdev = pfdev; |
| 264 | 262 | job->jc = args->jc; |
| 265 | 263 | job->requirements = args->requirements; |
| 266 | 264 | job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev); |
| 267 | 265 | job->file_priv = file->driver_priv; |
| 268 | 266 | |
| 267 | + slot = panfrost_job_get_slot(job); | |
| 268 | + | |
| 269 | + ret = drm_sched_job_init(&job->base, | |
| 270 | + &job->file_priv->sched_entity[slot], | |
| 271 | + NULL); | |
| 272 | + if (ret) | |
| 273 | + goto out_put_job; | |
| 274 | + | |
| 269 | 275 | ret = panfrost_copy_in_sync(dev, file, args, job); |
| 270 | 276 | if (ret) |
| 271 | − goto fail_job; | |
| 277 | + goto out_cleanup_job; | |
| 272 | 278 | |
| 273 | 279 | ret = panfrost_lookup_bos(dev, file, args, job); |
| 274 | 280 | if (ret) |
| 275 | − goto fail_job; | |
| 281 | + goto out_cleanup_job; | |
| 276 | 282 | |
| 277 | 283 | ret = panfrost_job_push(job); |
| 278 | 284 | if (ret) |
| 279 | − goto fail_job; | |
| 285 | + goto out_cleanup_job; | |
| 280 | 286 | |
| 281 | 287 | /* Update the return sync object for the job */ |
| 282 | 288 | if (sync_out) |
| 283 | 289 | drm_syncobj_replace_fence(sync_out, job->render_done_fence); |
| 284 | 290 | |
| 285 | −fail_job: | |
| 291 | +out_cleanup_job: | |
| 292 | + if (ret) | |
| 293 | + drm_sched_job_cleanup(&job->base); | |
| 294 | +out_put_job: | |
| 286 | 295 | panfrost_job_put(job); |
| 287 | −fail_out_sync: | |
| 296 | +out_put_syncout: | |
| 288 | 297 | if (sync_out) |
| 289 | 298 | drm_syncobj_put(sync_out); |
| 290 | 299 | |
| @@ -102,7 +102,7 @@ static struct dma_fence *panfrost_fence_create(struct panfrost_device *pfdev, in · 101 unmodified lines | ||
| 102 | 102 | return &fence->base; |
| 103 | 103 | } |
| 104 | 104 | |
| 105 | −static int panfrost_job_get_slot(struct panfrost_job *job) | |
| 105 | +int panfrost_job_get_slot(struct panfrost_job *job) | |
| 106 | 106 | { |
| 107 | 107 | /* JS0: fragment jobs. |
| 108 | 108 | * JS1: vertex/tiler jobs |
| @@ -137,8 +137,8 @@ static void panfrost_job_write_affinity(struct panfrost_device *pfdev, · 28 unmodified lines | ||
| 137 | 137 | */ |
| 138 | 138 | affinity = pfdev->features.shader_present; |
| 139 | 139 | |
| 140 | − job_write(pfdev, JS_AFFINITY_NEXT_LO(js), affinity & 0xFFFFFFFF); | |
| 141 | − job_write(pfdev, JS_AFFINITY_NEXT_HI(js), affinity >> 32); | |
| 140 | + job_write(pfdev, JS_AFFINITY_NEXT_LO(js), lower_32_bits(affinity)); | |
| 141 | + job_write(pfdev, JS_AFFINITY_NEXT_HI(js), upper_32_bits(affinity)); | |
| 142 | 142 | } |
| 143 | 143 | |
| 144 | 144 | static u32 |
| @@ -203,8 +203,8 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js) · 58 unmodified lines | ||
| 203 | 203 | |
| 204 | 204 | cfg = panfrost_mmu_as_get(pfdev, job->file_priv->mmu); |
| 205 | 205 | |
| 206 | − job_write(pfdev, JS_HEAD_NEXT_LO(js), jc_head & 0xFFFFFFFF); | |
| 207 | − job_write(pfdev, JS_HEAD_NEXT_HI(js), jc_head >> 32); | |
| 206 | + job_write(pfdev, JS_HEAD_NEXT_LO(js), lower_32_bits(jc_head)); | |
| 207 | + job_write(pfdev, JS_HEAD_NEXT_HI(js), upper_32_bits(jc_head)); | |
| 208 | 208 | |
| 209 | 209 | panfrost_job_write_affinity(pfdev, job->requirements, js); |
| 210 | 210 | |
| @@ -242,13 +242,14 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js) · 31 unmodified lines | ||
| 242 | 242 | |
| 243 | 243 | static int panfrost_acquire_object_fences(struct drm_gem_object **bos, |
| 244 | 244 | int bo_count, |
| 245 | − struct xarray *deps) | |
| 245 | + struct drm_sched_job *job) | |
| 246 | 246 | { |
| 247 | 247 | int i, ret; |
| 248 | 248 | |
| 249 | 249 | for (i = 0; i < bo_count; i++) { |
| 250 | 250 | /* panfrost always uses write mode in its current uapi */ |
| 251 | − ret = drm_gem_fence_array_add_implicit(deps, bos[i], true); | |
| 251 | + ret = drm_sched_job_add_implicit_dependencies(job, bos[i], | |
| 252 | + true); | |
| 252 | 253 | if (ret) |
| 253 | 254 | return ret; |
| 254 | 255 | } |
| @@ -269,29 +270,21 @@ static void panfrost_attach_object_fences(struct drm_gem_object **bos, · 14 unmodified lines | ||
| 269 | 270 | int panfrost_job_push(struct panfrost_job *job) |
| 270 | 271 | { |
| 271 | 272 | struct panfrost_device *pfdev = job->pfdev; |
| 272 | − int slot = panfrost_job_get_slot(job); | |
| 273 | − struct drm_sched_entity *entity = &job->file_priv->sched_entity[slot]; | |
| 274 | 273 | struct ww_acquire_ctx acquire_ctx; |
| 275 | 274 | int ret = 0; |
| 276 | 275 | |
| 277 | − | |
| 278 | 276 | ret = drm_gem_lock_reservations(job->bos, job->bo_count, |
| 279 | 277 | &acquire_ctx); |
| 280 | 278 | if (ret) |
| 281 | 279 | return ret; |
| 282 | 280 | |
| 283 | 281 | mutex_lock(&pfdev->sched_lock); |
| 284 | − | |
| 285 | − ret = drm_sched_job_init(&job->base, entity, NULL); | |
| 286 | − if (ret) { | |
| 287 | − mutex_unlock(&pfdev->sched_lock); | |
| 288 | − goto unlock; | |
| 289 | − } | |
| 282 | + drm_sched_job_arm(&job->base); | |
| 290 | 283 | |
| 291 | 284 | job->render_done_fence = dma_fence_get(&job->base.s_fence->finished); |
| 292 | 285 | |
| 293 | 286 | ret = panfrost_acquire_object_fences(job->bos, job->bo_count, |
| 294 | − &job->deps); | |
| 287 | + &job->base); | |
| 295 | 288 | if (ret) { |
| 296 | 289 | mutex_unlock(&pfdev->sched_lock); |
| 297 | 290 | goto unlock; |
| @@ -299,7 +292,7 @@ int panfrost_job_push(struct panfrost_job *job) · 1 unmodified line | ||
| 299 | 292 | |
| 300 | 293 | kref_get(&job->refcount); /* put by scheduler job completion */ |
| 301 | 294 | |
| 302 | − drm_sched_entity_push_job(&job->base, entity); | |
| 295 | + drm_sched_entity_push_job(&job->base); | |
| 303 | 296 | |
| 304 | 297 | mutex_unlock(&pfdev->sched_lock); |
| 305 | 298 | |
| @@ -316,15 +309,8 @@ static void panfrost_job_cleanup(struct kref *ref) · 10 unmodified lines | ||
| 316 | 309 | { |
| 317 | 310 | struct panfrost_job *job = container_of(ref, struct panfrost_job, |
| 318 | 311 | refcount); |
| 319 | − struct dma_fence *fence; | |
| 320 | − unsigned long index; | |
| 321 | 312 | unsigned int i; |
| 322 | 313 | |
| 323 | − xa_for_each(&job->deps, index, fence) { | |
| 324 | − dma_fence_put(fence); | |
| 325 | − } | |
| 326 | − xa_destroy(&job->deps); | |
| 327 | − | |
| 328 | 314 | dma_fence_put(job->done_fence); |
| 329 | 315 | dma_fence_put(job->render_done_fence); |
| 330 | 316 | |
| @@ -363,17 +349,6 @@ static void panfrost_job_free(struct drm_sched_job *sched_job) · 32 unmodified lines | ||
| 363 | 349 | panfrost_job_put(job); |
| 364 | 350 | } |
| 365 | 351 | |
| 366 | −static struct dma_fence *panfrost_job_dependency(struct drm_sched_job *sched_job, | |
| 367 | − struct drm_sched_entity *s_entity) | |
| 368 | −{ | |
| 369 | − struct panfrost_job *job = to_panfrost_job(sched_job); | |
| 370 | − | |
| 371 | − if (!xa_empty(&job->deps)) | |
| 372 | − return xa_erase(&job->deps, job->last_dep++); | |
| 373 | − | |
| 374 | − return NULL; | |
| 375 | −} | |
| 376 | − | |
| 377 | 352 | static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job) |
| 378 | 353 | { |
| 379 | 354 | struct panfrost_job *job = to_panfrost_job(sched_job); |
| @@ -763,7 +738,6 @@ static void panfrost_reset_work(struct work_struct *work) · 383 unmodified lines | ||
| 763 | 738 | } |
| 764 | 739 | |
| 765 | 740 | static const struct drm_sched_backend_ops panfrost_sched_ops = { |
| 766 | − .dependency = panfrost_job_dependency, | |
| 767 | 741 | .run_job = panfrost_job_run, |
| 768 | 742 | .timedout_job = panfrost_job_timedout, |
| 769 | 743 | .free_job = panfrost_job_free |
| @@ -19,10 +19,6 @@ struct panfrost_job { · 18 unmodified lines | ||
| 19 | 19 | struct panfrost_device *pfdev; |
| 20 | 20 | struct panfrost_file_priv *file_priv; |
| 21 | 21 | |
| 22 | − /* Contains both explicit and implicit fences */ | |
| 23 | − struct xarray deps; | |
| 24 | − unsigned long last_dep; | |
| 25 | − | |
| 26 | 22 | /* Fence to be signaled by IRQ handler when the job is complete. */ |
| 27 | 23 | struct dma_fence *done_fence; |
| 28 | 24 | |
| @@ -42,6 +38,7 @@ int panfrost_job_init(struct panfrost_device *pfdev); · 13 unmodified lines | ||
| 42 | 38 | void panfrost_job_fini(struct panfrost_device *pfdev); |
| 43 | 39 | int panfrost_job_open(struct panfrost_file_priv *panfrost_priv); |
| 44 | 40 | void panfrost_job_close(struct panfrost_file_priv *panfrost_priv); |
| 41 | +int panfrost_job_get_slot(struct panfrost_job *job); | |
| 45 | 42 | int panfrost_job_push(struct panfrost_job *job); |
| 46 | 43 | void panfrost_job_put(struct panfrost_job *job); |
| 47 | 44 | void panfrost_job_enable_interrupts(struct panfrost_device *pfdev); |
| @@ -71,8 +71,8 @@ static void lock_region(struct panfrost_device *pfdev, u32 as_nr, · 70 unmodified lines | ||
| 71 | 71 | region |= region_width; |
| 72 | 72 | |
| 73 | 73 | /* Lock the region that needs to be updated */ |
| 74 | − mmu_write(pfdev, AS_LOCKADDR_LO(as_nr), region & 0xFFFFFFFFUL); | |
| 75 | − mmu_write(pfdev, AS_LOCKADDR_HI(as_nr), (region >> 32) & 0xFFFFFFFFUL); | |
| 74 | + mmu_write(pfdev, AS_LOCKADDR_LO(as_nr), lower_32_bits(region)); | |
| 75 | + mmu_write(pfdev, AS_LOCKADDR_HI(as_nr), upper_32_bits(region)); | |
| 76 | 76 | write_cmd(pfdev, as_nr, AS_COMMAND_LOCK); |
| 77 | 77 | } |
| 78 | 78 | |
| @@ -114,14 +114,14 @@ static void panfrost_mmu_enable(struct panfrost_device *pfdev, struct panfrost_m · 35 unmodified lines | ||
| 114 | 114 | |
| 115 | 115 | mmu_hw_do_operation_locked(pfdev, as_nr, 0, ~0ULL, AS_COMMAND_FLUSH_MEM); |
| 116 | 116 | |
| 117 | − mmu_write(pfdev, AS_TRANSTAB_LO(as_nr), transtab & 0xffffffffUL); | |
| 118 | − mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), transtab >> 32); | |
| 117 | + mmu_write(pfdev, AS_TRANSTAB_LO(as_nr), lower_32_bits(transtab)); | |
| 118 | + mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), upper_32_bits(transtab)); | |
| 119 | 119 | |
| 120 | 120 | /* Need to revisit mem attrs. |
| 121 | 121 | * NC is the default, Mali driver is inner WT. |
| 122 | 122 | */ |
| 123 | − mmu_write(pfdev, AS_MEMATTR_LO(as_nr), memattr & 0xffffffffUL); | |
| 124 | − mmu_write(pfdev, AS_MEMATTR_HI(as_nr), memattr >> 32); | |
| 123 | + mmu_write(pfdev, AS_MEMATTR_LO(as_nr), lower_32_bits(memattr)); | |
| 124 | + mmu_write(pfdev, AS_MEMATTR_HI(as_nr), upper_32_bits(memattr)); | |
| 125 | 125 | |
| 126 | 126 | write_cmd(pfdev, as_nr, AS_COMMAND_UPDATE); |
| 127 | 127 | } |
| @@ -51,8 +51,8 @@ static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev) · 50 unmodified lines | ||
| 51 | 51 | |
| 52 | 52 | reinit_completion(&pfdev->perfcnt->dump_comp); |
| 53 | 53 | gpuva = pfdev->perfcnt->mapping->mmnode.start << PAGE_SHIFT; |
| 54 | − gpu_write(pfdev, GPU_PERFCNT_BASE_LO, gpuva); | |
| 55 | − gpu_write(pfdev, GPU_PERFCNT_BASE_HI, gpuva >> 32); | |
| 54 | + gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva)); | |
| 55 | + gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva)); | |
| 56 | 56 | gpu_write(pfdev, GPU_INT_CLEAR, |
| 57 | 57 | GPU_IRQ_CLEAN_CACHES_COMPLETED | |
| 58 | 58 | GPU_IRQ_PERFCNT_SAMPLE_COMPLETED); |
| @@ -36,10 +36,10 @@ · 35 unmodified lines | ||
| 36 | 36 | /* manage releaseables */ |
| 37 | 37 | /* stack them 16 high for now -drawable object is 191 */ |
| 38 | 38 | #define RELEASE_SIZE 256 |
| 39 | −#define RELEASES_PER_BO (4096 / RELEASE_SIZE) | |
| 39 | +#define RELEASES_PER_BO (PAGE_SIZE / RELEASE_SIZE) | |
| 40 | 40 | /* put an alloc/dealloc surface cmd into one bo and round up to 128 */ |
| 41 | 41 | #define SURFACE_RELEASE_SIZE 128 |
| 42 | −#define SURFACE_RELEASES_PER_BO (4096 / SURFACE_RELEASE_SIZE) | |
| 42 | +#define SURFACE_RELEASES_PER_BO (PAGE_SIZE / SURFACE_RELEASE_SIZE) | |
| 43 | 43 | |
| 44 | 44 | static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE }; |
| 45 | 45 | static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO }; |
| @@ -101,7 +101,6 @@ int qxl_ttm_io_mem_reserve(struct ttm_device *bdev, · 100 unmodified lines | ||
| 101 | 101 | */ |
| 102 | 102 | static void qxl_ttm_backend_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) |
| 103 | 103 | { |
| 104 | − ttm_tt_destroy_common(bdev, ttm); | |
| 105 | 104 | ttm_tt_fini(ttm); |
| 106 | 105 | kfree(ttm); |
| 107 | 106 | } |
| @@ -99,7 +99,8 @@ int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info · 98 unmodified lines | ||
| 99 | 99 | for (i = 0; i < pages; i++) { |
| 100 | 100 | if (!entry->busaddr[i]) |
| 101 | 101 | break; |
| 102 | − pci_unmap_page(pdev, entry->busaddr[i], PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | |
| 102 | + dma_unmap_page(&pdev->dev, entry->busaddr[i], | |
| 103 | + PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 103 | 104 | } |
| 104 | 105 | |
| 105 | 106 | if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) |
| @@ -134,7 +135,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga · 28 unmodified lines | ||
| 134 | 135 | if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { |
| 135 | 136 | DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); |
| 136 | 137 | |
| 137 | − if (pci_set_dma_mask(pdev, gart_info->table_mask)) { | |
| 138 | + if (dma_set_mask(&pdev->dev, gart_info->table_mask)) { | |
| 138 | 139 | DRM_ERROR("fail to set dma mask to 0x%Lx\n", |
| 139 | 140 | (unsigned long long)gart_info->table_mask); |
| 140 | 141 | ret = -EFAULT; |
| @@ -173,9 +174,9 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga · 32 unmodified lines | ||
| 173 | 174 | gart_idx = 0; |
| 174 | 175 | for (i = 0; i < pages; i++) { |
| 175 | 176 | /* we need to support large memory configurations */ |
| 176 | − entry->busaddr[i] = pci_map_page(pdev, entry->pagelist[i], | |
| 177 | − 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | |
| 178 | − if (pci_dma_mapping_error(pdev, entry->busaddr[i])) { | |
| 177 | + entry->busaddr[i] = dma_map_page(&pdev->dev, entry->pagelist[i], | |
| 178 | + 0, PAGE_SIZE, DMA_BIDIRECTIONAL); | |
| 179 | + if (dma_mapping_error(&pdev->dev, entry->busaddr[i])) { | |
| 179 | 180 | DRM_ERROR("unable to map PCIGART pages!\n"); |
| 180 | 181 | drm_ati_pcigart_cleanup(dev, gart_info); |
| 181 | 182 | address = NULL; |
| @@ -176,18 +176,11 @@ static int radeon_fence_check_signaled(wait_queue_entry_t *wait, unsigned mode, · 175 unmodified lines | ||
| 176 | 176 | */ |
| 177 | 177 | seq = atomic64_read(&fence->rdev->fence_drv[fence->ring].last_seq); |
| 178 | 178 | if (seq >= fence->seq) { |
| 179 | − int ret = dma_fence_signal_locked(&fence->base); | |
| 180 | − | |
| 181 | − if (!ret) | |
| 182 | − DMA_FENCE_TRACE(&fence->base, "signaled from irq context\n"); | |
| 183 | − else | |
| 184 | − DMA_FENCE_TRACE(&fence->base, "was already signaled\n"); | |
| 185 | − | |
| 179 | + dma_fence_signal_locked(&fence->base); | |
| 186 | 180 | radeon_irq_kms_sw_irq_put(fence->rdev, fence->ring); |
| 187 | 181 | __remove_wait_queue(&fence->rdev->fence_queue, &fence->fence_wake); |
| 188 | 182 | dma_fence_put(&fence->base); |
| 189 | − } else | |
| 190 | − DMA_FENCE_TRACE(&fence->base, "pending\n"); | |
| 183 | + } | |
| 191 | 184 | return 0; |
| 192 | 185 | } |
| 193 | 186 | |
| @@ -422,8 +415,6 @@ static bool radeon_fence_enable_signaling(struct dma_fence *f) · 228 unmodified lines | ||
| 422 | 415 | fence->fence_wake.func = radeon_fence_check_signaled; |
| 423 | 416 | __add_wait_queue(&rdev->fence_queue, &fence->fence_wake); |
| 424 | 417 | dma_fence_get(f); |
| 425 | − | |
| 426 | − DMA_FENCE_TRACE(&fence->base, "armed on ring %i!\n", fence->ring); | |
| 427 | 418 | return true; |
| 428 | 419 | } |
| 429 | 420 | |
| @@ -441,11 +432,7 @@ bool radeon_fence_signaled(struct radeon_fence *fence) · 11 unmodified lines | ||
| 441 | 432 | return true; |
| 442 | 433 | |
| 443 | 434 | if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) { |
| 444 | − int ret; | |
| 445 | − | |
| 446 | − ret = dma_fence_signal(&fence->base); | |
| 447 | − if (!ret) | |
| 448 | − DMA_FENCE_TRACE(&fence->base, "signaled from radeon_fence_signaled\n"); | |
| 435 | + dma_fence_signal(&fence->base); | |
| 449 | 436 | return true; |
| 450 | 437 | } |
| 451 | 438 | return false; |
| @@ -550,7 +537,6 @@ long radeon_fence_wait_timeout(struct radeon_fence *fence, bool intr, long timeo · 98 unmodified lines | ||
| 550 | 537 | { |
| 551 | 538 | uint64_t seq[RADEON_NUM_RINGS] = {}; |
| 552 | 539 | long r; |
| 553 | − int r_sig; | |
| 554 | 540 | |
| 555 | 541 | /* |
| 556 | 542 | * This function should not be called on !radeon fences. |
| @@ -567,9 +553,7 @@ long radeon_fence_wait_timeout(struct radeon_fence *fence, bool intr, long timeo · 10 unmodified lines | ||
| 567 | 553 | return r; |
| 568 | 554 | } |
| 569 | 555 | |
| 570 | − r_sig = dma_fence_signal(&fence->base); | |
| 571 | − if (!r_sig) | |
| 572 | − DMA_FENCE_TRACE(&fence->base, "signaled from fence_wait\n"); | |
| 556 | + dma_fence_signal(&fence->base); | |
| 573 | 557 | return r; |
| 574 | 558 | } |
| 575 | 559 | |
| @@ -488,9 +488,6 @@ static void radeon_ttm_backend_destroy(struct ttm_device *bdev, struct ttm_tt *t · 487 unmodified lines | ||
| 488 | 488 | { |
| 489 | 489 | struct radeon_ttm_tt *gtt = (void *)ttm; |
| 490 | 490 | |
| 491 | − radeon_ttm_backend_unbind(bdev, ttm); | |
| 492 | − ttm_tt_destroy_common(bdev, ttm); | |
| 493 | − | |
| 494 | 491 | ttm_tt_fini(>t->ttm); |
| 495 | 492 | kfree(gtt); |
| 496 | 493 | } |
| @@ -574,6 +571,8 @@ static void radeon_ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm · 77 unmodified lines | ||
| 574 | 571 | struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm); |
| 575 | 572 | bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); |
| 576 | 573 | |
| 574 | + radeon_ttm_tt_unbind(bdev, ttm); | |
| 575 | + | |
| 577 | 576 | if (gtt && gtt->userptr) { |
| 578 | 577 | kfree(ttm->sg); |
| 579 | 578 | ttm->page_flags &= ~TTM_PAGE_FLAG_SG; |
| @@ -651,8 +650,6 @@ static void radeon_ttm_tt_destroy(struct ttm_device *bdev, · 71 unmodified lines | ||
| 651 | 650 | struct radeon_device *rdev = radeon_get_rdev(bdev); |
| 652 | 651 | |
| 653 | 652 | if (rdev->flags & RADEON_IS_AGP) { |
| 654 | − ttm_agp_unbind(ttm); | |
| 655 | − ttm_tt_destroy_common(bdev, ttm); | |
| 656 | 653 | ttm_agp_destroy(ttm); |
| 657 | 654 | return; |
| 658 | 655 | } |
| @@ -9,7 +9,6 @@ config DRM_ROCKCHIP · 8 unmodified lines | ||
| 9 | 9 | select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP |
| 10 | 10 | select DRM_DW_HDMI if ROCKCHIP_DW_HDMI |
| 11 | 11 | select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI |
| 12 | − select DRM_RGB if ROCKCHIP_RGB | |
| 13 | 12 | select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI |
| 14 | 13 | select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI |
| 15 | 14 | select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC |
| @@ -45,8 +45,14 @@ · 44 unmodified lines | ||
| 45 | 45 | * @guilty: atomic_t set to 1 when a job on this queue |
| 46 | 46 | * is found to be guilty causing a timeout |
| 47 | 47 | * |
| 48 | − * Note: the sched_list should have at least one element to schedule | |
| 49 | − * the entity | |
| 48 | + * Note that the &sched_list must have at least one element to schedule the entity. | |
| 49 | + * | |
| 50 | + * For changing @priority later on at runtime see | |
| 51 | + * drm_sched_entity_set_priority(). For changing the set of schedulers | |
| 52 | + * @sched_list at runtime see drm_sched_entity_modify_sched(). | |
| 53 | + * | |
| 54 | + * An entity is cleaned up by callind drm_sched_entity_fini(). See also | |
| 55 | + * drm_sched_entity_destroy(). | |
| 50 | 56 | * |
| 51 | 57 | * Returns 0 on success or a negative error code on failure. |
| 52 | 58 | */ |
| @@ -92,6 +98,11 @@ EXPORT_SYMBOL(drm_sched_entity_init); · 39 unmodified lines | ||
| 92 | 98 | * @sched_list: the list of new drm scheds which will replace |
| 93 | 99 | * existing entity->sched_list |
| 94 | 100 | * @num_sched_list: number of drm sched in sched_list |
| 101 | + * | |
| 102 | + * Note that this must be called under the same common lock for @entity as | |
| 103 | + * drm_sched_job_arm() and drm_sched_entity_push_job(), or the driver needs to | |
| 104 | + * guarantee through some other means that this is never called while new jobs | |
| 105 | + * can be pushed to @entity. | |
| 95 | 106 | */ |
| 96 | 107 | void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, |
| 97 | 108 | struct drm_gpu_scheduler **sched_list, |
| @@ -104,13 +115,6 @@ void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, · 6 unmodified lines | ||
| 104 | 115 | } |
| 105 | 116 | EXPORT_SYMBOL(drm_sched_entity_modify_sched); |
| 106 | 117 | |
| 107 | −/** | |
| 108 | − * drm_sched_entity_is_idle - Check if entity is idle | |
| 109 | − * | |
| 110 | − * @entity: scheduler entity | |
| 111 | − * | |
| 112 | − * Returns true if the entity does not have any unscheduled jobs. | |
| 113 | − */ | |
| 114 | 118 | static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity) |
| 115 | 119 | { |
| 116 | 120 | rmb(); /* for list_empty to work without lock */ |
| @@ -123,13 +127,7 @@ static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity) · 6 unmodified lines | ||
| 123 | 127 | return false; |
| 124 | 128 | } |
| 125 | 129 | |
| 126 | −/** | |
| 127 | − * drm_sched_entity_is_ready - Check if entity is ready | |
| 128 | − * | |
| 129 | − * @entity: scheduler entity | |
| 130 | − * | |
| 131 | − * Return true if entity could provide a job. | |
| 132 | − */ | |
| 130 | +/* Return true if entity could provide a job. */ | |
| 133 | 131 | bool drm_sched_entity_is_ready(struct drm_sched_entity *entity) |
| 134 | 132 | { |
| 135 | 133 | if (spsc_queue_peek(&entity->job_queue) == NULL) |
| @@ -192,14 +190,7 @@ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout) · 56 unmodified lines | ||
| 192 | 190 | } |
| 193 | 191 | EXPORT_SYMBOL(drm_sched_entity_flush); |
| 194 | 192 | |
| 195 | −/** | |
| 196 | − * drm_sched_entity_kill_jobs_cb - helper for drm_sched_entity_kill_jobs | |
| 197 | − * | |
| 198 | − * @f: signaled fence | |
| 199 | − * @cb: our callback structure | |
| 200 | − * | |
| 201 | − * Signal the scheduler finished fence when the entity in question is killed. | |
| 202 | − */ | |
| 193 | +/* Signal the scheduler finished fence when the entity in question is killed. */ | |
| 203 | 194 | static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f, |
| 204 | 195 | struct dma_fence_cb *cb) |
| 205 | 196 | { |
| @@ -211,14 +202,19 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f, · 5 unmodified lines | ||
| 211 | 202 | job->sched->ops->free_job(job); |
| 212 | 203 | } |
| 213 | 204 | |
| 214 | −/** | |
| 215 | − * drm_sched_entity_kill_jobs - Make sure all remaining jobs are killed | |
| 216 | − * | |
| 217 | − * @entity: entity which is cleaned up | |
| 218 | − * | |
| 219 | − * Makes sure that all remaining jobs in an entity are killed before it is | |
| 220 | − * destroyed. | |
| 221 | − */ | |
| 205 | +static struct dma_fence * | |
| 206 | +drm_sched_job_dependency(struct drm_sched_job *job, | |
| 207 | + struct drm_sched_entity *entity) | |
| 208 | +{ | |
| 209 | + if (!xa_empty(&job->dependencies)) | |
| 210 | + return xa_erase(&job->dependencies, job->last_dependency++); | |
| 211 | + | |
| 212 | + if (job->sched->ops->dependency) | |
| 213 | + return job->sched->ops->dependency(job, entity); | |
| 214 | + | |
| 215 | + return NULL; | |
| 216 | +} | |
| 217 | + | |
| 222 | 218 | static void drm_sched_entity_kill_jobs(struct drm_sched_entity *entity) |
| 223 | 219 | { |
| 224 | 220 | struct drm_sched_job *job; |
| @@ -229,7 +225,7 @@ static void drm_sched_entity_kill_jobs(struct drm_sched_entity *entity) · 4 unmodified lines | ||
| 229 | 225 | struct drm_sched_fence *s_fence = job->s_fence; |
| 230 | 226 | |
| 231 | 227 | /* Wait for all dependencies to avoid data corruptions */ |
| 232 | − while ((f = job->sched->ops->dependency(job, entity))) | |
| 228 | + while ((f = drm_sched_job_dependency(job, entity))) | |
| 233 | 229 | dma_fence_wait(f, false); |
| 234 | 230 | |
| 235 | 231 | drm_sched_fence_scheduled(s_fence); |
| @@ -260,9 +256,11 @@ static void drm_sched_entity_kill_jobs(struct drm_sched_entity *entity) · 24 unmodified lines | ||
| 260 | 256 | * |
| 261 | 257 | * @entity: scheduler entity |
| 262 | 258 | * |
| 263 | − * This should be called after @drm_sched_entity_do_release. It goes over the | |
| 264 | − * entity and signals all jobs with an error code if the process was killed. | |
| 259 | + * Cleanups up @entity which has been initialized by drm_sched_entity_init(). | |
| 265 | 260 | * |
| 261 | + * If there are potentially job still in flight or getting newly queued | |
| 262 | + * drm_sched_entity_flush() must be called first. This function then goes over | |
| 263 | + * the entity and signals all jobs with an error code if the process was killed. | |
| 266 | 264 | */ |
| 267 | 265 | void drm_sched_entity_fini(struct drm_sched_entity *entity) |
| 268 | 266 | { |
| @@ -302,10 +300,10 @@ EXPORT_SYMBOL(drm_sched_entity_fini); · 33 unmodified lines | ||
| 302 | 300 | |
| 303 | 301 | /** |
| 304 | 302 | * drm_sched_entity_destroy - Destroy a context entity |
| 305 | − * | |
| 306 | 303 | * @entity: scheduler entity |
| 307 | 304 | * |
| 308 | − * Calls drm_sched_entity_do_release() and drm_sched_entity_cleanup() | |
| 305 | + * Calls drm_sched_entity_flush() and drm_sched_entity_fini() as a | |
| 306 | + * convenience wrapper. | |
| 309 | 307 | */ |
| 310 | 308 | void drm_sched_entity_destroy(struct drm_sched_entity *entity) |
| 311 | 309 | { |
| @@ -314,9 +312,7 @@ void drm_sched_entity_destroy(struct drm_sched_entity *entity) · 2 unmodified lines | ||
| 314 | 312 | } |
| 315 | 313 | EXPORT_SYMBOL(drm_sched_entity_destroy); |
| 316 | 314 | |
| 317 | −/* | |
| 318 | − * drm_sched_entity_clear_dep - callback to clear the entities dependency | |
| 319 | − */ | |
| 315 | +/* drm_sched_entity_clear_dep - callback to clear the entities dependency */ | |
| 320 | 316 | static void drm_sched_entity_clear_dep(struct dma_fence *f, |
| 321 | 317 | struct dma_fence_cb *cb) |
| 322 | 318 | { |
| @@ -358,11 +354,7 @@ void drm_sched_entity_set_priority(struct drm_sched_entity *entity, · 35 unmodified lines | ||
| 358 | 354 | } |
| 359 | 355 | EXPORT_SYMBOL(drm_sched_entity_set_priority); |
| 360 | 356 | |
| 361 | −/** | |
| 362 | − * drm_sched_entity_add_dependency_cb - add callback for the entities dependency | |
| 363 | − * | |
| 364 | − * @entity: entity with dependency | |
| 365 | − * | |
| 357 | +/* | |
| 366 | 358 | * Add a callback to the current dependency of the entity to wake up the |
| 367 | 359 | * scheduler when the entity becomes available. |
| 368 | 360 | */ |
| @@ -410,16 +402,8 @@ static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity) · 41 unmodified lines | ||
| 410 | 402 | return false; |
| 411 | 403 | } |
| 412 | 404 | |
| 413 | −/** | |
| 414 | − * drm_sched_entity_pop_job - get a ready to be scheduled job from the entity | |
| 415 | − * | |
| 416 | − * @entity: entity to get the job from | |
| 417 | − * | |
| 418 | − * Process all dependencies and try to get one job from the entities queue. | |
| 419 | − */ | |
| 420 | 405 | struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) |
| 421 | 406 | { |
| 422 | − struct drm_gpu_scheduler *sched = entity->rq->sched; | |
| 423 | 407 | struct drm_sched_job *sched_job; |
| 424 | 408 | |
| 425 | 409 | sched_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); |
| @@ -427,7 +411,7 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) · 1 unmodified line | ||
| 427 | 411 | return NULL; |
| 428 | 412 | |
| 429 | 413 | while ((entity->dependency = |
| 430 | − sched->ops->dependency(sched_job, entity))) { | |
| 414 | + drm_sched_job_dependency(sched_job, entity))) { | |
| 431 | 415 | trace_drm_sched_job_wait_dep(sched_job, entity->dependency); |
| 432 | 416 | |
| 433 | 417 | if (drm_sched_entity_add_dependency_cb(entity)) |
| @@ -439,30 +423,45 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) · 5 unmodified lines | ||
| 439 | 423 | dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED); |
| 440 | 424 | |
| 441 | 425 | dma_fence_put(entity->last_scheduled); |
| 426 | + | |
| 442 | 427 | entity->last_scheduled = dma_fence_get(&sched_job->s_fence->finished); |
| 443 | 428 | |
| 429 | + /* | |
| 430 | + * If the queue is empty we allow drm_sched_entity_select_rq() to | |
| 431 | + * locklessly access ->last_scheduled. This only works if we set the | |
| 432 | + * pointer before we dequeue and if we a write barrier here. | |
| 433 | + */ | |
| 434 | + smp_wmb(); | |
| 435 | + | |
| 444 | 436 | spsc_queue_pop(&entity->job_queue); |
| 445 | 437 | return sched_job; |
| 446 | 438 | } |
| 447 | 439 | |
| 448 | −/** | |
| 449 | − * drm_sched_entity_select_rq - select a new rq for the entity | |
| 450 | − * | |
| 451 | − * @entity: scheduler entity | |
| 452 | − * | |
| 453 | − * Check all prerequisites and select a new rq for the entity for load | |
| 454 | − * balancing. | |
| 455 | − */ | |
| 456 | 440 | void drm_sched_entity_select_rq(struct drm_sched_entity *entity) |
| 457 | 441 | { |
| 458 | 442 | struct dma_fence *fence; |
| 459 | 443 | struct drm_gpu_scheduler *sched; |
| 460 | 444 | struct drm_sched_rq *rq; |
| 461 | 445 | |
| 462 | − if (spsc_queue_count(&entity->job_queue) || !entity->sched_list) | |
| 446 | + /* single possible engine and already selected */ | |
| 447 | + if (!entity->sched_list) | |
| 463 | 448 | return; |
| 464 | 449 | |
| 465 | − fence = READ_ONCE(entity->last_scheduled); | |
| 450 | + /* queue non-empty, stay on the same engine */ | |
| 451 | + if (spsc_queue_count(&entity->job_queue)) | |
| 452 | + return; | |
| 453 | + | |
| 454 | + /* | |
| 455 | + * Only when the queue is empty are we guaranteed that the scheduler | |
| 456 | + * thread cannot change ->last_scheduled. To enforce ordering we need | |
| 457 | + * a read barrier here. See drm_sched_entity_pop_job() for the other | |
| 458 | + * side. | |
| 459 | + */ | |
| 460 | + smp_rmb(); | |
| 461 | + | |
| 462 | + fence = entity->last_scheduled; | |
| 463 | + | |
| 464 | + /* stay on the same engine if the previous job hasn't finished */ | |
| 466 | 465 | if (fence && !dma_fence_is_signaled(fence)) |
| 467 | 466 | return; |
| 468 | 467 | |
| @@ -481,19 +480,18 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity) · 12 unmodified lines | ||
| 481 | 480 | |
| 482 | 481 | /** |
| 483 | 482 | * drm_sched_entity_push_job - Submit a job to the entity's job queue |
| 484 | − * | |
| 485 | 483 | * @sched_job: job to submit |
| 486 | − * @entity: scheduler entity | |
| 487 | 484 | * |
| 488 | − * Note: To guarantee that the order of insertion to queue matches | |
| 489 | − * the job's fence sequence number this function should be | |
| 490 | − * called with drm_sched_job_init under common lock. | |
| 485 | + * Note: To guarantee that the order of insertion to queue matches the job's | |
| 486 | + * fence sequence number this function should be called with drm_sched_job_arm() | |
| 487 | + * under common lock for the struct drm_sched_entity that was set up for | |
| 488 | + * @sched_job in drm_sched_job_init(). | |
| 491 | 489 | * |
| 492 | 490 | * Returns 0 for success, negative error code otherwise. |
| 493 | 491 | */ |
| 494 | −void drm_sched_entity_push_job(struct drm_sched_job *sched_job, | |
| 495 | − struct drm_sched_entity *entity) | |
| 492 | +void drm_sched_entity_push_job(struct drm_sched_job *sched_job) | |
| 496 | 493 | { |
| 494 | + struct drm_sched_entity *entity = sched_job->entity; | |
| 497 | 495 | bool first; |
| 498 | 496 | |
| 499 | 497 | trace_drm_sched_job(sched_job, entity); |
| @@ -50,26 +50,12 @@ static void __exit drm_sched_fence_slab_fini(void) · 49 unmodified lines | ||
| 50 | 50 | |
| 51 | 51 | void drm_sched_fence_scheduled(struct drm_sched_fence *fence) |
| 52 | 52 | { |
| 53 | − int ret = dma_fence_signal(&fence->scheduled); | |
| 54 | − | |
| 55 | − if (!ret) | |
| 56 | − DMA_FENCE_TRACE(&fence->scheduled, | |
| 57 | − "signaled from irq context\n"); | |
| 58 | − else | |
| 59 | − DMA_FENCE_TRACE(&fence->scheduled, | |
| 60 | − "was already signaled\n"); | |
| 53 | + dma_fence_signal(&fence->scheduled); | |
| 61 | 54 | } |
| 62 | 55 | |
| 63 | 56 | void drm_sched_fence_finished(struct drm_sched_fence *fence) |
| 64 | 57 | { |
| 65 | − int ret = dma_fence_signal(&fence->finished); | |
| 66 | − | |
| 67 | − if (!ret) | |
| 68 | − DMA_FENCE_TRACE(&fence->finished, | |
| 69 | − "signaled from irq context\n"); | |
| 70 | − else | |
| 71 | − DMA_FENCE_TRACE(&fence->finished, | |
| 72 | − "was already signaled\n"); | |
| 58 | + dma_fence_signal(&fence->finished); | |
| 73 | 59 | } |
| 74 | 60 | |
| 75 | 61 | static const char *drm_sched_fence_get_driver_name(struct dma_fence *fence) |
| @@ -83,19 +69,28 @@ static const char *drm_sched_fence_get_timeline_name(struct dma_fence *f) · 7 unmodified lines | ||
| 83 | 69 | return (const char *)fence->sched->name; |
| 84 | 70 | } |
| 85 | 71 | |
| 72 | +static void drm_sched_fence_free_rcu(struct rcu_head *rcu) | |
| 73 | +{ | |
| 74 | + struct dma_fence *f = container_of(rcu, struct dma_fence, rcu); | |
| 75 | + struct drm_sched_fence *fence = to_drm_sched_fence(f); | |
| 76 | + | |
| 77 | + if (!WARN_ON_ONCE(!fence)) | |
| 78 | + kmem_cache_free(sched_fence_slab, fence); | |
| 79 | +} | |
| 80 | + | |
| 86 | 81 | /** |
| 87 | − * drm_sched_fence_free - free up the fence memory | |
| 82 | + * drm_sched_fence_free - free up an uninitialized fence | |
| 88 | 83 | * |
| 89 | − * @rcu: RCU callback head | |
| 84 | + * @fence: fence to free | |
| 90 | 85 | * |
| 91 | − * Free up the fence memory after the RCU grace period. | |
| 86 | + * Free up the fence memory. Should only be used if drm_sched_fence_init() | |
| 87 | + * has not been called yet. | |
| 92 | 88 | */ |
| 93 | −static void drm_sched_fence_free(struct rcu_head *rcu) | |
| 89 | +void drm_sched_fence_free(struct drm_sched_fence *fence) | |
| 94 | 90 | { |
| 95 | − struct dma_fence *f = container_of(rcu, struct dma_fence, rcu); | |
| 96 | − struct drm_sched_fence *fence = to_drm_sched_fence(f); | |
| 97 | − | |
| 98 | − kmem_cache_free(sched_fence_slab, fence); | |
| 91 | + /* This function should not be called if the fence has been initialized. */ | |
| 92 | + if (!WARN_ON_ONCE(fence->sched)) | |
| 93 | + kmem_cache_free(sched_fence_slab, fence); | |
| 99 | 94 | } |
| 100 | 95 | |
| 101 | 96 | /** |
| @@ -111,7 +106,7 @@ static void drm_sched_fence_release_scheduled(struct dma_fence *f) · 9 unmodified lines | ||
| 111 | 106 | struct drm_sched_fence *fence = to_drm_sched_fence(f); |
| 112 | 107 | |
| 113 | 108 | dma_fence_put(fence->parent); |
| 114 | − call_rcu(&fence->finished.rcu, drm_sched_fence_free); | |
| 109 | + call_rcu(&fence->finished.rcu, drm_sched_fence_free_rcu); | |
| 115 | 110 | } |
| 116 | 111 | |
| 117 | 112 | /** |
| @@ -152,27 +147,32 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f) · 34 unmodified lines | ||
| 152 | 147 | } |
| 153 | 148 | EXPORT_SYMBOL(to_drm_sched_fence); |
| 154 | 149 | |
| 155 | −struct drm_sched_fence *drm_sched_fence_create(struct drm_sched_entity *entity, | |
| 156 | − void *owner) | |
| 150 | +struct drm_sched_fence *drm_sched_fence_alloc(struct drm_sched_entity *entity, | |
| 151 | + void *owner) | |
| 157 | 152 | { |
| 158 | 153 | struct drm_sched_fence *fence = NULL; |
| 159 | − unsigned seq; | |
| 160 | 154 | |
| 161 | 155 | fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL); |
| 162 | 156 | if (fence == NULL) |
| 163 | 157 | return NULL; |
| 164 | 158 | |
| 165 | 159 | fence->owner = owner; |
| 166 | − fence->sched = entity->rq->sched; | |
| 167 | 160 | spin_lock_init(&fence->lock); |
| 168 | 161 | |
| 162 | + return fence; | |
| 163 | +} | |
| 164 | + | |
| 165 | +void drm_sched_fence_init(struct drm_sched_fence *fence, | |
| 166 | + struct drm_sched_entity *entity) | |
| 167 | +{ | |
| 168 | + unsigned seq; | |
| 169 | + | |
| 170 | + fence->sched = entity->rq->sched; | |
| 169 | 171 | seq = atomic_inc_return(&entity->fence_seq); |
| 170 | 172 | dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled, |
| 171 | 173 | &fence->lock, entity->fence_context, seq); |
| 172 | 174 | dma_fence_init(&fence->finished, &drm_sched_fence_ops_finished, |
| 173 | 175 | &fence->lock, entity->fence_context + 1, seq); |
| 174 | − | |
| 175 | − return fence; | |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | 178 | module_init(drm_sched_fence_slab_init); |
| @@ -48,9 +48,11 @@ · 47 unmodified lines | ||
| 48 | 48 | #include <linux/wait.h> |
| 49 | 49 | #include <linux/sched.h> |
| 50 | 50 | #include <linux/completion.h> |
| 51 | +#include <linux/dma-resv.h> | |
| 51 | 52 | #include <uapi/linux/sched/types.h> |
| 52 | 53 | |
| 53 | 54 | #include <drm/drm_print.h> |
| 55 | +#include <drm/drm_gem.h> | |
| 54 | 56 | #include <drm/gpu_scheduler.h> |
| 55 | 57 | #include <drm/spsc_queue.h> |
| 56 | 58 | |
| @@ -564,7 +566,6 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs_ext); · 507 unmodified lines | ||
| 564 | 566 | |
| 565 | 567 | /** |
| 566 | 568 | * drm_sched_job_init - init a scheduler job |
| 567 | − * | |
| 568 | 569 | * @job: scheduler job to init |
| 569 | 570 | * @entity: scheduler entity to use |
| 570 | 571 | * @owner: job owner for debugging |
| @@ -572,43 +573,193 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs_ext); · 1 unmodified line | ||
| 572 | 573 | * Refer to drm_sched_entity_push_job() documentation |
| 573 | 574 | * for locking considerations. |
| 574 | 575 | * |
| 576 | + * Drivers must make sure drm_sched_job_cleanup() if this function returns | |
| 577 | + * successfully, even when @job is aborted before drm_sched_job_arm() is called. | |
| 578 | + * | |
| 579 | + * WARNING: amdgpu abuses &drm_sched.ready to signal when the hardware | |
| 580 | + * has died, which can mean that there's no valid runqueue for a @entity. | |
| 581 | + * This function returns -ENOENT in this case (which probably should be -EIO as | |
| 582 | + * a more meanigful return value). | |
| 583 | + * | |
| 575 | 584 | * Returns 0 for success, negative error code otherwise. |
| 576 | 585 | */ |
| 577 | 586 | int drm_sched_job_init(struct drm_sched_job *job, |
| 578 | 587 | struct drm_sched_entity *entity, |
| 579 | 588 | void *owner) |
| 580 | 589 | { |
| 581 | − struct drm_gpu_scheduler *sched; | |
| 582 | − | |
| 583 | 590 | drm_sched_entity_select_rq(entity); |
| 584 | 591 | if (!entity->rq) |
| 585 | 592 | return -ENOENT; |
| 586 | 593 | |
| 587 | − sched = entity->rq->sched; | |
| 588 | − | |
| 589 | − job->sched = sched; | |
| 590 | 594 | job->entity = entity; |
| 591 | − job->s_priority = entity->rq - sched->sched_rq; | |
| 592 | − job->s_fence = drm_sched_fence_create(entity, owner); | |
| 595 | + job->s_fence = drm_sched_fence_alloc(entity, owner); | |
| 593 | 596 | if (!job->s_fence) |
| 594 | 597 | return -ENOMEM; |
| 595 | − job->id = atomic64_inc_return(&sched->job_id_count); | |
| 596 | 598 | |
| 597 | 599 | INIT_LIST_HEAD(&job->list); |
| 598 | 600 | |
| 601 | + xa_init_flags(&job->dependencies, XA_FLAGS_ALLOC); | |
| 602 | + | |
| 599 | 603 | return 0; |
| 600 | 604 | } |
| 601 | 605 | EXPORT_SYMBOL(drm_sched_job_init); |
| 602 | 606 | |
| 603 | 607 | /** |
| 604 | − * drm_sched_job_cleanup - clean up scheduler job resources | |
| 608 | + * drm_sched_job_arm - arm a scheduler job for execution | |
| 609 | + * @job: scheduler job to arm | |
| 610 | + * | |
| 611 | + * This arms a scheduler job for execution. Specifically it initializes the | |
| 612 | + * &drm_sched_job.s_fence of @job, so that it can be attached to struct dma_resv | |
| 613 | + * or other places that need to track the completion of this job. | |
| 614 | + * | |
| 615 | + * Refer to drm_sched_entity_push_job() documentation for locking | |
| 616 | + * considerations. | |
| 617 | + * | |
| 618 | + * This can only be called if drm_sched_job_init() succeeded. | |
| 619 | + */ | |
| 620 | +void drm_sched_job_arm(struct drm_sched_job *job) | |
| 621 | +{ | |
| 622 | + struct drm_gpu_scheduler *sched; | |
| 623 | + struct drm_sched_entity *entity = job->entity; | |
| 624 | + | |
| 625 | + BUG_ON(!entity); | |
| 626 | + | |
| 627 | + sched = entity->rq->sched; | |
| 628 | + | |
| 629 | + job->sched = sched; | |
| 630 | + job->s_priority = entity->rq - sched->sched_rq; | |
| 631 | + job->id = atomic64_inc_return(&sched->job_id_count); | |
| 632 | + | |
| 633 | + drm_sched_fence_init(job->s_fence, job->entity); | |
| 634 | +} | |
| 635 | +EXPORT_SYMBOL(drm_sched_job_arm); | |
| 636 | + | |
| 637 | +/** | |
| 638 | + * drm_sched_job_add_dependency - adds the fence as a job dependency | |
| 639 | + * @job: scheduler job to add the dependencies to | |
| 640 | + * @fence: the dma_fence to add to the list of dependencies. | |
| 641 | + * | |
| 642 | + * Note that @fence is consumed in both the success and error cases. | |
| 643 | + * | |
| 644 | + * Returns: | |
| 645 | + * 0 on success, or an error on failing to expand the array. | |
| 646 | + */ | |
| 647 | +int drm_sched_job_add_dependency(struct drm_sched_job *job, | |
| 648 | + struct dma_fence *fence) | |
| 649 | +{ | |
| 650 | + struct dma_fence *entry; | |
| 651 | + unsigned long index; | |
| 652 | + u32 id = 0; | |
| 653 | + int ret; | |
| 654 | + | |
| 655 | + if (!fence) | |
| 656 | + return 0; | |
| 657 | + | |
| 658 | + /* Deduplicate if we already depend on a fence from the same context. | |
| 659 | + * This lets the size of the array of deps scale with the number of | |
| 660 | + * engines involved, rather than the number of BOs. | |
| 661 | + */ | |
| 662 | + xa_for_each(&job->dependencies, index, entry) { | |
| 663 | + if (entry->context != fence->context) | |
| 664 | + continue; | |
| 665 | + | |
| 666 | + if (dma_fence_is_later(fence, entry)) { | |
| 667 | + dma_fence_put(entry); | |
| 668 | + xa_store(&job->dependencies, index, fence, GFP_KERNEL); | |
| 669 | + } else { | |
| 670 | + dma_fence_put(fence); | |
| 671 | + } | |
| 672 | + return 0; | |
| 673 | + } | |
| 674 | + | |
| 675 | + ret = xa_alloc(&job->dependencies, &id, fence, xa_limit_32b, GFP_KERNEL); | |
| 676 | + if (ret != 0) | |
| 677 | + dma_fence_put(fence); | |
| 678 | + | |
| 679 | + return ret; | |
| 680 | +} | |
| 681 | +EXPORT_SYMBOL(drm_sched_job_add_dependency); | |
| 682 | + | |
| 683 | +/** | |
| 684 | + * drm_sched_job_add_implicit_dependencies - adds implicit dependencies as job | |
| 685 | + * dependencies | |
| 686 | + * @job: scheduler job to add the dependencies to | |
| 687 | + * @obj: the gem object to add new dependencies from. | |
| 688 | + * @write: whether the job might write the object (so we need to depend on | |
| 689 | + * shared fences in the reservation object). | |
| 605 | 690 | * |
| 691 | + * This should be called after drm_gem_lock_reservations() on your array of | |
| 692 | + * GEM objects used in the job but before updating the reservations with your | |
| 693 | + * own fences. | |
| 694 | + * | |
| 695 | + * Returns: | |
| 696 | + * 0 on success, or an error on failing to expand the array. | |
| 697 | + */ | |
| 698 | +int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, | |
| 699 | + struct drm_gem_object *obj, | |
| 700 | + bool write) | |
| 701 | +{ | |
| 702 | + int ret; | |
| 703 | + struct dma_fence **fences; | |
| 704 | + unsigned int i, fence_count; | |
| 705 | + | |
| 706 | + if (!write) { | |
| 707 | + struct dma_fence *fence = dma_resv_get_excl_unlocked(obj->resv); | |
| 708 | + | |
| 709 | + return drm_sched_job_add_dependency(job, fence); | |
| 710 | + } | |
| 711 | + | |
| 712 | + ret = dma_resv_get_fences(obj->resv, NULL, &fence_count, &fences); | |
| 713 | + if (ret || !fence_count) | |
| 714 | + return ret; | |
| 715 | + | |
| 716 | + for (i = 0; i < fence_count; i++) { | |
| 717 | + ret = drm_sched_job_add_dependency(job, fences[i]); | |
| 718 | + if (ret) | |
| 719 | + break; | |
| 720 | + } | |
| 721 | + | |
| 722 | + for (; i < fence_count; i++) | |
| 723 | + dma_fence_put(fences[i]); | |
| 724 | + kfree(fences); | |
| 725 | + return ret; | |
| 726 | +} | |
| 727 | +EXPORT_SYMBOL(drm_sched_job_add_implicit_dependencies); | |
| 728 | + | |
| 729 | + | |
| 730 | +/** | |
| 731 | + * drm_sched_job_cleanup - clean up scheduler job resources | |
| 606 | 732 | * @job: scheduler job to clean up |
| 733 | + * | |
| 734 | + * Cleans up the resources allocated with drm_sched_job_init(). | |
| 735 | + * | |
| 736 | + * Drivers should call this from their error unwind code if @job is aborted | |
| 737 | + * before drm_sched_job_arm() is called. | |
| 738 | + * | |
| 739 | + * After that point of no return @job is committed to be executed by the | |
| 740 | + * scheduler, and this function should be called from the | |
| 741 | + * &drm_sched_backend_ops.free_job callback. | |
| 607 | 742 | */ |
| 608 | 743 | void drm_sched_job_cleanup(struct drm_sched_job *job) |
| 609 | 744 | { |
| 610 | − dma_fence_put(&job->s_fence->finished); | |
| 745 | + struct dma_fence *fence; | |
| 746 | + unsigned long index; | |
| 747 | + | |
| 748 | + if (kref_read(&job->s_fence->finished.refcount)) { | |
| 749 | + /* drm_sched_job_arm() has been called */ | |
| 750 | + dma_fence_put(&job->s_fence->finished); | |
| 751 | + } else { | |
| 752 | + /* aborted job before committing to run it */ | |
| 753 | + drm_sched_fence_free(job->s_fence); | |
| 754 | + } | |
| 755 | + | |
| 611 | 756 | job->s_fence = NULL; |
| 757 | + | |
| 758 | + xa_for_each(&job->dependencies, index, fence) { | |
| 759 | + dma_fence_put(fence); | |
| 760 | + } | |
| 761 | + xa_destroy(&job->dependencies); | |
| 762 | + | |
| 612 | 763 | } |
| 613 | 764 | EXPORT_SYMBOL(drm_sched_job_cleanup); |
| 614 | 765 | |
| @@ -676,15 +827,6 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched) · 61 unmodified lines | ||
| 676 | 827 | { |
| 677 | 828 | struct drm_sched_job *job, *next; |
| 678 | 829 | |
| 679 | − /* | |
| 680 | − * Don't destroy jobs while the timeout worker is running OR thread | |
| 681 | − * is being parked and hence assumed to not touch pending_list | |
| 682 | − */ | |
| 683 | − if ((sched->timeout != MAX_SCHEDULE_TIMEOUT && | |
| 684 | − !cancel_delayed_work(&sched->work_tdr)) || | |
| 685 | − kthread_should_park()) | |
| 686 | − return NULL; | |
| 687 | − | |
| 688 | 830 | spin_lock(&sched->job_list_lock); |
| 689 | 831 | |
| 690 | 832 | job = list_first_entry_or_null(&sched->pending_list, |
| @@ -693,17 +835,21 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched) · 2 unmodified lines | ||
| 693 | 835 | if (job && dma_fence_is_signaled(&job->s_fence->finished)) { |
| 694 | 836 | /* remove job from pending_list */ |
| 695 | 837 | list_del_init(&job->list); |
| 838 | + | |
| 839 | + /* cancel this job's TO timer */ | |
| 840 | + cancel_delayed_work(&sched->work_tdr); | |
| 696 | 841 | /* make the scheduled timestamp more accurate */ |
| 697 | 842 | next = list_first_entry_or_null(&sched->pending_list, |
| 698 | 843 | typeof(*next), list); |
| 699 | − if (next) | |
| 844 | + | |
| 845 | + if (next) { | |
| 700 | 846 | next->s_fence->scheduled.timestamp = |
| 701 | 847 | job->s_fence->finished.timestamp; |
| 702 | − | |
| 848 | + /* start TO timer for next job */ | |
| 849 | + drm_sched_start_timeout(sched); | |
| 850 | + } | |
| 703 | 851 | } else { |
| 704 | 852 | job = NULL; |
| 705 | − /* queue timeout for next job */ | |
| 706 | − drm_sched_start_timeout(sched); | |
| 707 | 853 | } |
| 708 | 854 | |
| 709 | 855 | spin_unlock(&sched->job_list_lock); |
| @@ -791,11 +937,8 @@ static int drm_sched_main(void *param) · 81 unmodified lines | ||
| 791 | 937 | (entity = drm_sched_select_entity(sched))) || |
| 792 | 938 | kthread_should_stop()); |
| 793 | 939 | |
| 794 | − if (cleanup_job) { | |
| 940 | + if (cleanup_job) | |
| 795 | 941 | sched->ops->free_job(cleanup_job); |
| 796 | − /* queue timeout for next job */ | |
| 797 | − drm_sched_start_timeout(sched); | |
| 798 | − } | |
| 799 | 942 | |
| 800 | 943 | if (!entity) |
| 801 | 944 | continue; |
| @@ -845,7 +845,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, · 844 unmodified lines | ||
| 845 | 845 | LXCFBLR_CFBLL | LXCFBLR_CFBP, val); |
| 846 | 846 | |
| 847 | 847 | /* Specifies the constant alpha value */ |
| 848 | − val = CONSTA_MAX; | |
| 848 | + val = newstate->alpha >> 8; | |
| 849 | 849 | reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val); |
| 850 | 850 | |
| 851 | 851 | /* Specifies the blending factors */ |
| @@ -997,6 +997,8 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, · 145 unmodified lines | ||
| 997 | 997 | |
| 998 | 998 | drm_plane_helper_add(plane, <dc_plane_helper_funcs); |
| 999 | 999 | |
| 1000 | + drm_plane_create_alpha_property(plane); | |
| 1001 | + | |
| 1000 | 1002 | DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id); |
| 1001 | 1003 | |
| 1002 | 1004 | return plane; |
| @@ -1024,6 +1026,8 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) · 21 unmodified lines | ||
| 1024 | 1026 | return -EINVAL; |
| 1025 | 1027 | } |
| 1026 | 1028 | |
| 1029 | + drm_plane_create_zpos_immutable_property(primary, 0); | |
| 1030 | + | |
| 1027 | 1031 | ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, |
| 1028 | 1032 | <dc_crtc_funcs, NULL); |
| 1029 | 1033 | if (ret) { |
| @@ -1046,6 +1050,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) · 16 unmodified lines | ||
| 1046 | 1050 | DRM_ERROR("Can not create overlay plane %d\n", i); |
| 1047 | 1051 | goto cleanup; |
| 1048 | 1052 | } |
| 1053 | + drm_plane_create_zpos_immutable_property(overlay, i); | |
| 1049 | 1054 | } |
| 1050 | 1055 | |
| 1051 | 1056 | return 0; |
| @@ -782,7 +782,6 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, · 781 unmodified lines | ||
| 782 | 782 | struct sun4i_drv *drv = drm->dev_private; |
| 783 | 783 | struct sun4i_backend *backend; |
| 784 | 784 | const struct sun4i_backend_quirks *quirks; |
| 785 | − struct resource *res; | |
| 786 | 785 | void __iomem *regs; |
| 787 | 786 | int i, ret; |
| 788 | 787 | |
| @@ -815,8 +814,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, · 26 unmodified lines | ||
| 815 | 814 | if (IS_ERR(backend->frontend)) |
| 816 | 815 | dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n"); |
| 817 | 816 | |
| 818 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 819 | − regs = devm_ioremap_resource(dev, res); | |
| 817 | + regs = devm_platform_ioremap_resource(pdev, 0); | |
| 820 | 818 | if (IS_ERR(regs)) |
| 821 | 819 | return PTR_ERR(regs); |
| 822 | 820 | |
| @@ -561,7 +561,6 @@ static int sun4i_frontend_bind(struct device *dev, struct device *master, · 560 unmodified lines | ||
| 561 | 561 | struct sun4i_frontend *frontend; |
| 562 | 562 | struct drm_device *drm = data; |
| 563 | 563 | struct sun4i_drv *drv = drm->dev_private; |
| 564 | − struct resource *res; | |
| 565 | 564 | void __iomem *regs; |
| 566 | 565 | |
| 567 | 566 | frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL); |
| @@ -576,8 +575,7 @@ static int sun4i_frontend_bind(struct device *dev, struct device *master, · 8 unmodified lines | ||
| 576 | 575 | if (!frontend->data) |
| 577 | 576 | return -ENODEV; |
| 578 | 577 | |
| 579 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 580 | − regs = devm_ioremap_resource(dev, res); | |
| 578 | + regs = devm_platform_ioremap_resource(pdev, 0); | |
| 581 | 579 | if (IS_ERR(regs)) |
| 582 | 580 | return PTR_ERR(regs); |
| 583 | 581 | |
| @@ -489,7 +489,6 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, · 488 unmodified lines | ||
| 489 | 489 | struct cec_connector_info conn_info; |
| 490 | 490 | struct sun4i_drv *drv = drm->dev_private; |
| 491 | 491 | struct sun4i_hdmi *hdmi; |
| 492 | − struct resource *res; | |
| 493 | 492 | u32 reg; |
| 494 | 493 | int ret; |
| 495 | 494 | |
| @@ -504,8 +503,7 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, · 8 unmodified lines | ||
| 504 | 503 | if (!hdmi->variant) |
| 505 | 504 | return -EINVAL; |
| 506 | 505 | |
| 507 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 508 | − hdmi->base = devm_ioremap_resource(dev, res); | |
| 506 | + hdmi->base = devm_platform_ioremap_resource(pdev, 0); | |
| 509 | 507 | if (IS_ERR(hdmi->base)) { |
| 510 | 508 | dev_err(dev, "Couldn't map the HDMI encoder registers\n"); |
| 511 | 509 | return PTR_ERR(hdmi->base); |
| @@ -841,11 +841,9 @@ static int sun4i_tcon_init_regmap(struct device *dev, · 840 unmodified lines | ||
| 841 | 841 | struct sun4i_tcon *tcon) |
| 842 | 842 | { |
| 843 | 843 | struct platform_device *pdev = to_platform_device(dev); |
| 844 | − struct resource *res; | |
| 845 | 844 | void __iomem *regs; |
| 846 | 845 | |
| 847 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 848 | − regs = devm_ioremap_resource(dev, res); | |
| 846 | + regs = devm_platform_ioremap_resource(pdev, 0); | |
| 849 | 847 | if (IS_ERR(regs)) |
| 850 | 848 | return PTR_ERR(regs); |
| 851 | 849 | |
| @@ -538,7 +538,6 @@ static int sun4i_tv_bind(struct device *dev, struct device *master, · 537 unmodified lines | ||
| 538 | 538 | struct drm_device *drm = data; |
| 539 | 539 | struct sun4i_drv *drv = drm->dev_private; |
| 540 | 540 | struct sun4i_tv *tv; |
| 541 | − struct resource *res; | |
| 542 | 541 | void __iomem *regs; |
| 543 | 542 | int ret; |
| 544 | 543 | |
| @@ -548,8 +547,7 @@ static int sun4i_tv_bind(struct device *dev, struct device *master, · 3 unmodified lines | ||
| 548 | 547 | tv->drv = drv; |
| 549 | 548 | dev_set_drvdata(dev, tv); |
| 550 | 549 | |
| 551 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 552 | − regs = devm_ioremap_resource(dev, res); | |
| 550 | + regs = devm_platform_ioremap_resource(pdev, 0); | |
| 553 | 551 | if (IS_ERR(regs)) { |
| 554 | 552 | dev_err(dev, "Couldn't map the TV encoder registers\n"); |
| 555 | 553 | return PTR_ERR(regs); |
| @@ -1104,7 +1104,6 @@ static int sun6i_dsi_probe(struct platform_device *pdev) · 1103 unmodified lines | ||
| 1104 | 1104 | struct device *dev = &pdev->dev; |
| 1105 | 1105 | const char *bus_clk_name = NULL; |
| 1106 | 1106 | struct sun6i_dsi *dsi; |
| 1107 | − struct resource *res; | |
| 1108 | 1107 | void __iomem *base; |
| 1109 | 1108 | int ret; |
| 1110 | 1109 | |
| @@ -1120,8 +1119,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev) · 9 unmodified lines | ||
| 1120 | 1119 | "allwinner,sun6i-a31-mipi-dsi")) |
| 1121 | 1120 | bus_clk_name = "bus"; |
| 1122 | 1121 | |
| 1123 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 1124 | − base = devm_ioremap_resource(dev, res); | |
| 1122 | + base = devm_platform_ioremap_resource(pdev, 0); | |
| 1125 | 1123 | if (IS_ERR(base)) { |
| 1126 | 1124 | dev_err(dev, "Couldn't map the DSI encoder registers\n"); |
| 1127 | 1125 | return PTR_ERR(base); |
| @@ -16,8 +16,8 @@ struct sun8i_mixer; · 15 unmodified lines | ||
| 16 | 16 | #define CCSC10_OFFSET 0xA0000 |
| 17 | 17 | #define CCSC11_OFFSET 0xF0000 |
| 18 | 18 | |
| 19 | −#define SUN8I_CSC_CTRL(base) (base + 0x0) | |
| 20 | −#define SUN8I_CSC_COEFF(base, i) (base + 0x10 + 4 * i) | |
| 19 | +#define SUN8I_CSC_CTRL(base) ((base) + 0x0) | |
| 20 | +#define SUN8I_CSC_COEFF(base, i) ((base) + 0x10 + 4 * (i)) | |
| 21 | 21 | |
| 22 | 22 | #define SUN8I_CSC_CTRL_EN BIT(0) |
| 23 | 23 | |
| @@ -337,7 +337,6 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, · 336 unmodified lines | ||
| 337 | 337 | struct drm_device *drm = data; |
| 338 | 338 | struct sun4i_drv *drv = drm->dev_private; |
| 339 | 339 | struct sun8i_mixer *mixer; |
| 340 | − struct resource *res; | |
| 341 | 340 | void __iomem *regs; |
| 342 | 341 | unsigned int base; |
| 343 | 342 | int plane_cnt; |
| @@ -390,8 +389,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, · 46 unmodified lines | ||
| 390 | 389 | if (!mixer->cfg) |
| 391 | 390 | return -EINVAL; |
| 392 | 391 | |
| 393 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 394 | − regs = devm_ioremap_resource(dev, res); | |
| 392 | + regs = devm_platform_ioremap_resource(pdev, 0); | |
| 395 | 393 | if (IS_ERR(regs)) |
| 396 | 394 | return PTR_ERR(regs); |
| 397 | 395 | |
| @@ -128,7 +128,6 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, · 127 unmodified lines | ||
| 128 | 128 | struct clk_hw_onecell_data *clk_data; |
| 129 | 129 | struct sun8i_tcon_top *tcon_top; |
| 130 | 130 | const struct sun8i_tcon_top_quirks *quirks; |
| 131 | − struct resource *res; | |
| 132 | 131 | void __iomem *regs; |
| 133 | 132 | int ret, i; |
| 134 | 133 | |
| @@ -158,8 +157,7 @@ static int sun8i_tcon_top_bind(struct device *dev, struct device *master, · 23 unmodified lines | ||
| 158 | 157 | return PTR_ERR(tcon_top->bus); |
| 159 | 158 | } |
| 160 | 159 | |
| 161 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 162 | − regs = devm_ioremap_resource(dev, res); | |
| 160 | + regs = devm_platform_ioremap_resource(pdev, 0); | |
| 163 | 161 | tcon_top->regs = regs; |
| 164 | 162 | if (IS_ERR(regs)) |
| 165 | 163 | return PTR_ERR(regs); |
| @@ -44,7 +44,7 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, · 43 unmodified lines | ||
| 44 | 44 | { |
| 45 | 45 | uint64_t modifier = framebuffer->modifier; |
| 46 | 46 | |
| 47 | − if ((modifier >> 56) == DRM_FORMAT_MOD_VENDOR_NVIDIA) { | |
| 47 | + if (fourcc_mod_is_vendor(modifier, NVIDIA)) { | |
| 48 | 48 | if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0) |
| 49 | 49 | tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA; |
| 50 | 50 | else |
| @@ -113,7 +113,7 @@ static bool tegra_plane_format_mod_supported(struct drm_plane *plane, · 112 unmodified lines | ||
| 113 | 113 | return true; |
| 114 | 114 | |
| 115 | 115 | /* check for the sector layout bit */ |
| 116 | − if ((modifier >> 56) == DRM_FORMAT_MOD_VENDOR_NVIDIA) { | |
| 116 | + if (fourcc_mod_is_vendor(modifier, NVIDIA)) { | |
| 117 | 117 | if (modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) { |
| 118 | 118 | if (!tegra_plane_supports_sector_layout(plane)) |
| 119 | 119 | return false; |
| @@ -44,7 +44,7 @@ config DRM_CIRRUS_QEMU · 43 unmodified lines | ||
| 44 | 44 | |
| 45 | 45 | config DRM_GM12U320 |
| 46 | 46 | tristate "GM12U320 driver for USB projectors" |
| 47 | − depends on DRM && USB | |
| 47 | + depends on DRM && USB && MMU | |
| 48 | 48 | select DRM_KMS_HELPER |
| 49 | 49 | select DRM_GEM_SHMEM_HELPER |
| 50 | 50 | help |
| @@ -53,7 +53,7 @@ config DRM_GM12U320 · 2 unmodified lines | ||
| 53 | 53 | |
| 54 | 54 | config DRM_SIMPLEDRM |
| 55 | 55 | tristate "Simple framebuffer driver" |
| 56 | − depends on DRM | |
| 56 | + depends on DRM && MMU | |
| 57 | 57 | select DRM_GEM_SHMEM_HELPER |
| 58 | 58 | select DRM_KMS_HELPER |
| 59 | 59 | help |
| @@ -63,6 +63,7 @@ MODULE_PARM_DESC(defy, "default y resolution"); · 62 unmodified lines | ||
| 63 | 63 | |
| 64 | 64 | enum bochs_types { |
| 65 | 65 | BOCHS_QEMU_STDVGA, |
| 66 | + BOCHS_SIMICS, | |
| 66 | 67 | BOCHS_UNKNOWN, |
| 67 | 68 | }; |
| 68 | 69 | |
| @@ -695,6 +696,13 @@ static const struct pci_device_id bochs_pci_tbl[] = { · 626 unmodified lines | ||
| 695 | 696 | .subdevice = PCI_ANY_ID, |
| 696 | 697 | .driver_data = BOCHS_UNKNOWN, |
| 697 | 698 | }, |
| 699 | + { | |
| 700 | + .vendor = 0x4321, | |
| 701 | + .device = 0x1111, | |
| 702 | + .subvendor = PCI_ANY_ID, | |
| 703 | + .subdevice = PCI_ANY_ID, | |
| 704 | + .driver_data = BOCHS_SIMICS, | |
| 705 | + }, | |
| 698 | 706 | { /* end of list */ } |
| 699 | 707 | }; |
| 700 | 708 | |
| @@ -69,7 +69,17 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo, · 68 unmodified lines | ||
| 69 | 69 | } |
| 70 | 70 | } |
| 71 | 71 | |
| 72 | −static void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) | |
| 72 | +static inline void ttm_bo_move_to_pinned(struct ttm_buffer_object *bo) | |
| 73 | +{ | |
| 74 | + struct ttm_device *bdev = bo->bdev; | |
| 75 | + | |
| 76 | + list_move_tail(&bo->lru, &bdev->pinned); | |
| 77 | + | |
| 78 | + if (bdev->funcs->del_from_lru_notify) | |
| 79 | + bdev->funcs->del_from_lru_notify(bo); | |
| 80 | +} | |
| 81 | + | |
| 82 | +static inline void ttm_bo_del_from_lru(struct ttm_buffer_object *bo) | |
| 73 | 83 | { |
| 74 | 84 | struct ttm_device *bdev = bo->bdev; |
| 75 | 85 | |
| @@ -98,7 +108,7 @@ void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo, · 22 unmodified lines | ||
| 98 | 108 | dma_resv_assert_held(bo->base.resv); |
| 99 | 109 | |
| 100 | 110 | if (bo->pin_count) { |
| 101 | − ttm_bo_del_from_lru(bo); | |
| 111 | + ttm_bo_move_to_pinned(bo); | |
| 102 | 112 | return; |
| 103 | 113 | } |
| 104 | 114 | |
| @@ -342,7 +352,7 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, · 237 unmodified lines | ||
| 342 | 352 | return ret; |
| 343 | 353 | } |
| 344 | 354 | |
| 345 | − ttm_bo_del_from_lru(bo); | |
| 355 | + ttm_bo_move_to_pinned(bo); | |
| 346 | 356 | list_del_init(&bo->ddestroy); |
| 347 | 357 | spin_unlock(&bo->bdev->lru_lock); |
| 348 | 358 | ttm_bo_cleanup_memtype_use(bo); |
| @@ -914,57 +924,11 @@ out: · 565 unmodified lines | ||
| 914 | 924 | return ret; |
| 915 | 925 | } |
| 916 | 926 | |
| 917 | −static bool ttm_bo_places_compat(const struct ttm_place *places, | |
| 918 | − unsigned num_placement, | |
| 919 | − struct ttm_resource *mem, | |
| 920 | − uint32_t *new_flags) | |
| 921 | −{ | |
| 922 | − unsigned i; | |
| 923 | − | |
| 924 | − if (mem->placement & TTM_PL_FLAG_TEMPORARY) | |
| 925 | − return false; | |
| 926 | − | |
| 927 | − for (i = 0; i < num_placement; i++) { | |
| 928 | − const struct ttm_place *heap = &places[i]; | |
| 929 | − | |
| 930 | − if ((mem->start < heap->fpfn || | |
| 931 | − (heap->lpfn != 0 && (mem->start + mem->num_pages) > heap->lpfn))) | |
| 932 | − continue; | |
| 933 | − | |
| 934 | − *new_flags = heap->flags; | |
| 935 | − if ((mem->mem_type == heap->mem_type) && | |
| 936 | − (!(*new_flags & TTM_PL_FLAG_CONTIGUOUS) || | |
| 937 | − (mem->placement & TTM_PL_FLAG_CONTIGUOUS))) | |
| 938 | − return true; | |
| 939 | − } | |
| 940 | − return false; | |
| 941 | −} | |
| 942 | − | |
| 943 | −bool ttm_bo_mem_compat(struct ttm_placement *placement, | |
| 944 | − struct ttm_resource *mem, | |
| 945 | − uint32_t *new_flags) | |
| 946 | −{ | |
| 947 | − if (ttm_bo_places_compat(placement->placement, placement->num_placement, | |
| 948 | − mem, new_flags)) | |
| 949 | − return true; | |
| 950 | − | |
| 951 | − if ((placement->busy_placement != placement->placement || | |
| 952 | − placement->num_busy_placement > placement->num_placement) && | |
| 953 | − ttm_bo_places_compat(placement->busy_placement, | |
| 954 | − placement->num_busy_placement, | |
| 955 | − mem, new_flags)) | |
| 956 | − return true; | |
| 957 | − | |
| 958 | − return false; | |
| 959 | −} | |
| 960 | −EXPORT_SYMBOL(ttm_bo_mem_compat); | |
| 961 | − | |
| 962 | 927 | int ttm_bo_validate(struct ttm_buffer_object *bo, |
| 963 | 928 | struct ttm_placement *placement, |
| 964 | 929 | struct ttm_operation_ctx *ctx) |
| 965 | 930 | { |
| 966 | 931 | int ret; |
| 967 | − uint32_t new_flags; | |
| 968 | 932 | |
| 969 | 933 | dma_resv_assert_held(bo->base.resv); |
| 970 | 934 | |
| @@ -977,7 +941,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, · 6 unmodified lines | ||
| 977 | 941 | /* |
| 978 | 942 | * Check whether we need to move buffer. |
| 979 | 943 | */ |
| 980 | − if (!ttm_bo_mem_compat(placement, bo->resource, &new_flags)) { | |
| 944 | + if (!ttm_resource_compat(bo->resource, placement)) { | |
| 981 | 945 | ret = ttm_bo_move_buffer(bo, placement, ctx); |
| 982 | 946 | if (ret) |
| 983 | 947 | return ret; |
| @@ -1165,7 +1129,7 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, · 181 unmodified lines | ||
| 1165 | 1129 | return ret == -EBUSY ? -ENOSPC : ret; |
| 1166 | 1130 | } |
| 1167 | 1131 | |
| 1168 | − ttm_bo_del_from_lru(bo); | |
| 1132 | + ttm_bo_move_to_pinned(bo); | |
| 1169 | 1133 | /* TODO: Cleanup the locking */ |
| 1170 | 1134 | spin_unlock(&bo->bdev->lru_lock); |
| 1171 | 1135 | |
| @@ -1224,6 +1188,7 @@ void ttm_bo_tt_destroy(struct ttm_buffer_object *bo) · 52 unmodified lines | ||
| 1224 | 1188 | if (bo->ttm == NULL) |
| 1225 | 1189 | return; |
| 1226 | 1190 | |
| 1191 | + ttm_tt_unpopulate(bo->bdev, bo->ttm); | |
| 1227 | 1192 | ttm_tt_destroy(bo->bdev, bo->ttm); |
| 1228 | 1193 | bo->ttm = NULL; |
| 1229 | 1194 | } |
| @@ -220,6 +220,7 @@ int ttm_device_init(struct ttm_device *bdev, struct ttm_device_funcs *funcs, · 219 unmodified lines | ||
| 220 | 220 | INIT_DELAYED_WORK(&bdev->wq, ttm_device_delayed_workqueue); |
| 221 | 221 | spin_lock_init(&bdev->lru_lock); |
| 222 | 222 | INIT_LIST_HEAD(&bdev->ddestroy); |
| 223 | + INIT_LIST_HEAD(&bdev->pinned); | |
| 223 | 224 | bdev->dev_mapping = mapping; |
| 224 | 225 | mutex_lock(&ttm_global_mutex); |
| 225 | 226 | list_add_tail(&bdev->device_list, &glob->device_list); |
| @@ -257,3 +258,50 @@ void ttm_device_fini(struct ttm_device *bdev) · 31 unmodified lines | ||
| 257 | 258 | ttm_global_release(); |
| 258 | 259 | } |
| 259 | 260 | EXPORT_SYMBOL(ttm_device_fini); |
| 261 | + | |
| 262 | +void ttm_device_clear_dma_mappings(struct ttm_device *bdev) | |
| 263 | +{ | |
| 264 | + struct ttm_resource_manager *man; | |
| 265 | + struct ttm_buffer_object *bo; | |
| 266 | + unsigned int i, j; | |
| 267 | + | |
| 268 | + spin_lock(&bdev->lru_lock); | |
| 269 | + while (!list_empty(&bdev->pinned)) { | |
| 270 | + bo = list_first_entry(&bdev->pinned, struct ttm_buffer_object, lru); | |
| 271 | + /* Take ref against racing releases once lru_lock is unlocked */ | |
| 272 | + if (ttm_bo_get_unless_zero(bo)) { | |
| 273 | + list_del_init(&bo->lru); | |
| 274 | + spin_unlock(&bdev->lru_lock); | |
| 275 | + | |
| 276 | + if (bo->ttm) | |
| 277 | + ttm_tt_unpopulate(bo->bdev, bo->ttm); | |
| 278 | + | |
| 279 | + ttm_bo_put(bo); | |
| 280 | + spin_lock(&bdev->lru_lock); | |
| 281 | + } | |
| 282 | + } | |
| 283 | + | |
| 284 | + for (i = TTM_PL_SYSTEM; i < TTM_NUM_MEM_TYPES; ++i) { | |
| 285 | + man = ttm_manager_type(bdev, i); | |
| 286 | + if (!man || !man->use_tt) | |
| 287 | + continue; | |
| 288 | + | |
| 289 | + for (j = 0; j < TTM_MAX_BO_PRIORITY; ++j) { | |
| 290 | + while (!list_empty(&man->lru[j])) { | |
| 291 | + bo = list_first_entry(&man->lru[j], struct ttm_buffer_object, lru); | |
| 292 | + if (ttm_bo_get_unless_zero(bo)) { | |
| 293 | + list_del_init(&bo->lru); | |
| 294 | + spin_unlock(&bdev->lru_lock); | |
| 295 | + | |
| 296 | + if (bo->ttm) | |
| 297 | + ttm_tt_unpopulate(bo->bdev, bo->ttm); | |
| 298 | + | |
| 299 | + ttm_bo_put(bo); | |
| 300 | + spin_lock(&bdev->lru_lock); | |
| 301 | + } | |
| 302 | + } | |
| 303 | + } | |
| 304 | + } | |
| 305 | + spin_unlock(&bdev->lru_lock); | |
| 306 | +} | |
| 307 | +EXPORT_SYMBOL(ttm_device_clear_dma_mappings); | |
| @@ -39,6 +39,18 @@ · 38 unmodified lines | ||
| 39 | 39 | |
| 40 | 40 | #include "ttm_module.h" |
| 41 | 41 | |
| 42 | +/** | |
| 43 | + * DOC: TTM | |
| 44 | + * | |
| 45 | + * TTM is a memory manager for accelerator devices with dedicated memory. | |
| 46 | + * | |
| 47 | + * The basic idea is that resources are grouped together in buffer objects of | |
| 48 | + * certain size and TTM handles lifetime, movement and CPU mappings of those | |
| 49 | + * objects. | |
| 50 | + * | |
| 51 | + * TODO: Add more design background and information here. | |
| 52 | + */ | |
| 53 | + | |
| 42 | 54 | /** |
| 43 | 55 | * ttm_prot_from_caching - Modify the page protection according to the |
| 44 | 56 | * ttm cacing mode |
| @@ -70,7 +70,7 @@ static struct ttm_pool_type global_uncached[MAX_ORDER]; · 69 unmodified lines | ||
| 70 | 70 | static struct ttm_pool_type global_dma32_write_combined[MAX_ORDER]; |
| 71 | 71 | static struct ttm_pool_type global_dma32_uncached[MAX_ORDER]; |
| 72 | 72 | |
| 73 | −static struct mutex shrinker_lock; | |
| 73 | +static spinlock_t shrinker_lock; | |
| 74 | 74 | static struct list_head shrinker_list; |
| 75 | 75 | static struct shrinker mm_shrinker; |
| 76 | 76 | |
| @@ -263,9 +263,9 @@ static void ttm_pool_type_init(struct ttm_pool_type *pt, struct ttm_pool *pool, · 186 unmodified lines | ||
| 263 | 263 | spin_lock_init(&pt->lock); |
| 264 | 264 | INIT_LIST_HEAD(&pt->pages); |
| 265 | 265 | |
| 266 | − mutex_lock(&shrinker_lock); | |
| 266 | + spin_lock(&shrinker_lock); | |
| 267 | 267 | list_add_tail(&pt->shrinker_list, &shrinker_list); |
| 268 | − mutex_unlock(&shrinker_lock); | |
| 268 | + spin_unlock(&shrinker_lock); | |
| 269 | 269 | } |
| 270 | 270 | |
| 271 | 271 | /* Remove a pool_type from the global shrinker list and free all pages */ |
| @@ -273,9 +273,9 @@ static void ttm_pool_type_fini(struct ttm_pool_type *pt) · 1 unmodified line | ||
| 273 | 273 | { |
| 274 | 274 | struct page *p; |
| 275 | 275 | |
| 276 | − mutex_lock(&shrinker_lock); | |
| 276 | + spin_lock(&shrinker_lock); | |
| 277 | 277 | list_del(&pt->shrinker_list); |
| 278 | − mutex_unlock(&shrinker_lock); | |
| 278 | + spin_unlock(&shrinker_lock); | |
| 279 | 279 | |
| 280 | 280 | while ((p = ttm_pool_type_take(pt))) |
| 281 | 281 | ttm_pool_free_page(pt->pool, pt->caching, pt->order, p); |
| @@ -313,24 +313,23 @@ static struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool, · 31 unmodified lines | ||
| 313 | 313 | static unsigned int ttm_pool_shrink(void) |
| 314 | 314 | { |
| 315 | 315 | struct ttm_pool_type *pt; |
| 316 | − unsigned int num_freed; | |
| 316 | + unsigned int num_pages; | |
| 317 | 317 | struct page *p; |
| 318 | 318 | |
| 319 | − mutex_lock(&shrinker_lock); | |
| 319 | + spin_lock(&shrinker_lock); | |
| 320 | 320 | pt = list_first_entry(&shrinker_list, typeof(*pt), shrinker_list); |
| 321 | + list_move_tail(&pt->shrinker_list, &shrinker_list); | |
| 322 | + spin_unlock(&shrinker_lock); | |
| 321 | 323 | |
| 322 | 324 | p = ttm_pool_type_take(pt); |
| 323 | 325 | if (p) { |
| 324 | 326 | ttm_pool_free_page(pt->pool, pt->caching, pt->order, p); |
| 325 | − num_freed = 1 << pt->order; | |
| 327 | + num_pages = 1 << pt->order; | |
| 326 | 328 | } else { |
| 327 | − num_freed = 0; | |
| 329 | + num_pages = 0; | |
| 328 | 330 | } |
| 329 | 331 | |
| 330 | − list_move_tail(&pt->shrinker_list, &shrinker_list); | |
| 331 | − mutex_unlock(&shrinker_lock); | |
| 332 | − | |
| 333 | − return num_freed; | |
| 332 | + return num_pages; | |
| 334 | 333 | } |
| 335 | 334 | |
| 336 | 335 | /* Return the allocation order based for a page */ |
| @@ -531,6 +530,11 @@ void ttm_pool_fini(struct ttm_pool *pool) · 194 unmodified lines | ||
| 531 | 530 | for (j = 0; j < MAX_ORDER; ++j) |
| 532 | 531 | ttm_pool_type_fini(&pool->caching[i].orders[j]); |
| 533 | 532 | } |
| 533 | + | |
| 534 | + /* We removed the pool types from the LRU, but we need to also make sure | |
| 535 | + * that no shrinker is concurrently freeing pages from the pool. | |
| 536 | + */ | |
| 537 | + synchronize_shrinkers(); | |
| 534 | 538 | } |
| 535 | 539 | |
| 536 | 540 | /* As long as pages are available make sure to release at least one */ |
| @@ -605,7 +609,7 @@ static int ttm_pool_debugfs_globals_show(struct seq_file *m, void *data) · 68 unmodified lines | ||
| 605 | 609 | { |
| 606 | 610 | ttm_pool_debugfs_header(m); |
| 607 | 611 | |
| 608 | − mutex_lock(&shrinker_lock); | |
| 612 | + spin_lock(&shrinker_lock); | |
| 609 | 613 | seq_puts(m, "wc\t:"); |
| 610 | 614 | ttm_pool_debugfs_orders(global_write_combined, m); |
| 611 | 615 | seq_puts(m, "uc\t:"); |
| @@ -614,7 +618,7 @@ static int ttm_pool_debugfs_globals_show(struct seq_file *m, void *data) · 2 unmodified lines | ||
| 614 | 618 | ttm_pool_debugfs_orders(global_dma32_write_combined, m); |
| 615 | 619 | seq_puts(m, "uc 32\t:"); |
| 616 | 620 | ttm_pool_debugfs_orders(global_dma32_uncached, m); |
| 617 | − mutex_unlock(&shrinker_lock); | |
| 621 | + spin_unlock(&shrinker_lock); | |
| 618 | 622 | |
| 619 | 623 | ttm_pool_debugfs_footer(m); |
| 620 | 624 | |
| @@ -641,7 +645,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m) · 20 unmodified lines | ||
| 641 | 645 | |
| 642 | 646 | ttm_pool_debugfs_header(m); |
| 643 | 647 | |
| 644 | − mutex_lock(&shrinker_lock); | |
| 648 | + spin_lock(&shrinker_lock); | |
| 645 | 649 | for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) { |
| 646 | 650 | seq_puts(m, "DMA "); |
| 647 | 651 | switch (i) { |
| @@ -657,7 +661,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m) · 9 unmodified lines | ||
| 657 | 661 | } |
| 658 | 662 | ttm_pool_debugfs_orders(pool->caching[i].orders, m); |
| 659 | 663 | } |
| 660 | − mutex_unlock(&shrinker_lock); | |
| 664 | + spin_unlock(&shrinker_lock); | |
| 661 | 665 | |
| 662 | 666 | ttm_pool_debugfs_footer(m); |
| 663 | 667 | return 0; |
| @@ -694,7 +698,7 @@ int ttm_pool_mgr_init(unsigned long num_pages) · 30 unmodified lines | ||
| 694 | 698 | if (!page_pool_size) |
| 695 | 699 | page_pool_size = num_pages; |
| 696 | 700 | |
| 697 | − mutex_init(&shrinker_lock); | |
| 701 | + spin_lock_init(&shrinker_lock); | |
| 698 | 702 | INIT_LIST_HEAD(&shrinker_list); |
| 699 | 703 | |
| 700 | 704 | for (i = 0; i < MAX_ORDER; ++i) { |
| @@ -138,7 +138,7 @@ static const struct ttm_resource_manager_func ttm_range_manager_func = { · 137 unmodified lines | ||
| 138 | 138 | * Initialise a generic range manager for the selected memory type. |
| 139 | 139 | * The range manager is installed for this device in the type slot. |
| 140 | 140 | */ |
| 141 | −int ttm_range_man_init(struct ttm_device *bdev, | |
| 141 | +int ttm_range_man_init_nocheck(struct ttm_device *bdev, | |
| 142 | 142 | unsigned type, bool use_tt, |
| 143 | 143 | unsigned long p_size) |
| 144 | 144 | { |
| @@ -163,7 +163,7 @@ int ttm_range_man_init(struct ttm_device *bdev, · 18 unmodified lines | ||
| 163 | 163 | ttm_resource_manager_set_used(man, true); |
| 164 | 164 | return 0; |
| 165 | 165 | } |
| 166 | −EXPORT_SYMBOL(ttm_range_man_init); | |
| 166 | +EXPORT_SYMBOL(ttm_range_man_init_nocheck); | |
| 167 | 167 | |
| 168 | 168 | /** |
| 169 | 169 | * ttm_range_man_fini |
| @@ -173,7 +173,7 @@ EXPORT_SYMBOL(ttm_range_man_init); · 3 unmodified lines | ||
| 173 | 173 | * |
| 174 | 174 | * Remove the generic range manager from a slot and tear it down. |
| 175 | 175 | */ |
| 176 | −int ttm_range_man_fini(struct ttm_device *bdev, | |
| 176 | +int ttm_range_man_fini_nocheck(struct ttm_device *bdev, | |
| 177 | 177 | unsigned type) |
| 178 | 178 | { |
| 179 | 179 | struct ttm_resource_manager *man = ttm_manager_type(bdev, type); |
| @@ -200,4 +200,4 @@ int ttm_range_man_fini(struct ttm_device *bdev, · 20 unmodified lines | ||
| 200 | 200 | kfree(rman); |
| 201 | 201 | return 0; |
| 202 | 202 | } |
| 203 | −EXPORT_SYMBOL(ttm_range_man_fini); | |
| 203 | +EXPORT_SYMBOL(ttm_range_man_fini_nocheck); | |
| @@ -67,6 +67,55 @@ void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res) · 66 unmodified lines | ||
| 67 | 67 | } |
| 68 | 68 | EXPORT_SYMBOL(ttm_resource_free); |
| 69 | 69 | |
| 70 | +static bool ttm_resource_places_compat(struct ttm_resource *res, | |
| 71 | + const struct ttm_place *places, | |
| 72 | + unsigned num_placement) | |
| 73 | +{ | |
| 74 | + unsigned i; | |
| 75 | + | |
| 76 | + if (res->placement & TTM_PL_FLAG_TEMPORARY) | |
| 77 | + return false; | |
| 78 | + | |
| 79 | + for (i = 0; i < num_placement; i++) { | |
| 80 | + const struct ttm_place *heap = &places[i]; | |
| 81 | + | |
| 82 | + if (res->start < heap->fpfn || (heap->lpfn && | |
| 83 | + (res->start + res->num_pages) > heap->lpfn)) | |
| 84 | + continue; | |
| 85 | + | |
| 86 | + if ((res->mem_type == heap->mem_type) && | |
| 87 | + (!(heap->flags & TTM_PL_FLAG_CONTIGUOUS) || | |
| 88 | + (res->placement & TTM_PL_FLAG_CONTIGUOUS))) | |
| 89 | + return true; | |
| 90 | + } | |
| 91 | + return false; | |
| 92 | +} | |
| 93 | + | |
| 94 | +/** | |
| 95 | + * ttm_resource_compat - check if resource is compatible with placement | |
| 96 | + * | |
| 97 | + * @res: the resource to check | |
| 98 | + * @placement: the placement to check against | |
| 99 | + * | |
| 100 | + * Returns true if the placement is compatible. | |
| 101 | + */ | |
| 102 | +bool ttm_resource_compat(struct ttm_resource *res, | |
| 103 | + struct ttm_placement *placement) | |
| 104 | +{ | |
| 105 | + if (ttm_resource_places_compat(res, placement->placement, | |
| 106 | + placement->num_placement)) | |
| 107 | + return true; | |
| 108 | + | |
| 109 | + if ((placement->busy_placement != placement->placement || | |
| 110 | + placement->num_busy_placement > placement->num_placement) && | |
| 111 | + ttm_resource_places_compat(res, placement->busy_placement, | |
| 112 | + placement->num_busy_placement)) | |
| 113 | + return true; | |
| 114 | + | |
| 115 | + return false; | |
| 116 | +} | |
| 117 | +EXPORT_SYMBOL(ttm_resource_compat); | |
| 118 | + | |
| 70 | 119 | /** |
| 71 | 120 | * ttm_resource_manager_init |
| 72 | 121 | * |
| @@ -122,17 +122,6 @@ static int ttm_sg_tt_alloc_page_directory(struct ttm_tt *ttm) · 121 unmodified lines | ||
| 122 | 122 | return 0; |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | −void ttm_tt_destroy_common(struct ttm_device *bdev, struct ttm_tt *ttm) | |
| 126 | −{ | |
| 127 | − ttm_tt_unpopulate(bdev, ttm); | |
| 128 | − | |
| 129 | − if (ttm->swap_storage) | |
| 130 | − fput(ttm->swap_storage); | |
| 131 | − | |
| 132 | − ttm->swap_storage = NULL; | |
| 133 | −} | |
| 134 | −EXPORT_SYMBOL(ttm_tt_destroy_common); | |
| 135 | − | |
| 136 | 125 | void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) |
| 137 | 126 | { |
| 138 | 127 | bdev->funcs->ttm_tt_destroy(bdev, ttm); |
| @@ -167,6 +156,12 @@ EXPORT_SYMBOL(ttm_tt_init); · 28 unmodified lines | ||
| 167 | 156 | |
| 168 | 157 | void ttm_tt_fini(struct ttm_tt *ttm) |
| 169 | 158 | { |
| 159 | + WARN_ON(ttm->page_flags & TTM_PAGE_FLAG_PRIV_POPULATED); | |
| 160 | + | |
| 161 | + if (ttm->swap_storage) | |
| 162 | + fput(ttm->swap_storage); | |
| 163 | + ttm->swap_storage = NULL; | |
| 164 | + | |
| 170 | 165 | if (ttm->pages) |
| 171 | 166 | kvfree(ttm->pages); |
| 172 | 167 | else |
| @@ -4,6 +4,7 @@ config DRM_UDL · 3 unmodified lines | ||
| 4 | 4 | depends on DRM |
| 5 | 5 | depends on USB |
| 6 | 6 | depends on USB_ARCH_HAS_HCD |
| 7 | + depends on MMU | |
| 7 | 8 | select DRM_GEM_SHMEM_HELPER |
| 8 | 9 | select DRM_KMS_HELPER |
| 9 | 10 | help |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | 2 | config DRM_V3D |
| 3 | 3 | tristate "Broadcom V3D 3.x and newer" |
| 4 | − depends on ARCH_BCM || ARCH_BCMSTB || COMPILE_TEST | |
| 4 | + depends on ARCH_BCM || ARCH_BRCMSTB || COMPILE_TEST | |
| 5 | 5 | depends on DRM |
| 6 | 6 | depends on COMMON_CLK |
| 7 | 7 | depends on MMU |
| @@ -234,11 +234,6 @@ struct v3d_job { · 233 unmodified lines | ||
| 234 | 234 | struct drm_gem_object **bo; |
| 235 | 235 | u32 bo_count; |
| 236 | 236 | |
| 237 | − /* Array of struct dma_fence * to block on before submitting this job. | |
| 238 | − */ | |
| 239 | − struct xarray deps; | |
| 240 | − unsigned long last_dep; | |
| 241 | − | |
| 242 | 237 | /* v3d fence to be signaled by IRQ handler when the job is complete. */ |
| 243 | 238 | struct dma_fence *irq_fence; |
| 244 | 239 | |
| @@ -379,6 +374,7 @@ int v3d_submit_csd_ioctl(struct drm_device *dev, void *data, · 134 unmodified lines | ||
| 379 | 374 | struct drm_file *file_priv); |
| 380 | 375 | int v3d_wait_bo_ioctl(struct drm_device *dev, void *data, |
| 381 | 376 | struct drm_file *file_priv); |
| 377 | +void v3d_job_cleanup(struct v3d_job *job); | |
| 382 | 378 | void v3d_job_put(struct v3d_job *job); |
| 383 | 379 | void v3d_reset(struct v3d_dev *v3d); |
| 384 | 380 | void v3d_invalidate_caches(struct v3d_dev *v3d); |
| @@ -197,8 +197,8 @@ v3d_clean_caches(struct v3d_dev *v3d) · 196 unmodified lines | ||
| 197 | 197 | |
| 198 | 198 | V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); |
| 199 | 199 | if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & |
| 200 | − V3D_L2TCACTL_L2TFLS), 100)) { | |
| 201 | − DRM_ERROR("Timeout waiting for L1T write combiner flush\n"); | |
| 200 | + V3D_L2TCACTL_TMUWCF), 100)) { | |
| 201 | + DRM_ERROR("Timeout waiting for TMU write combiner flush\n"); | |
| 202 | 202 | } |
| 203 | 203 | |
| 204 | 204 | mutex_lock(&v3d->cache_clean_lock); |
| @@ -259,8 +259,8 @@ v3d_lock_bo_reservations(struct v3d_job *job, · 54 unmodified lines | ||
| 259 | 259 | return ret; |
| 260 | 260 | |
| 261 | 261 | for (i = 0; i < job->bo_count; i++) { |
| 262 | − ret = drm_gem_fence_array_add_implicit(&job->deps, | |
| 263 | − job->bo[i], true); | |
| 262 | + ret = drm_sched_job_add_implicit_dependencies(&job->base, | |
| 263 | + job->bo[i], true); | |
| 264 | 264 | if (ret) { |
| 265 | 265 | drm_gem_unlock_reservations(job->bo, job->bo_count, |
| 266 | 266 | acquire_ctx); |
| @@ -356,8 +356,6 @@ static void · 89 unmodified lines | ||
| 356 | 356 | v3d_job_free(struct kref *ref) |
| 357 | 357 | { |
| 358 | 358 | struct v3d_job *job = container_of(ref, struct v3d_job, refcount); |
| 359 | − unsigned long index; | |
| 360 | − struct dma_fence *fence; | |
| 361 | 359 | int i; |
| 362 | 360 | |
| 363 | 361 | for (i = 0; i < job->bo_count; i++) { |
| @@ -366,11 +364,6 @@ v3d_job_free(struct kref *ref) · 2 unmodified lines | ||
| 366 | 364 | } |
| 367 | 365 | kvfree(job->bo); |
| 368 | 366 | |
| 369 | − xa_for_each(&job->deps, index, fence) { | |
| 370 | − dma_fence_put(fence); | |
| 371 | − } | |
| 372 | − xa_destroy(&job->deps); | |
| 373 | − | |
| 374 | 367 | dma_fence_put(job->irq_fence); |
| 375 | 368 | dma_fence_put(job->done_fence); |
| 376 | 369 | |
| @@ -397,6 +390,12 @@ v3d_render_job_free(struct kref *ref) · 20 unmodified lines | ||
| 397 | 390 | v3d_job_free(ref); |
| 398 | 391 | } |
| 399 | 392 | |
| 393 | +void v3d_job_cleanup(struct v3d_job *job) | |
| 394 | +{ | |
| 395 | + drm_sched_job_cleanup(&job->base); | |
| 396 | + v3d_job_put(job); | |
| 397 | +} | |
| 398 | + | |
| 400 | 399 | void v3d_job_put(struct v3d_job *job) |
| 401 | 400 | { |
| 402 | 401 | kref_put(&job->refcount, job->free); |
| @@ -438,9 +437,10 @@ v3d_wait_bo_ioctl(struct drm_device *dev, void *data, · 35 unmodified lines | ||
| 438 | 437 | static int |
| 439 | 438 | v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, |
| 440 | 439 | struct v3d_job *job, void (*free)(struct kref *ref), |
| 441 | − u32 in_sync) | |
| 440 | + u32 in_sync, enum v3d_queue queue) | |
| 442 | 441 | { |
| 443 | 442 | struct dma_fence *in_fence = NULL; |
| 443 | + struct v3d_file_priv *v3d_priv = file_priv->driver_priv; | |
| 444 | 444 | int ret; |
| 445 | 445 | |
| 446 | 446 | job->v3d = v3d; |
| @@ -450,44 +450,40 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, · 3 unmodified lines | ||
| 450 | 450 | if (ret < 0) |
| 451 | 451 | return ret; |
| 452 | 452 | |
| 453 | − xa_init_flags(&job->deps, XA_FLAGS_ALLOC); | |
| 453 | + ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], | |
| 454 | + v3d_priv); | |
| 455 | + if (ret) | |
| 456 | + goto fail; | |
| 454 | 457 | |
| 455 | 458 | ret = drm_syncobj_find_fence(file_priv, in_sync, 0, 0, &in_fence); |
| 456 | 459 | if (ret == -EINVAL) |
| 457 | − goto fail; | |
| 460 | + goto fail_job; | |
| 458 | 461 | |
| 459 | − ret = drm_gem_fence_array_add(&job->deps, in_fence); | |
| 462 | + ret = drm_sched_job_add_dependency(&job->base, in_fence); | |
| 460 | 463 | if (ret) |
| 461 | − goto fail; | |
| 464 | + goto fail_job; | |
| 462 | 465 | |
| 463 | 466 | kref_init(&job->refcount); |
| 464 | 467 | |
| 465 | 468 | return 0; |
| 469 | +fail_job: | |
| 470 | + drm_sched_job_cleanup(&job->base); | |
| 466 | 471 | fail: |
| 467 | − xa_destroy(&job->deps); | |
| 468 | 472 | pm_runtime_put_autosuspend(v3d->drm.dev); |
| 469 | 473 | return ret; |
| 470 | 474 | } |
| 471 | 475 | |
| 472 | −static int | |
| 473 | −v3d_push_job(struct v3d_file_priv *v3d_priv, | |
| 474 | − struct v3d_job *job, enum v3d_queue queue) | |
| 476 | +static void | |
| 477 | +v3d_push_job(struct v3d_job *job) | |
| 475 | 478 | { |
| 476 | − int ret; | |
| 477 | − | |
| 478 | − ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], | |
| 479 | − v3d_priv); | |
| 480 | − if (ret) | |
| 481 | − return ret; | |
| 479 | + drm_sched_job_arm(&job->base); | |
| 482 | 480 | |
| 483 | 481 | job->done_fence = dma_fence_get(&job->base.s_fence->finished); |
| 484 | 482 | |
| 485 | 483 | /* put by scheduler job completion */ |
| 486 | 484 | kref_get(&job->refcount); |
| 487 | 485 | |
| 488 | − drm_sched_entity_push_job(&job->base, &v3d_priv->sched_entity[queue]); | |
| 489 | − | |
| 490 | − return 0; | |
| 486 | + drm_sched_entity_push_job(&job->base); | |
| 491 | 487 | } |
| 492 | 488 | |
| 493 | 489 | static void |
| @@ -562,7 +558,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, · 68 unmodified lines | ||
| 562 | 558 | INIT_LIST_HEAD(&render->unref_list); |
| 563 | 559 | |
| 564 | 560 | ret = v3d_job_init(v3d, file_priv, &render->base, |
| 565 | − v3d_render_job_free, args->in_sync_rcl); | |
| 561 | + v3d_render_job_free, args->in_sync_rcl, V3D_RENDER); | |
| 566 | 562 | if (ret) { |
| 567 | 563 | kfree(render); |
| 568 | 564 | return ret; |
| @@ -576,7 +572,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, · 7 unmodified lines | ||
| 576 | 572 | } |
| 577 | 573 | |
| 578 | 574 | ret = v3d_job_init(v3d, file_priv, &bin->base, |
| 579 | − v3d_job_free, args->in_sync_bcl); | |
| 575 | + v3d_job_free, args->in_sync_bcl, V3D_BIN); | |
| 580 | 576 | if (ret) { |
| 581 | 577 | v3d_job_put(&render->base); |
| 582 | 578 | kfree(bin); |
| @@ -598,7 +594,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, · 15 unmodified lines | ||
| 598 | 594 | goto fail; |
| 599 | 595 | } |
| 600 | 596 | |
| 601 | − ret = v3d_job_init(v3d, file_priv, clean_job, v3d_job_free, 0); | |
| 597 | + ret = v3d_job_init(v3d, file_priv, clean_job, v3d_job_free, 0, V3D_CACHE_CLEAN); | |
| 602 | 598 | if (ret) { |
| 603 | 599 | kfree(clean_job); |
| 604 | 600 | clean_job = NULL; |
| @@ -633,31 +629,26 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, · 28 unmodified lines | ||
| 633 | 629 | if (bin) { |
| 634 | 630 | bin->base.perfmon = render->base.perfmon; |
| 635 | 631 | v3d_perfmon_get(bin->base.perfmon); |
| 636 | − ret = v3d_push_job(v3d_priv, &bin->base, V3D_BIN); | |
| 637 | − if (ret) | |
| 638 | − goto fail_unreserve; | |
| 632 | + v3d_push_job(&bin->base); | |
| 639 | 633 | |
| 640 | − ret = drm_gem_fence_array_add(&render->base.deps, | |
| 641 | − dma_fence_get(bin->base.done_fence)); | |
| 634 | + ret = drm_sched_job_add_dependency(&render->base.base, | |
| 635 | + dma_fence_get(bin->base.done_fence)); | |
| 642 | 636 | if (ret) |
| 643 | 637 | goto fail_unreserve; |
| 644 | 638 | } |
| 645 | 639 | |
| 646 | − ret = v3d_push_job(v3d_priv, &render->base, V3D_RENDER); | |
| 647 | − if (ret) | |
| 648 | − goto fail_unreserve; | |
| 640 | + v3d_push_job(&render->base); | |
| 649 | 641 | |
| 650 | 642 | if (clean_job) { |
| 651 | 643 | struct dma_fence *render_fence = |
| 652 | 644 | dma_fence_get(render->base.done_fence); |
| 653 | − ret = drm_gem_fence_array_add(&clean_job->deps, render_fence); | |
| 645 | + ret = drm_sched_job_add_dependency(&clean_job->base, | |
| 646 | + render_fence); | |
| 654 | 647 | if (ret) |
| 655 | 648 | goto fail_unreserve; |
| 656 | 649 | clean_job->perfmon = render->base.perfmon; |
| 657 | 650 | v3d_perfmon_get(clean_job->perfmon); |
| 658 | − ret = v3d_push_job(v3d_priv, clean_job, V3D_CACHE_CLEAN); | |
| 659 | − if (ret) | |
| 660 | − goto fail_unreserve; | |
| 651 | + v3d_push_job(clean_job); | |
| 661 | 652 | } |
| 662 | 653 | |
| 663 | 654 | mutex_unlock(&v3d->sched_lock); |
| @@ -682,10 +673,10 @@ fail_unreserve: · 18 unmodified lines | ||
| 682 | 673 | last_job->bo_count, &acquire_ctx); |
| 683 | 674 | fail: |
| 684 | 675 | if (bin) |
| 685 | − v3d_job_put(&bin->base); | |
| 686 | − v3d_job_put(&render->base); | |
| 676 | + v3d_job_cleanup(&bin->base); | |
| 677 | + v3d_job_cleanup(&render->base); | |
| 687 | 678 | if (clean_job) |
| 688 | − v3d_job_put(clean_job); | |
| 679 | + v3d_job_cleanup(clean_job); | |
| 689 | 680 | |
| 690 | 681 | return ret; |
| 691 | 682 | } |
| @@ -704,7 +695,6 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, · 12 unmodified lines | ||
| 704 | 695 | struct drm_file *file_priv) |
| 705 | 696 | { |
| 706 | 697 | struct v3d_dev *v3d = to_v3d_dev(dev); |
| 707 | − struct v3d_file_priv *v3d_priv = file_priv->driver_priv; | |
| 708 | 698 | struct drm_v3d_submit_tfu *args = data; |
| 709 | 699 | struct v3d_tfu_job *job; |
| 710 | 700 | struct ww_acquire_ctx acquire_ctx; |
| @@ -717,7 +707,7 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, · 6 unmodified lines | ||
| 717 | 707 | return -ENOMEM; |
| 718 | 708 | |
| 719 | 709 | ret = v3d_job_init(v3d, file_priv, &job->base, |
| 720 | − v3d_job_free, args->in_sync); | |
| 710 | + v3d_job_free, args->in_sync, V3D_TFU); | |
| 721 | 711 | if (ret) { |
| 722 | 712 | kfree(job); |
| 723 | 713 | return ret; |
| @@ -761,9 +751,7 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, · 37 unmodified lines | ||
| 761 | 751 | goto fail; |
| 762 | 752 | |
| 763 | 753 | mutex_lock(&v3d->sched_lock); |
| 764 | − ret = v3d_push_job(v3d_priv, &job->base, V3D_TFU); | |
| 765 | − if (ret) | |
| 766 | − goto fail_unreserve; | |
| 754 | + v3d_push_job(&job->base); | |
| 767 | 755 | mutex_unlock(&v3d->sched_lock); |
| 768 | 756 | |
| 769 | 757 | v3d_attach_fences_and_unlock_reservation(file_priv, |
| @@ -775,12 +763,8 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, · 5 unmodified lines | ||
| 775 | 763 | |
| 776 | 764 | return 0; |
| 777 | 765 | |
| 778 | −fail_unreserve: | |
| 779 | − mutex_unlock(&v3d->sched_lock); | |
| 780 | − drm_gem_unlock_reservations(job->base.bo, job->base.bo_count, | |
| 781 | − &acquire_ctx); | |
| 782 | 766 | fail: |
| 783 | − v3d_job_put(&job->base); | |
| 767 | + v3d_job_cleanup(&job->base); | |
| 784 | 768 | |
| 785 | 769 | return ret; |
| 786 | 770 | } |
| @@ -818,7 +802,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data, · 31 unmodified lines | ||
| 818 | 802 | return -ENOMEM; |
| 819 | 803 | |
| 820 | 804 | ret = v3d_job_init(v3d, file_priv, &job->base, |
| 821 | − v3d_job_free, args->in_sync); | |
| 805 | + v3d_job_free, args->in_sync, V3D_CSD); | |
| 822 | 806 | if (ret) { |
| 823 | 807 | kfree(job); |
| 824 | 808 | return ret; |
| @@ -831,7 +815,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data, · 6 unmodified lines | ||
| 831 | 815 | return -ENOMEM; |
| 832 | 816 | } |
| 833 | 817 | |
| 834 | − ret = v3d_job_init(v3d, file_priv, clean_job, v3d_job_free, 0); | |
| 818 | + ret = v3d_job_init(v3d, file_priv, clean_job, v3d_job_free, 0, V3D_CACHE_CLEAN); | |
| 835 | 819 | if (ret) { |
| 836 | 820 | v3d_job_put(&job->base); |
| 837 | 821 | kfree(clean_job); |
| @@ -859,18 +843,14 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data, · 21 unmodified lines | ||
| 859 | 843 | } |
| 860 | 844 | |
| 861 | 845 | mutex_lock(&v3d->sched_lock); |
| 862 | − ret = v3d_push_job(v3d_priv, &job->base, V3D_CSD); | |
| 863 | − if (ret) | |
| 864 | − goto fail_unreserve; | |
| 846 | + v3d_push_job(&job->base); | |
| 865 | 847 | |
| 866 | − ret = drm_gem_fence_array_add(&clean_job->deps, | |
| 867 | − dma_fence_get(job->base.done_fence)); | |
| 848 | + ret = drm_sched_job_add_dependency(&clean_job->base, | |
| 849 | + dma_fence_get(job->base.done_fence)); | |
| 868 | 850 | if (ret) |
| 869 | 851 | goto fail_unreserve; |
| 870 | 852 | |
| 871 | − ret = v3d_push_job(v3d_priv, clean_job, V3D_CACHE_CLEAN); | |
| 872 | − if (ret) | |
| 873 | − goto fail_unreserve; | |
| 853 | + v3d_push_job(clean_job); | |
| 874 | 854 | mutex_unlock(&v3d->sched_lock); |
| 875 | 855 | |
| 876 | 856 | v3d_attach_fences_and_unlock_reservation(file_priv, |
| @@ -889,8 +869,8 @@ fail_unreserve: · 12 unmodified lines | ||
| 889 | 869 | drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count, |
| 890 | 870 | &acquire_ctx); |
| 891 | 871 | fail: |
| 892 | − v3d_job_put(&job->base); | |
| 893 | − v3d_job_put(clean_job); | |
| 872 | + v3d_job_cleanup(&job->base); | |
| 873 | + v3d_job_cleanup(clean_job); | |
| 894 | 874 | |
| 895 | 875 | return ret; |
| 896 | 876 | } |
| @@ -13,7 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | * jobs when bulk background jobs are queued up, we submit a new job |
| 14 | 14 | * to the HW only when it has completed the last one, instead of |
| 15 | 15 | * filling up the CT[01]Q FIFOs with jobs. Similarly, we use |
| 16 | − * v3d_job_dependency() to manage the dependency between bin and | |
| 16 | + * drm_sched_job_add_dependency() to manage the dependency between bin and | |
| 17 | 17 | * render, instead of having the clients submit jobs using the HW's |
| 18 | 18 | * semaphores to interlock between them. |
| 19 | 19 | */ |
| @@ -55,12 +55,11 @@ to_csd_job(struct drm_sched_job *sched_job) · 35 unmodified lines | ||
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | static void |
| 58 | −v3d_job_free(struct drm_sched_job *sched_job) | |
| 58 | +v3d_sched_job_free(struct drm_sched_job *sched_job) | |
| 59 | 59 | { |
| 60 | 60 | struct v3d_job *job = to_v3d_job(sched_job); |
| 61 | 61 | |
| 62 | − drm_sched_job_cleanup(sched_job); | |
| 63 | − v3d_job_put(job); | |
| 62 | + v3d_job_cleanup(job); | |
| 64 | 63 | } |
| 65 | 64 | |
| 66 | 65 | static void |
| @@ -73,28 +72,6 @@ v3d_switch_perfmon(struct v3d_dev *v3d, struct v3d_job *job) · 6 unmodified lines | ||
| 73 | 72 | v3d_perfmon_start(v3d, job->perfmon); |
| 74 | 73 | } |
| 75 | 74 | |
| 76 | −/* | |
| 77 | − * Returns the fences that the job depends on, one by one. | |
| 78 | − * | |
| 79 | − * If placed in the scheduler's .dependency method, the corresponding | |
| 80 | − * .run_job won't be called until all of them have been signaled. | |
| 81 | − */ | |
| 82 | −static struct dma_fence * | |
| 83 | −v3d_job_dependency(struct drm_sched_job *sched_job, | |
| 84 | − struct drm_sched_entity *s_entity) | |
| 85 | −{ | |
| 86 | − struct v3d_job *job = to_v3d_job(sched_job); | |
| 87 | − | |
| 88 | − /* XXX: Wait on a fence for switching the GMP if necessary, | |
| 89 | − * and then do so. | |
| 90 | − */ | |
| 91 | − | |
| 92 | − if (!xa_empty(&job->deps)) | |
| 93 | − return xa_erase(&job->deps, job->last_dep++); | |
| 94 | − | |
| 95 | − return NULL; | |
| 96 | −} | |
| 97 | − | |
| 98 | 75 | static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) |
| 99 | 76 | { |
| 100 | 77 | struct v3d_bin_job *job = to_bin_job(sched_job); |
| @@ -373,38 +350,33 @@ v3d_csd_job_timedout(struct drm_sched_job *sched_job) · 272 unmodified lines | ||
| 373 | 350 | } |
| 374 | 351 | |
| 375 | 352 | static const struct drm_sched_backend_ops v3d_bin_sched_ops = { |
| 376 | − .dependency = v3d_job_dependency, | |
| 377 | 353 | .run_job = v3d_bin_job_run, |
| 378 | 354 | .timedout_job = v3d_bin_job_timedout, |
| 379 | − .free_job = v3d_job_free, | |
| 355 | + .free_job = v3d_sched_job_free, | |
| 380 | 356 | }; |
| 381 | 357 | |
| 382 | 358 | static const struct drm_sched_backend_ops v3d_render_sched_ops = { |
| 383 | − .dependency = v3d_job_dependency, | |
| 384 | 359 | .run_job = v3d_render_job_run, |
| 385 | 360 | .timedout_job = v3d_render_job_timedout, |
| 386 | − .free_job = v3d_job_free, | |
| 361 | + .free_job = v3d_sched_job_free, | |
| 387 | 362 | }; |
| 388 | 363 | |
| 389 | 364 | static const struct drm_sched_backend_ops v3d_tfu_sched_ops = { |
| 390 | − .dependency = v3d_job_dependency, | |
| 391 | 365 | .run_job = v3d_tfu_job_run, |
| 392 | 366 | .timedout_job = v3d_generic_job_timedout, |
| 393 | − .free_job = v3d_job_free, | |
| 367 | + .free_job = v3d_sched_job_free, | |
| 394 | 368 | }; |
| 395 | 369 | |
| 396 | 370 | static const struct drm_sched_backend_ops v3d_csd_sched_ops = { |
| 397 | − .dependency = v3d_job_dependency, | |
| 398 | 371 | .run_job = v3d_csd_job_run, |
| 399 | 372 | .timedout_job = v3d_csd_job_timedout, |
| 400 | − .free_job = v3d_job_free | |
| 373 | + .free_job = v3d_sched_job_free | |
| 401 | 374 | }; |
| 402 | 375 | |
| 403 | 376 | static const struct drm_sched_backend_ops v3d_cache_clean_sched_ops = { |
| 404 | − .dependency = v3d_job_dependency, | |
| 405 | 377 | .run_job = v3d_cache_clean_job_run, |
| 406 | 378 | .timedout_job = v3d_generic_job_timedout, |
| 407 | − .free_job = v3d_job_free | |
| 379 | + .free_job = v3d_sched_job_free | |
| 408 | 380 | }; |
| 409 | 381 | |
| 410 | 382 | int |
| @@ -229,26 +229,19 @@ static const struct of_device_id vc4_dpi_dt_match[] = { · 228 unmodified lines | ||
| 229 | 229 | static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) |
| 230 | 230 | { |
| 231 | 231 | struct device *dev = &dpi->pdev->dev; |
| 232 | − struct drm_panel *panel; | |
| 233 | 232 | struct drm_bridge *bridge; |
| 234 | − int ret; | |
| 235 | 233 | |
| 236 | − ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, | |
| 237 | − &panel, &bridge); | |
| 238 | − if (ret) { | |
| 234 | + bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); | |
| 235 | + if (IS_ERR(bridge)) { | |
| 239 | 236 | /* If nothing was connected in the DT, that's not an |
| 240 | 237 | * error. |
| 241 | 238 | */ |
| 242 | − if (ret == -ENODEV) | |
| 239 | + if (PTR_ERR(bridge) == -ENODEV) | |
| 243 | 240 | return 0; |
| 244 | 241 | else |
| 245 | − return ret; | |
| 242 | + return PTR_ERR(bridge); | |
| 246 | 243 | } |
| 247 | 244 | |
| 248 | − if (panel) | |
| 249 | − bridge = drm_panel_bridge_add_typed(panel, | |
| 250 | − DRM_MODE_CONNECTOR_DPI); | |
| 251 | − | |
| 252 | 245 | return drm_bridge_attach(dpi->encoder, bridge, NULL, 0); |
| 253 | 246 | } |
| 254 | 247 | |
| @@ -50,13 +50,11 @@ · 49 unmodified lines | ||
| 50 | 50 | #define DRIVER_PATCHLEVEL 0 |
| 51 | 51 | |
| 52 | 52 | /* Helper function for mapping the regs on a platform device. */ |
| 53 | −void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index) | |
| 53 | +void __iomem *vc4_ioremap_regs(struct platform_device *pdev, int index) | |
| 54 | 54 | { |
| 55 | − struct resource *res; | |
| 56 | 55 | void __iomem *map; |
| 57 | 56 | |
| 58 | − res = platform_get_resource(dev, IORESOURCE_MEM, index); | |
| 59 | − map = devm_ioremap_resource(&dev->dev, res); | |
| 57 | + map = devm_platform_ioremap_resource(pdev, index); | |
| 60 | 58 | if (IS_ERR(map)) |
| 61 | 59 | return map; |
| 62 | 60 | |
| @@ -1497,7 +1497,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) · 1496 unmodified lines | ||
| 1497 | 1497 | struct drm_device *drm = dev_get_drvdata(master); |
| 1498 | 1498 | struct vc4_dsi *dsi = dev_get_drvdata(dev); |
| 1499 | 1499 | struct vc4_dsi_encoder *vc4_dsi_encoder; |
| 1500 | − struct drm_panel *panel; | |
| 1501 | 1500 | const struct of_device_id *match; |
| 1502 | 1501 | dma_cap_mask_t dma_mask; |
| 1503 | 1502 | int ret; |
| @@ -1609,27 +1608,9 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) · 105 unmodified lines | ||
| 1609 | 1608 | return ret; |
| 1610 | 1609 | } |
| 1611 | 1610 | |
| 1612 | − ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, | |
| 1613 | − &panel, &dsi->bridge); | |
| 1614 | − if (ret) { | |
| 1615 | − /* If the bridge or panel pointed by dev->of_node is not | |
| 1616 | − * enabled, just return 0 here so that we don't prevent the DRM | |
| 1617 | − * dev from being registered. Of course that means the DSI | |
| 1618 | − * encoder won't be exposed, but that's not a problem since | |
| 1619 | − * nothing is connected to it. | |
| 1620 | − */ | |
| 1621 | − if (ret == -ENODEV) | |
| 1622 | − return 0; | |
| 1623 | − | |
| 1624 | − return ret; | |
| 1625 | − } | |
| 1626 | − | |
| 1627 | − if (panel) { | |
| 1628 | − dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel, | |
| 1629 | − DRM_MODE_CONNECTOR_DSI); | |
| 1630 | − if (IS_ERR(dsi->bridge)) | |
| 1631 | − return PTR_ERR(dsi->bridge); | |
| 1632 | − } | |
| 1611 | + dsi->bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); | |
| 1612 | + if (IS_ERR(dsi->bridge)) | |
| 1613 | + return PTR_ERR(dsi->bridge); | |
| 1633 | 1614 | |
| 1634 | 1615 | /* The esc clock rate is supposed to always be 100Mhz. */ |
| 1635 | 1616 | ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); |
| @@ -1667,8 +1648,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, · 31 unmodified lines | ||
| 1667 | 1648 | { |
| 1668 | 1649 | struct vc4_dsi *dsi = dev_get_drvdata(dev); |
| 1669 | 1650 | |
| 1670 | − if (dsi->bridge) | |
| 1671 | − pm_runtime_disable(dev); | |
| 1651 | + pm_runtime_disable(dev); | |
| 1672 | 1652 | |
| 1673 | 1653 | /* |
| 1674 | 1654 | * Restore the bridge_chain so the bridge detach procedure can happen |
| @@ -38,6 +38,7 @@ · 37 unmodified lines | ||
| 38 | 38 | |
| 39 | 39 | #include <drm/drm_drv.h> |
| 40 | 40 | #include <drm/drm_file.h> |
| 41 | +#include <drm/drm_gem_shmem_helper.h> | |
| 41 | 42 | #include <drm/drm_ioctl.h> |
| 42 | 43 | #include <drm/drm_managed.h> |
| 43 | 44 | #include <drm/drm_prime.h> |
| @@ -50,87 +51,11 @@ · 6 unmodified lines | ||
| 50 | 51 | #define DRIVER_MAJOR 1 |
| 51 | 52 | #define DRIVER_MINOR 0 |
| 52 | 53 | |
| 53 | −static const struct drm_gem_object_funcs vgem_gem_object_funcs; | |
| 54 | − | |
| 55 | 54 | static struct vgem_device { |
| 56 | 55 | struct drm_device drm; |
| 57 | 56 | struct platform_device *platform; |
| 58 | 57 | } *vgem_device; |
| 59 | 58 | |
| 60 | −static void vgem_gem_free_object(struct drm_gem_object *obj) | |
| 61 | −{ | |
| 62 | − struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj); | |
| 63 | − | |
| 64 | − kvfree(vgem_obj->pages); | |
| 65 | − mutex_destroy(&vgem_obj->pages_lock); | |
| 66 | − | |
| 67 | − if (obj->import_attach) | |
| 68 | − drm_prime_gem_destroy(obj, vgem_obj->table); | |
| 69 | − | |
| 70 | − drm_gem_object_release(obj); | |
| 71 | − kfree(vgem_obj); | |
| 72 | −} | |
| 73 | − | |
| 74 | −static vm_fault_t vgem_gem_fault(struct vm_fault *vmf) | |
| 75 | −{ | |
| 76 | − struct vm_area_struct *vma = vmf->vma; | |
| 77 | − struct drm_vgem_gem_object *obj = vma->vm_private_data; | |
| 78 | − /* We don't use vmf->pgoff since that has the fake offset */ | |
| 79 | − unsigned long vaddr = vmf->address; | |
| 80 | − vm_fault_t ret = VM_FAULT_SIGBUS; | |
| 81 | − loff_t num_pages; | |
| 82 | − pgoff_t page_offset; | |
| 83 | − page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT; | |
| 84 | − | |
| 85 | − num_pages = DIV_ROUND_UP(obj->base.size, PAGE_SIZE); | |
| 86 | − | |
| 87 | − if (page_offset >= num_pages) | |
| 88 | − return VM_FAULT_SIGBUS; | |
| 89 | − | |
| 90 | − mutex_lock(&obj->pages_lock); | |
| 91 | − if (obj->pages) { | |
| 92 | − get_page(obj->pages[page_offset]); | |
| 93 | − vmf->page = obj->pages[page_offset]; | |
| 94 | − ret = 0; | |
| 95 | − } | |
| 96 | − mutex_unlock(&obj->pages_lock); | |
| 97 | − if (ret) { | |
| 98 | − struct page *page; | |
| 99 | − | |
| 100 | − page = shmem_read_mapping_page( | |
| 101 | − file_inode(obj->base.filp)->i_mapping, | |
| 102 | − page_offset); | |
| 103 | − if (!IS_ERR(page)) { | |
| 104 | − vmf->page = page; | |
| 105 | − ret = 0; | |
| 106 | − } else switch (PTR_ERR(page)) { | |
| 107 | − case -ENOSPC: | |
| 108 | − case -ENOMEM: | |
| 109 | − ret = VM_FAULT_OOM; | |
| 110 | − break; | |
| 111 | − case -EBUSY: | |
| 112 | − ret = VM_FAULT_RETRY; | |
| 113 | − break; | |
| 114 | − case -EFAULT: | |
| 115 | − case -EINVAL: | |
| 116 | − ret = VM_FAULT_SIGBUS; | |
| 117 | − break; | |
| 118 | − default: | |
| 119 | − WARN_ON(PTR_ERR(page)); | |
| 120 | − ret = VM_FAULT_SIGBUS; | |
| 121 | − break; | |
| 122 | − } | |
| 123 | − | |
| 124 | − } | |
| 125 | − return ret; | |
| 126 | −} | |
| 127 | − | |
| 128 | −static const struct vm_operations_struct vgem_gem_vm_ops = { | |
| 129 | − .fault = vgem_gem_fault, | |
| 130 | − .open = drm_gem_vm_open, | |
| 131 | − .close = drm_gem_vm_close, | |
| 132 | −}; | |
| 133 | − | |
| 134 | 59 | static int vgem_open(struct drm_device *dev, struct drm_file *file) |
| 135 | 60 | { |
| 136 | 61 | struct vgem_file *vfile; |
| @@ -159,266 +84,30 @@ static void vgem_postclose(struct drm_device *dev, struct drm_file *file) · 22 unmodified lines | ||
| 159 | 84 | kfree(vfile); |
| 160 | 85 | } |
| 161 | 86 | |
| 162 | −static struct drm_vgem_gem_object *__vgem_gem_create(struct drm_device *dev, | |
| 163 | − unsigned long size) | |
| 164 | −{ | |
| 165 | − struct drm_vgem_gem_object *obj; | |
| 166 | − int ret; | |
| 167 | − | |
| 168 | − obj = kzalloc(sizeof(*obj), GFP_KERNEL); | |
| 169 | − if (!obj) | |
| 170 | − return ERR_PTR(-ENOMEM); | |
| 171 | − | |
| 172 | − obj->base.funcs = &vgem_gem_object_funcs; | |
| 173 | − | |
| 174 | − ret = drm_gem_object_init(dev, &obj->base, roundup(size, PAGE_SIZE)); | |
| 175 | − if (ret) { | |
| 176 | − kfree(obj); | |
| 177 | − return ERR_PTR(ret); | |
| 178 | − } | |
| 179 | − | |
| 180 | − mutex_init(&obj->pages_lock); | |
| 181 | − | |
| 182 | − return obj; | |
| 183 | −} | |
| 184 | − | |
| 185 | −static void __vgem_gem_destroy(struct drm_vgem_gem_object *obj) | |
| 186 | −{ | |
| 187 | − drm_gem_object_release(&obj->base); | |
| 188 | − kfree(obj); | |
| 189 | −} | |
| 190 | − | |
| 191 | −static struct drm_gem_object *vgem_gem_create(struct drm_device *dev, | |
| 192 | − struct drm_file *file, | |
| 193 | − unsigned int *handle, | |
| 194 | − unsigned long size) | |
| 195 | −{ | |
| 196 | − struct drm_vgem_gem_object *obj; | |
| 197 | − int ret; | |
| 198 | − | |
| 199 | − obj = __vgem_gem_create(dev, size); | |
| 200 | − if (IS_ERR(obj)) | |
| 201 | − return ERR_CAST(obj); | |
| 202 | − | |
| 203 | − ret = drm_gem_handle_create(file, &obj->base, handle); | |
| 204 | − if (ret) { | |
| 205 | − drm_gem_object_put(&obj->base); | |
| 206 | − return ERR_PTR(ret); | |
| 207 | − } | |
| 208 | − | |
| 209 | − return &obj->base; | |
| 210 | −} | |
| 211 | − | |
| 212 | −static int vgem_gem_dumb_create(struct drm_file *file, struct drm_device *dev, | |
| 213 | − struct drm_mode_create_dumb *args) | |
| 214 | −{ | |
| 215 | − struct drm_gem_object *gem_object; | |
| 216 | − u64 pitch, size; | |
| 217 | − | |
| 218 | − pitch = args->width * DIV_ROUND_UP(args->bpp, 8); | |
| 219 | − size = args->height * pitch; | |
| 220 | − if (size == 0) | |
| 221 | − return -EINVAL; | |
| 222 | − | |
| 223 | − gem_object = vgem_gem_create(dev, file, &args->handle, size); | |
| 224 | − if (IS_ERR(gem_object)) | |
| 225 | − return PTR_ERR(gem_object); | |
| 226 | − | |
| 227 | − args->size = gem_object->size; | |
| 228 | − args->pitch = pitch; | |
| 229 | − | |
| 230 | − drm_gem_object_put(gem_object); | |
| 231 | − | |
| 232 | − DRM_DEBUG("Created object of size %llu\n", args->size); | |
| 233 | − | |
| 234 | − return 0; | |
| 235 | −} | |
| 236 | − | |
| 237 | 87 | static struct drm_ioctl_desc vgem_ioctls[] = { |
| 238 | 88 | DRM_IOCTL_DEF_DRV(VGEM_FENCE_ATTACH, vgem_fence_attach_ioctl, DRM_RENDER_ALLOW), |
| 239 | 89 | DRM_IOCTL_DEF_DRV(VGEM_FENCE_SIGNAL, vgem_fence_signal_ioctl, DRM_RENDER_ALLOW), |
| 240 | 90 | }; |
| 241 | 91 | |
| 242 | −static int vgem_mmap(struct file *filp, struct vm_area_struct *vma) | |
| 243 | −{ | |
| 244 | − unsigned long flags = vma->vm_flags; | |
| 245 | − int ret; | |
| 246 | − | |
| 247 | − ret = drm_gem_mmap(filp, vma); | |
| 248 | − if (ret) | |
| 249 | − return ret; | |
| 250 | − | |
| 251 | − /* Keep the WC mmaping set by drm_gem_mmap() but our pages | |
| 252 | − * are ordinary and not special. | |
| 253 | − */ | |
| 254 | − vma->vm_flags = flags | VM_DONTEXPAND | VM_DONTDUMP; | |
| 255 | − return 0; | |
| 256 | −} | |
| 92 | +DEFINE_DRM_GEM_FOPS(vgem_driver_fops); | |
| 257 | 93 | |
| 258 | −static const struct file_operations vgem_driver_fops = { | |
| 259 | − .owner = THIS_MODULE, | |
| 260 | − .open = drm_open, | |
| 261 | − .mmap = vgem_mmap, | |
| 262 | − .poll = drm_poll, | |
| 263 | − .read = drm_read, | |
| 264 | − .unlocked_ioctl = drm_ioctl, | |
| 265 | − .compat_ioctl = drm_compat_ioctl, | |
| 266 | − .release = drm_release, | |
| 267 | −}; | |
| 268 | − | |
| 269 | −static struct page **vgem_pin_pages(struct drm_vgem_gem_object *bo) | |
| 270 | −{ | |
| 271 | − mutex_lock(&bo->pages_lock); | |
| 272 | − if (bo->pages_pin_count++ == 0) { | |
| 273 | − struct page **pages; | |
| 274 | − | |
| 275 | − pages = drm_gem_get_pages(&bo->base); | |
| 276 | − if (IS_ERR(pages)) { | |
| 277 | − bo->pages_pin_count--; | |
| 278 | − mutex_unlock(&bo->pages_lock); | |
| 279 | − return pages; | |
| 280 | − } | |
| 281 | − | |
| 282 | − bo->pages = pages; | |
| 283 | − } | |
| 284 | − mutex_unlock(&bo->pages_lock); | |
| 285 | − | |
| 286 | − return bo->pages; | |
| 287 | −} | |
| 288 | − | |
| 289 | −static void vgem_unpin_pages(struct drm_vgem_gem_object *bo) | |
| 290 | −{ | |
| 291 | − mutex_lock(&bo->pages_lock); | |
| 292 | − if (--bo->pages_pin_count == 0) { | |
| 293 | − drm_gem_put_pages(&bo->base, bo->pages, true, true); | |
| 294 | − bo->pages = NULL; | |
| 295 | − } | |
| 296 | − mutex_unlock(&bo->pages_lock); | |
| 297 | −} | |
| 298 | − | |
| 299 | −static int vgem_prime_pin(struct drm_gem_object *obj) | |
| 94 | +static struct drm_gem_object *vgem_gem_create_object(struct drm_device *dev, size_t size) | |
| 300 | 95 | { |
| 301 | − struct drm_vgem_gem_object *bo = to_vgem_bo(obj); | |
| 302 | − long n_pages = obj->size >> PAGE_SHIFT; | |
| 303 | − struct page **pages; | |
| 96 | + struct drm_gem_shmem_object *obj; | |
| 304 | 97 | |
| 305 | − pages = vgem_pin_pages(bo); | |
| 306 | − if (IS_ERR(pages)) | |
| 307 | − return PTR_ERR(pages); | |
| 98 | + obj = kzalloc(sizeof(*obj), GFP_KERNEL); | |
| 99 | + if (!obj) | |
| 100 | + return NULL; | |
| 308 | 101 | |
| 309 | − /* Flush the object from the CPU cache so that importers can rely | |
| 310 | − * on coherent indirect access via the exported dma-address. | |
| 102 | + /* | |
| 103 | + * vgem doesn't have any begin/end cpu access ioctls, therefore must use | |
| 104 | + * coherent memory or dma-buf sharing just wont work. | |
| 311 | 105 | */ |
| 312 | − drm_clflush_pages(pages, n_pages); | |
| 313 | − | |
| 314 | − return 0; | |
| 315 | −} | |
| 316 | − | |
| 317 | −static void vgem_prime_unpin(struct drm_gem_object *obj) | |
| 318 | −{ | |
| 319 | − struct drm_vgem_gem_object *bo = to_vgem_bo(obj); | |
| 320 | − | |
| 321 | − vgem_unpin_pages(bo); | |
| 322 | −} | |
| 323 | − | |
| 324 | −static struct sg_table *vgem_prime_get_sg_table(struct drm_gem_object *obj) | |
| 325 | −{ | |
| 326 | − struct drm_vgem_gem_object *bo = to_vgem_bo(obj); | |
| 327 | − | |
| 328 | − return drm_prime_pages_to_sg(obj->dev, bo->pages, bo->base.size >> PAGE_SHIFT); | |
| 329 | −} | |
| 330 | − | |
| 331 | −static struct drm_gem_object* vgem_prime_import(struct drm_device *dev, | |
| 332 | − struct dma_buf *dma_buf) | |
| 333 | −{ | |
| 334 | − struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm); | |
| 335 | − | |
| 336 | − return drm_gem_prime_import_dev(dev, dma_buf, &vgem->platform->dev); | |
| 337 | −} | |
| 338 | − | |
| 339 | −static struct drm_gem_object *vgem_prime_import_sg_table(struct drm_device *dev, | |
| 340 | − struct dma_buf_attachment *attach, struct sg_table *sg) | |
| 341 | −{ | |
| 342 | − struct drm_vgem_gem_object *obj; | |
| 343 | − int npages; | |
| 344 | − | |
| 345 | − obj = __vgem_gem_create(dev, attach->dmabuf->size); | |
| 346 | − if (IS_ERR(obj)) | |
| 347 | − return ERR_CAST(obj); | |
| 106 | + obj->map_wc = true; | |
| 348 | 107 | |
| 349 | − npages = PAGE_ALIGN(attach->dmabuf->size) / PAGE_SIZE; | |
| 350 | − | |
| 351 | − obj->table = sg; | |
| 352 | − obj->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); | |
| 353 | − if (!obj->pages) { | |
| 354 | − __vgem_gem_destroy(obj); | |
| 355 | − return ERR_PTR(-ENOMEM); | |
| 356 | − } | |
| 357 | − | |
| 358 | − obj->pages_pin_count++; /* perma-pinned */ | |
| 359 | − drm_prime_sg_to_page_array(obj->table, obj->pages, npages); | |
| 360 | 108 | return &obj->base; |
| 361 | 109 | } |
| 362 | 110 | |
| 363 | −static int vgem_prime_vmap(struct drm_gem_object *obj, struct dma_buf_map *map) | |
| 364 | −{ | |
| 365 | − struct drm_vgem_gem_object *bo = to_vgem_bo(obj); | |
| 366 | − long n_pages = obj->size >> PAGE_SHIFT; | |
| 367 | − struct page **pages; | |
| 368 | − void *vaddr; | |
| 369 | − | |
| 370 | − pages = vgem_pin_pages(bo); | |
| 371 | − if (IS_ERR(pages)) | |
| 372 | − return PTR_ERR(pages); | |
| 373 | − | |
| 374 | − vaddr = vmap(pages, n_pages, 0, pgprot_writecombine(PAGE_KERNEL)); | |
| 375 | − if (!vaddr) | |
| 376 | − return -ENOMEM; | |
| 377 | − dma_buf_map_set_vaddr(map, vaddr); | |
| 378 | − | |
| 379 | − return 0; | |
| 380 | −} | |
| 381 | − | |
| 382 | −static void vgem_prime_vunmap(struct drm_gem_object *obj, struct dma_buf_map *map) | |
| 383 | −{ | |
| 384 | − struct drm_vgem_gem_object *bo = to_vgem_bo(obj); | |
| 385 | − | |
| 386 | − vunmap(map->vaddr); | |
| 387 | − vgem_unpin_pages(bo); | |
| 388 | −} | |
| 389 | − | |
| 390 | −static int vgem_prime_mmap(struct drm_gem_object *obj, | |
| 391 | − struct vm_area_struct *vma) | |
| 392 | −{ | |
| 393 | − int ret; | |
| 394 | − | |
| 395 | − if (obj->size < vma->vm_end - vma->vm_start) | |
| 396 | − return -EINVAL; | |
| 397 | − | |
| 398 | − if (!obj->filp) | |
| 399 | − return -ENODEV; | |
| 400 | − | |
| 401 | − ret = call_mmap(obj->filp, vma); | |
| 402 | − if (ret) | |
| 403 | − return ret; | |
| 404 | − | |
| 405 | − vma_set_file(vma, obj->filp); | |
| 406 | − vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; | |
| 407 | − vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); | |
| 408 | − | |
| 409 | − return 0; | |
| 410 | −} | |
| 411 | − | |
| 412 | −static const struct drm_gem_object_funcs vgem_gem_object_funcs = { | |
| 413 | − .free = vgem_gem_free_object, | |
| 414 | − .pin = vgem_prime_pin, | |
| 415 | − .unpin = vgem_prime_unpin, | |
| 416 | − .get_sg_table = vgem_prime_get_sg_table, | |
| 417 | − .vmap = vgem_prime_vmap, | |
| 418 | − .vunmap = vgem_prime_vunmap, | |
| 419 | − .vm_ops = &vgem_gem_vm_ops, | |
| 420 | −}; | |
| 421 | − | |
| 422 | 111 | static const struct drm_driver vgem_driver = { |
| 423 | 112 | .driver_features = DRIVER_GEM | DRIVER_RENDER, |
| 424 | 113 | .open = vgem_open, |
| @@ -427,13 +116,8 @@ static const struct drm_driver vgem_driver = { · 2 unmodified lines | ||
| 427 | 116 | .num_ioctls = ARRAY_SIZE(vgem_ioctls), |
| 428 | 117 | .fops = &vgem_driver_fops, |
| 429 | 118 | |
| 430 | − .dumb_create = vgem_gem_dumb_create, | |
| 431 | − | |
| 432 | − .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | |
| 433 | − .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
| 434 | − .gem_prime_import = vgem_prime_import, | |
| 435 | − .gem_prime_import_sg_table = vgem_prime_import_sg_table, | |
| 436 | − .gem_prime_mmap = vgem_prime_mmap, | |
| 119 | + DRM_GEM_SHMEM_DRIVER_OPS, | |
| 120 | + .gem_create_object = vgem_gem_create_object, | |
| 437 | 121 | |
| 438 | 122 | .name = DRIVER_NAME, |
| 439 | 123 | .desc = DRIVER_DESC, |
| @@ -26,6 +26,7 @@ · 25 unmodified lines | ||
| 26 | 26 | #ifndef VIRTIO_DRV_H |
| 27 | 27 | #define VIRTIO_DRV_H |
| 28 | 28 | |
| 29 | +#include <linux/dma-direction.h> | |
| 29 | 30 | #include <linux/virtio.h> |
| 30 | 31 | #include <linux/virtio_ids.h> |
| 31 | 32 | #include <linux/virtio_config.h> |
| @@ -459,4 +460,11 @@ bool virtio_gpu_is_vram(struct virtio_gpu_object *bo); · 427 unmodified lines | ||
| 459 | 460 | int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev, |
| 460 | 461 | struct virtio_gpu_object_params *params, |
| 461 | 462 | struct virtio_gpu_object **bo_ptr); |
| 463 | +struct sg_table *virtio_gpu_vram_map_dma_buf(struct virtio_gpu_object *bo, | |
| 464 | + struct device *dev, | |
| 465 | + enum dma_data_direction dir); | |
| 466 | +void virtio_gpu_vram_unmap_dma_buf(struct device *dev, | |
| 467 | + struct sg_table *sgt, | |
| 468 | + enum dma_data_direction dir); | |
| 469 | + | |
| 462 | 470 | #endif |
| @@ -43,13 +43,41 @@ static int virtgpu_virtio_get_uuid(struct dma_buf *buf, · 42 unmodified lines | ||
| 43 | 43 | return 0; |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | +static struct sg_table * | |
| 47 | +virtgpu_gem_map_dma_buf(struct dma_buf_attachment *attach, | |
| 48 | + enum dma_data_direction dir) | |
| 49 | +{ | |
| 50 | + struct drm_gem_object *obj = attach->dmabuf->priv; | |
| 51 | + struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj); | |
| 52 | + | |
| 53 | + if (virtio_gpu_is_vram(bo)) | |
| 54 | + return virtio_gpu_vram_map_dma_buf(bo, attach->dev, dir); | |
| 55 | + | |
| 56 | + return drm_gem_map_dma_buf(attach, dir); | |
| 57 | +} | |
| 58 | + | |
| 59 | +static void virtgpu_gem_unmap_dma_buf(struct dma_buf_attachment *attach, | |
| 60 | + struct sg_table *sgt, | |
| 61 | + enum dma_data_direction dir) | |
| 62 | +{ | |
| 63 | + struct drm_gem_object *obj = attach->dmabuf->priv; | |
| 64 | + struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj); | |
| 65 | + | |
| 66 | + if (virtio_gpu_is_vram(bo)) { | |
| 67 | + virtio_gpu_vram_unmap_dma_buf(attach->dev, sgt, dir); | |
| 68 | + return; | |
| 69 | + } | |
| 70 | + | |
| 71 | + drm_gem_unmap_dma_buf(attach, sgt, dir); | |
| 72 | +} | |
| 73 | + | |
| 46 | 74 | static const struct virtio_dma_buf_ops virtgpu_dmabuf_ops = { |
| 47 | 75 | .ops = { |
| 48 | 76 | .cache_sgt_mapping = true, |
| 49 | 77 | .attach = virtio_dma_buf_attach, |
| 50 | 78 | .detach = drm_gem_map_detach, |
| 51 | − .map_dma_buf = drm_gem_map_dma_buf, | |
| 52 | − .unmap_dma_buf = drm_gem_unmap_dma_buf, | |
| 79 | + .map_dma_buf = virtgpu_gem_map_dma_buf, | |
| 80 | + .unmap_dma_buf = virtgpu_gem_unmap_dma_buf, | |
| 53 | 81 | .release = drm_gem_dmabuf_release, |
| 54 | 82 | .mmap = drm_gem_dmabuf_mmap, |
| 55 | 83 | .vmap = drm_gem_dmabuf_vmap, |
| @@ -1,6 +1,8 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | #include "virtgpu_drv.h" |
| 3 | 3 | |
| 4 | +#include <linux/dma-mapping.h> | |
| 5 | + | |
| 4 | 6 | static void virtio_gpu_vram_free(struct drm_gem_object *obj) |
| 5 | 7 | { |
| 6 | 8 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj); |
| @@ -64,6 +66,65 @@ static int virtio_gpu_vram_mmap(struct drm_gem_object *obj, · 57 unmodified lines | ||
| 64 | 66 | return ret; |
| 65 | 67 | } |
| 66 | 68 | |
| 69 | +struct sg_table *virtio_gpu_vram_map_dma_buf(struct virtio_gpu_object *bo, | |
| 70 | + struct device *dev, | |
| 71 | + enum dma_data_direction dir) | |
| 72 | +{ | |
| 73 | + struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; | |
| 74 | + struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo); | |
| 75 | + struct sg_table *sgt; | |
| 76 | + dma_addr_t addr; | |
| 77 | + int ret; | |
| 78 | + | |
| 79 | + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | |
| 80 | + if (!sgt) | |
| 81 | + return ERR_PTR(-ENOMEM); | |
| 82 | + | |
| 83 | + if (!(bo->blob_flags & VIRTGPU_BLOB_FLAG_USE_MAPPABLE)) { | |
| 84 | + // Virtio devices can access the dma-buf via its UUID. Return a stub | |
| 85 | + // sg_table so the dma-buf API still works. | |
| 86 | + if (!is_virtio_device(dev) || !vgdev->has_resource_assign_uuid) { | |
| 87 | + ret = -EIO; | |
| 88 | + goto out; | |
| 89 | + } | |
| 90 | + return sgt; | |
| 91 | + } | |
| 92 | + | |
| 93 | + ret = sg_alloc_table(sgt, 1, GFP_KERNEL); | |
| 94 | + if (ret) | |
| 95 | + goto out; | |
| 96 | + | |
| 97 | + addr = dma_map_resource(dev, vram->vram_node.start, | |
| 98 | + vram->vram_node.size, dir, | |
| 99 | + DMA_ATTR_SKIP_CPU_SYNC); | |
| 100 | + ret = dma_mapping_error(dev, addr); | |
| 101 | + if (ret) | |
| 102 | + goto out; | |
| 103 | + | |
| 104 | + sg_set_page(sgt->sgl, NULL, vram->vram_node.size, 0); | |
| 105 | + sg_dma_address(sgt->sgl) = addr; | |
| 106 | + sg_dma_len(sgt->sgl) = vram->vram_node.size; | |
| 107 | + | |
| 108 | + return sgt; | |
| 109 | +out: | |
| 110 | + sg_free_table(sgt); | |
| 111 | + kfree(sgt); | |
| 112 | + return ERR_PTR(ret); | |
| 113 | +} | |
| 114 | + | |
| 115 | +void virtio_gpu_vram_unmap_dma_buf(struct device *dev, | |
| 116 | + struct sg_table *sgt, | |
| 117 | + enum dma_data_direction dir) | |
| 118 | +{ | |
| 119 | + if (sgt->nents) { | |
| 120 | + dma_unmap_resource(dev, sg_dma_address(sgt->sgl), | |
| 121 | + sg_dma_len(sgt->sgl), dir, | |
| 122 | + DMA_ATTR_SKIP_CPU_SYNC); | |
| 123 | + } | |
| 124 | + sg_free_table(sgt); | |
| 125 | + kfree(sgt); | |
| 126 | +} | |
| 127 | + | |
| 67 | 128 | static const struct drm_gem_object_funcs virtio_gpu_vram_funcs = { |
| 68 | 129 | .open = virtio_gpu_gem_object_open, |
| 69 | 130 | .close = virtio_gpu_gem_object_close, |
| @@ -94,7 +94,6 @@ int vmw_bo_pin_in_placement(struct vmw_private *dev_priv, · 93 unmodified lines | ||
| 94 | 94 | struct ttm_operation_ctx ctx = {interruptible, false }; |
| 95 | 95 | struct ttm_buffer_object *bo = &buf->base; |
| 96 | 96 | int ret; |
| 97 | − uint32_t new_flags; | |
| 98 | 97 | |
| 99 | 98 | vmw_execbuf_release_pinned_bo(dev_priv); |
| 100 | 99 | |
| @@ -103,8 +102,8 @@ int vmw_bo_pin_in_placement(struct vmw_private *dev_priv, · 2 unmodified lines | ||
| 103 | 102 | goto err; |
| 104 | 103 | |
| 105 | 104 | if (buf->base.pin_count > 0) |
| 106 | − ret = ttm_bo_mem_compat(placement, bo->resource, | |
| 107 | − &new_flags) == true ? 0 : -EINVAL; | |
| 105 | + ret = ttm_resource_compat(bo->resource, placement) | |
| 106 | + ? 0 : -EINVAL; | |
| 108 | 107 | else |
| 109 | 108 | ret = ttm_bo_validate(bo, placement, &ctx); |
| 110 | 109 | |
| @@ -136,7 +135,6 @@ int vmw_bo_pin_in_vram_or_gmr(struct vmw_private *dev_priv, · 25 unmodified lines | ||
| 136 | 135 | struct ttm_operation_ctx ctx = {interruptible, false }; |
| 137 | 136 | struct ttm_buffer_object *bo = &buf->base; |
| 138 | 137 | int ret; |
| 139 | − uint32_t new_flags; | |
| 140 | 138 | |
| 141 | 139 | vmw_execbuf_release_pinned_bo(dev_priv); |
| 142 | 140 | |
| @@ -145,8 +143,8 @@ int vmw_bo_pin_in_vram_or_gmr(struct vmw_private *dev_priv, · 2 unmodified lines | ||
| 145 | 143 | goto err; |
| 146 | 144 | |
| 147 | 145 | if (buf->base.pin_count > 0) { |
| 148 | − ret = ttm_bo_mem_compat(&vmw_vram_gmr_placement, bo->resource, | |
| 149 | − &new_flags) == true ? 0 : -EINVAL; | |
| 146 | + ret = ttm_resource_compat(bo->resource, &vmw_vram_gmr_placement) | |
| 147 | + ? 0 : -EINVAL; | |
| 150 | 148 | goto out_unreserve; |
| 151 | 149 | } |
| 152 | 150 | |
| @@ -208,7 +206,6 @@ int vmw_bo_pin_in_start_of_vram(struct vmw_private *dev_priv, · 55 unmodified lines | ||
| 208 | 206 | struct ttm_placement placement; |
| 209 | 207 | struct ttm_place place; |
| 210 | 208 | int ret = 0; |
| 211 | − uint32_t new_flags; | |
| 212 | 209 | |
| 213 | 210 | place = vmw_vram_placement.placement[0]; |
| 214 | 211 | place.lpfn = bo->resource->num_pages; |
| @@ -236,8 +233,8 @@ int vmw_bo_pin_in_start_of_vram(struct vmw_private *dev_priv, · 21 unmodified lines | ||
| 236 | 233 | } |
| 237 | 234 | |
| 238 | 235 | if (buf->base.pin_count > 0) |
| 239 | − ret = ttm_bo_mem_compat(&placement, bo->resource, | |
| 240 | − &new_flags) == true ? 0 : -EINVAL; | |
| 236 | + ret = ttm_resource_compat(bo->resource, &placement) | |
| 237 | + ? 0 : -EINVAL; | |
| 241 | 238 | else |
| 242 | 239 | ret = ttm_bo_validate(bo, &placement, &ctx); |
| 243 | 240 | |
| @@ -522,14 +522,8 @@ static void vmw_ttm_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) · 521 unmodified lines | ||
| 522 | 522 | struct vmw_ttm_tt *vmw_be = |
| 523 | 523 | container_of(ttm, struct vmw_ttm_tt, dma_ttm); |
| 524 | 524 | |
| 525 | − vmw_ttm_unbind(bdev, ttm); | |
| 526 | − ttm_tt_destroy_common(bdev, ttm); | |
| 527 | 525 | vmw_ttm_unmap_dma(vmw_be); |
| 528 | − if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) | |
| 529 | − ttm_tt_fini(&vmw_be->dma_ttm); | |
| 530 | − else | |
| 531 | − ttm_tt_fini(ttm); | |
| 532 | − | |
| 526 | + ttm_tt_fini(ttm); | |
| 533 | 527 | if (vmw_be->mob) |
| 534 | 528 | vmw_mob_destroy(vmw_be->mob); |
| 535 | 529 | |
| @@ -574,6 +568,8 @@ static void vmw_ttm_unpopulate(struct ttm_device *bdev, · 38 unmodified lines | ||
| 574 | 568 | dma_ttm); |
| 575 | 569 | unsigned int i; |
| 576 | 570 | |
| 571 | + vmw_ttm_unbind(bdev, ttm); | |
| 572 | + | |
| 577 | 573 | if (vmw_tt->mob) { |
| 578 | 574 | vmw_mob_destroy(vmw_tt->mob); |
| 579 | 575 | vmw_tt->mob = NULL; |
| @@ -1,10 +0,0 @@ | ||
| 1 | −# SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −config DRM_ZTE | |
| 3 | − tristate "DRM Support for ZTE SoCs" | |
| 4 | − depends on DRM && ARCH_ZX | |
| 5 | − select DRM_KMS_CMA_HELPER | |
| 6 | − select DRM_KMS_HELPER | |
| 7 | − select SND_SOC_HDMI_CODEC if SND_SOC | |
| 8 | − select VIDEOMODE_HELPERS | |
| 9 | − help | |
| 10 | − Choose this option to enable DRM on ZTE ZX SoCs. | |
| @@ -1,10 +0,0 @@ | ||
| 1 | −# SPDX-License-Identifier: GPL-2.0 | |
| 2 | −zxdrm-y := \ | |
| 3 | − zx_drm_drv.o \ | |
| 4 | − zx_hdmi.o \ | |
| 5 | − zx_plane.o \ | |
| 6 | − zx_tvenc.o \ | |
| 7 | − zx_vga.o \ | |
| 8 | − zx_vou.o | |
| 9 | − | |
| 10 | −obj-$(CONFIG_DRM_ZTE) += zxdrm.o | |
| @@ -1,28 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright (C) 2017 Sanechips Technology Co., Ltd. | |
| 4 | − * Copyright 2017 Linaro Ltd. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_COMMON_REGS_H__ | |
| 8 | −#define __ZX_COMMON_REGS_H__ | |
| 9 | − | |
| 10 | −/* CSC registers */ | |
| 11 | −#define CSC_CTRL0 0x30 | |
| 12 | −#define CSC_COV_MODE_SHIFT 16 | |
| 13 | −#define CSC_COV_MODE_MASK (0xffff << CSC_COV_MODE_SHIFT) | |
| 14 | −#define CSC_BT601_IMAGE_RGB2YCBCR 0 | |
| 15 | −#define CSC_BT601_IMAGE_YCBCR2RGB 1 | |
| 16 | −#define CSC_BT601_VIDEO_RGB2YCBCR 2 | |
| 17 | −#define CSC_BT601_VIDEO_YCBCR2RGB 3 | |
| 18 | −#define CSC_BT709_IMAGE_RGB2YCBCR 4 | |
| 19 | −#define CSC_BT709_IMAGE_YCBCR2RGB 5 | |
| 20 | −#define CSC_BT709_VIDEO_RGB2YCBCR 6 | |
| 21 | −#define CSC_BT709_VIDEO_YCBCR2RGB 7 | |
| 22 | −#define CSC_BT2020_IMAGE_RGB2YCBCR 8 | |
| 23 | −#define CSC_BT2020_IMAGE_YCBCR2RGB 9 | |
| 24 | −#define CSC_BT2020_VIDEO_RGB2YCBCR 10 | |
| 25 | −#define CSC_BT2020_VIDEO_YCBCR2RGB 11 | |
| 26 | −#define CSC_WORK_ENABLE BIT(0) | |
| 27 | − | |
| 28 | −#endif /* __ZX_COMMON_REGS_H__ */ | |
| @@ -1,184 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#include <linux/clk.h> | |
| 8 | −#include <linux/component.h> | |
| 9 | −#include <linux/list.h> | |
| 10 | −#include <linux/module.h> | |
| 11 | −#include <linux/of_graph.h> | |
| 12 | −#include <linux/of_platform.h> | |
| 13 | −#include <linux/spinlock.h> | |
| 14 | − | |
| 15 | −#include <drm/drm_atomic_helper.h> | |
| 16 | −#include <drm/drm_crtc.h> | |
| 17 | −#include <drm/drm_drv.h> | |
| 18 | −#include <drm/drm_fb_cma_helper.h> | |
| 19 | −#include <drm/drm_fb_helper.h> | |
| 20 | −#include <drm/drm_gem_cma_helper.h> | |
| 21 | −#include <drm/drm_gem_framebuffer_helper.h> | |
| 22 | −#include <drm/drm_of.h> | |
| 23 | −#include <drm/drm_probe_helper.h> | |
| 24 | −#include <drm/drm_vblank.h> | |
| 25 | − | |
| 26 | −#include "zx_drm_drv.h" | |
| 27 | −#include "zx_vou.h" | |
| 28 | − | |
| 29 | −static const struct drm_mode_config_funcs zx_drm_mode_config_funcs = { | |
| 30 | − .fb_create = drm_gem_fb_create, | |
| 31 | − .atomic_check = drm_atomic_helper_check, | |
| 32 | − .atomic_commit = drm_atomic_helper_commit, | |
| 33 | −}; | |
| 34 | − | |
| 35 | −DEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops); | |
| 36 | − | |
| 37 | −static const struct drm_driver zx_drm_driver = { | |
| 38 | − .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, | |
| 39 | − DRM_GEM_CMA_DRIVER_OPS, | |
| 40 | − .fops = &zx_drm_fops, | |
| 41 | − .name = "zx-vou", | |
| 42 | − .desc = "ZTE VOU Controller DRM", | |
| 43 | − .date = "20160811", | |
| 44 | − .major = 1, | |
| 45 | − .minor = 0, | |
| 46 | −}; | |
| 47 | − | |
| 48 | −static int zx_drm_bind(struct device *dev) | |
| 49 | −{ | |
| 50 | − struct drm_device *drm; | |
| 51 | − int ret; | |
| 52 | − | |
| 53 | − drm = drm_dev_alloc(&zx_drm_driver, dev); | |
| 54 | − if (IS_ERR(drm)) | |
| 55 | − return PTR_ERR(drm); | |
| 56 | − | |
| 57 | − dev_set_drvdata(dev, drm); | |
| 58 | − | |
| 59 | − drm_mode_config_init(drm); | |
| 60 | − drm->mode_config.min_width = 16; | |
| 61 | − drm->mode_config.min_height = 16; | |
| 62 | − drm->mode_config.max_width = 4096; | |
| 63 | − drm->mode_config.max_height = 4096; | |
| 64 | − drm->mode_config.funcs = &zx_drm_mode_config_funcs; | |
| 65 | − | |
| 66 | − ret = component_bind_all(dev, drm); | |
| 67 | − if (ret) { | |
| 68 | − DRM_DEV_ERROR(dev, "failed to bind all components: %d\n", ret); | |
| 69 | − goto out_unregister; | |
| 70 | − } | |
| 71 | − | |
| 72 | − ret = drm_vblank_init(drm, drm->mode_config.num_crtc); | |
| 73 | − if (ret < 0) { | |
| 74 | − DRM_DEV_ERROR(dev, "failed to init vblank: %d\n", ret); | |
| 75 | − goto out_unbind; | |
| 76 | − } | |
| 77 | − | |
| 78 | − drm_mode_config_reset(drm); | |
| 79 | − drm_kms_helper_poll_init(drm); | |
| 80 | − | |
| 81 | − ret = drm_dev_register(drm, 0); | |
| 82 | − if (ret) | |
| 83 | − goto out_poll_fini; | |
| 84 | − | |
| 85 | − drm_fbdev_generic_setup(drm, 32); | |
| 86 | − | |
| 87 | − return 0; | |
| 88 | − | |
| 89 | −out_poll_fini: | |
| 90 | − drm_kms_helper_poll_fini(drm); | |
| 91 | − drm_mode_config_cleanup(drm); | |
| 92 | −out_unbind: | |
| 93 | − component_unbind_all(dev, drm); | |
| 94 | −out_unregister: | |
| 95 | − dev_set_drvdata(dev, NULL); | |
| 96 | − drm_dev_put(drm); | |
| 97 | − return ret; | |
| 98 | −} | |
| 99 | − | |
| 100 | −static void zx_drm_unbind(struct device *dev) | |
| 101 | −{ | |
| 102 | − struct drm_device *drm = dev_get_drvdata(dev); | |
| 103 | − | |
| 104 | − drm_dev_unregister(drm); | |
| 105 | − drm_kms_helper_poll_fini(drm); | |
| 106 | − drm_atomic_helper_shutdown(drm); | |
| 107 | − drm_mode_config_cleanup(drm); | |
| 108 | − component_unbind_all(dev, drm); | |
| 109 | − dev_set_drvdata(dev, NULL); | |
| 110 | − drm_dev_put(drm); | |
| 111 | −} | |
| 112 | − | |
| 113 | −static const struct component_master_ops zx_drm_master_ops = { | |
| 114 | − .bind = zx_drm_bind, | |
| 115 | − .unbind = zx_drm_unbind, | |
| 116 | −}; | |
| 117 | − | |
| 118 | −static int compare_of(struct device *dev, void *data) | |
| 119 | −{ | |
| 120 | − return dev->of_node == data; | |
| 121 | −} | |
| 122 | − | |
| 123 | −static int zx_drm_probe(struct platform_device *pdev) | |
| 124 | −{ | |
| 125 | − struct device *dev = &pdev->dev; | |
| 126 | − struct device_node *parent = dev->of_node; | |
| 127 | − struct device_node *child; | |
| 128 | − struct component_match *match = NULL; | |
| 129 | − int ret; | |
| 130 | − | |
| 131 | − ret = devm_of_platform_populate(dev); | |
| 132 | − if (ret) | |
| 133 | − return ret; | |
| 134 | − | |
| 135 | − for_each_available_child_of_node(parent, child) | |
| 136 | − component_match_add(dev, &match, compare_of, child); | |
| 137 | − | |
| 138 | − return component_master_add_with_match(dev, &zx_drm_master_ops, match); | |
| 139 | −} | |
| 140 | − | |
| 141 | −static int zx_drm_remove(struct platform_device *pdev) | |
| 142 | −{ | |
| 143 | − component_master_del(&pdev->dev, &zx_drm_master_ops); | |
| 144 | − return 0; | |
| 145 | −} | |
| 146 | − | |
| 147 | −static const struct of_device_id zx_drm_of_match[] = { | |
| 148 | − { .compatible = "zte,zx296718-vou", }, | |
| 149 | − { /* end */ }, | |
| 150 | −}; | |
| 151 | −MODULE_DEVICE_TABLE(of, zx_drm_of_match); | |
| 152 | − | |
| 153 | −static struct platform_driver zx_drm_platform_driver = { | |
| 154 | − .probe = zx_drm_probe, | |
| 155 | − .remove = zx_drm_remove, | |
| 156 | − .driver = { | |
| 157 | − .name = "zx-drm", | |
| 158 | − .of_match_table = zx_drm_of_match, | |
| 159 | − }, | |
| 160 | −}; | |
| 161 | − | |
| 162 | −static struct platform_driver *drivers[] = { | |
| 163 | − &zx_crtc_driver, | |
| 164 | − &zx_hdmi_driver, | |
| 165 | − &zx_tvenc_driver, | |
| 166 | − &zx_vga_driver, | |
| 167 | − &zx_drm_platform_driver, | |
| 168 | −}; | |
| 169 | − | |
| 170 | −static int zx_drm_init(void) | |
| 171 | −{ | |
| 172 | − return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); | |
| 173 | −} | |
| 174 | −module_init(zx_drm_init); | |
| 175 | − | |
| 176 | −static void zx_drm_exit(void) | |
| 177 | −{ | |
| 178 | − platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); | |
| 179 | −} | |
| 180 | −module_exit(zx_drm_exit); | |
| 181 | − | |
| 182 | −MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); | |
| 183 | −MODULE_DESCRIPTION("ZTE ZX VOU DRM driver"); | |
| 184 | −MODULE_LICENSE("GPL v2"); | |
| @@ -1,34 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_DRM_DRV_H__ | |
| 8 | −#define __ZX_DRM_DRV_H__ | |
| 9 | − | |
| 10 | −extern struct platform_driver zx_crtc_driver; | |
| 11 | −extern struct platform_driver zx_hdmi_driver; | |
| 12 | −extern struct platform_driver zx_tvenc_driver; | |
| 13 | −extern struct platform_driver zx_vga_driver; | |
| 14 | − | |
| 15 | −static inline u32 zx_readl(void __iomem *reg) | |
| 16 | −{ | |
| 17 | − return readl_relaxed(reg); | |
| 18 | −} | |
| 19 | − | |
| 20 | −static inline void zx_writel(void __iomem *reg, u32 val) | |
| 21 | −{ | |
| 22 | − writel_relaxed(val, reg); | |
| 23 | −} | |
| 24 | − | |
| 25 | −static inline void zx_writel_mask(void __iomem *reg, u32 mask, u32 val) | |
| 26 | −{ | |
| 27 | − u32 tmp; | |
| 28 | − | |
| 29 | − tmp = zx_readl(reg); | |
| 30 | − tmp = (tmp & ~mask) | (val & mask); | |
| 31 | − zx_writel(reg, tmp); | |
| 32 | −} | |
| 33 | − | |
| 34 | −#endif /* __ZX_DRM_DRV_H__ */ | |
| @@ -1,760 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#include <linux/clk.h> | |
| 8 | −#include <linux/component.h> | |
| 9 | −#include <linux/delay.h> | |
| 10 | −#include <linux/err.h> | |
| 11 | −#include <linux/hdmi.h> | |
| 12 | −#include <linux/irq.h> | |
| 13 | −#include <linux/mfd/syscon.h> | |
| 14 | −#include <linux/module.h> | |
| 15 | −#include <linux/mutex.h> | |
| 16 | −#include <linux/of_device.h> | |
| 17 | − | |
| 18 | −#include <drm/drm_atomic_helper.h> | |
| 19 | −#include <drm/drm_edid.h> | |
| 20 | −#include <drm/drm_of.h> | |
| 21 | −#include <drm/drm_probe_helper.h> | |
| 22 | −#include <drm/drm_print.h> | |
| 23 | −#include <drm/drm_simple_kms_helper.h> | |
| 24 | − | |
| 25 | −#include <sound/hdmi-codec.h> | |
| 26 | − | |
| 27 | −#include "zx_hdmi_regs.h" | |
| 28 | −#include "zx_vou.h" | |
| 29 | − | |
| 30 | −#define ZX_HDMI_INFOFRAME_SIZE 31 | |
| 31 | −#define DDC_SEGMENT_ADDR 0x30 | |
| 32 | − | |
| 33 | −struct zx_hdmi_i2c { | |
| 34 | − struct i2c_adapter adap; | |
| 35 | − struct mutex lock; | |
| 36 | −}; | |
| 37 | − | |
| 38 | −struct zx_hdmi { | |
| 39 | − struct drm_connector connector; | |
| 40 | − struct drm_encoder encoder; | |
| 41 | − struct zx_hdmi_i2c *ddc; | |
| 42 | − struct device *dev; | |
| 43 | − struct drm_device *drm; | |
| 44 | − void __iomem *mmio; | |
| 45 | − struct clk *cec_clk; | |
| 46 | − struct clk *osc_clk; | |
| 47 | − struct clk *xclk; | |
| 48 | − bool sink_is_hdmi; | |
| 49 | − bool sink_has_audio; | |
| 50 | − struct platform_device *audio_pdev; | |
| 51 | −}; | |
| 52 | − | |
| 53 | −#define to_zx_hdmi(x) container_of(x, struct zx_hdmi, x) | |
| 54 | − | |
| 55 | −static inline u8 hdmi_readb(struct zx_hdmi *hdmi, u16 offset) | |
| 56 | −{ | |
| 57 | − return readl_relaxed(hdmi->mmio + offset * 4); | |
| 58 | −} | |
| 59 | − | |
| 60 | −static inline void hdmi_writeb(struct zx_hdmi *hdmi, u16 offset, u8 val) | |
| 61 | −{ | |
| 62 | − writel_relaxed(val, hdmi->mmio + offset * 4); | |
| 63 | −} | |
| 64 | − | |
| 65 | −static inline void hdmi_writeb_mask(struct zx_hdmi *hdmi, u16 offset, | |
| 66 | − u8 mask, u8 val) | |
| 67 | −{ | |
| 68 | − u8 tmp; | |
| 69 | − | |
| 70 | − tmp = hdmi_readb(hdmi, offset); | |
| 71 | − tmp = (tmp & ~mask) | (val & mask); | |
| 72 | − hdmi_writeb(hdmi, offset, tmp); | |
| 73 | −} | |
| 74 | − | |
| 75 | −static int zx_hdmi_infoframe_trans(struct zx_hdmi *hdmi, | |
| 76 | − union hdmi_infoframe *frame, u8 fsel) | |
| 77 | −{ | |
| 78 | − u8 buffer[ZX_HDMI_INFOFRAME_SIZE]; | |
| 79 | − int num; | |
| 80 | − int i; | |
| 81 | − | |
| 82 | − hdmi_writeb(hdmi, TPI_INFO_FSEL, fsel); | |
| 83 | − | |
| 84 | − num = hdmi_infoframe_pack(frame, buffer, ZX_HDMI_INFOFRAME_SIZE); | |
| 85 | − if (num < 0) { | |
| 86 | − DRM_DEV_ERROR(hdmi->dev, "failed to pack infoframe: %d\n", num); | |
| 87 | − return num; | |
| 88 | − } | |
| 89 | − | |
| 90 | − for (i = 0; i < num; i++) | |
| 91 | − hdmi_writeb(hdmi, TPI_INFO_B0 + i, buffer[i]); | |
| 92 | − | |
| 93 | − hdmi_writeb_mask(hdmi, TPI_INFO_EN, TPI_INFO_TRANS_RPT, | |
| 94 | − TPI_INFO_TRANS_RPT); | |
| 95 | − hdmi_writeb_mask(hdmi, TPI_INFO_EN, TPI_INFO_TRANS_EN, | |
| 96 | − TPI_INFO_TRANS_EN); | |
| 97 | − | |
| 98 | − return num; | |
| 99 | −} | |
| 100 | − | |
| 101 | −static int zx_hdmi_config_video_vsi(struct zx_hdmi *hdmi, | |
| 102 | − struct drm_display_mode *mode) | |
| 103 | −{ | |
| 104 | − union hdmi_infoframe frame; | |
| 105 | − int ret; | |
| 106 | − | |
| 107 | − ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi, | |
| 108 | − &hdmi->connector, | |
| 109 | − mode); | |
| 110 | − if (ret) { | |
| 111 | − DRM_DEV_ERROR(hdmi->dev, "failed to get vendor infoframe: %d\n", | |
| 112 | − ret); | |
| 113 | − return ret; | |
| 114 | − } | |
| 115 | − | |
| 116 | − return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_VSIF); | |
| 117 | −} | |
| 118 | − | |
| 119 | −static int zx_hdmi_config_video_avi(struct zx_hdmi *hdmi, | |
| 120 | − struct drm_display_mode *mode) | |
| 121 | −{ | |
| 122 | − union hdmi_infoframe frame; | |
| 123 | − int ret; | |
| 124 | − | |
| 125 | − ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, | |
| 126 | − &hdmi->connector, | |
| 127 | − mode); | |
| 128 | − if (ret) { | |
| 129 | − DRM_DEV_ERROR(hdmi->dev, "failed to get avi infoframe: %d\n", | |
| 130 | − ret); | |
| 131 | − return ret; | |
| 132 | − } | |
| 133 | − | |
| 134 | − /* We always use YUV444 for HDMI output. */ | |
| 135 | − frame.avi.colorspace = HDMI_COLORSPACE_YUV444; | |
| 136 | − | |
| 137 | − return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AVI); | |
| 138 | −} | |
| 139 | − | |
| 140 | −static void zx_hdmi_encoder_mode_set(struct drm_encoder *encoder, | |
| 141 | − struct drm_display_mode *mode, | |
| 142 | − struct drm_display_mode *adj_mode) | |
| 143 | −{ | |
| 144 | − struct zx_hdmi *hdmi = to_zx_hdmi(encoder); | |
| 145 | − | |
| 146 | − if (hdmi->sink_is_hdmi) { | |
| 147 | − zx_hdmi_config_video_avi(hdmi, mode); | |
| 148 | − zx_hdmi_config_video_vsi(hdmi, mode); | |
| 149 | − } | |
| 150 | −} | |
| 151 | − | |
| 152 | −static void zx_hdmi_phy_start(struct zx_hdmi *hdmi) | |
| 153 | −{ | |
| 154 | − /* Copy from ZTE BSP code */ | |
| 155 | − hdmi_writeb(hdmi, 0x222, 0x0); | |
| 156 | − hdmi_writeb(hdmi, 0x224, 0x4); | |
| 157 | − hdmi_writeb(hdmi, 0x909, 0x0); | |
| 158 | − hdmi_writeb(hdmi, 0x7b0, 0x90); | |
| 159 | − hdmi_writeb(hdmi, 0x7b1, 0x00); | |
| 160 | − hdmi_writeb(hdmi, 0x7b2, 0xa7); | |
| 161 | − hdmi_writeb(hdmi, 0x7b8, 0xaa); | |
| 162 | − hdmi_writeb(hdmi, 0x7b2, 0xa7); | |
| 163 | − hdmi_writeb(hdmi, 0x7b3, 0x0f); | |
| 164 | − hdmi_writeb(hdmi, 0x7b4, 0x0f); | |
| 165 | − hdmi_writeb(hdmi, 0x7b5, 0x55); | |
| 166 | − hdmi_writeb(hdmi, 0x7b7, 0x03); | |
| 167 | − hdmi_writeb(hdmi, 0x7b9, 0x12); | |
| 168 | − hdmi_writeb(hdmi, 0x7ba, 0x32); | |
| 169 | − hdmi_writeb(hdmi, 0x7bc, 0x68); | |
| 170 | − hdmi_writeb(hdmi, 0x7be, 0x40); | |
| 171 | − hdmi_writeb(hdmi, 0x7bf, 0x84); | |
| 172 | − hdmi_writeb(hdmi, 0x7c1, 0x0f); | |
| 173 | − hdmi_writeb(hdmi, 0x7c8, 0x02); | |
| 174 | − hdmi_writeb(hdmi, 0x7c9, 0x03); | |
| 175 | − hdmi_writeb(hdmi, 0x7ca, 0x40); | |
| 176 | − hdmi_writeb(hdmi, 0x7dc, 0x31); | |
| 177 | − hdmi_writeb(hdmi, 0x7e2, 0x04); | |
| 178 | − hdmi_writeb(hdmi, 0x7e0, 0x06); | |
| 179 | − hdmi_writeb(hdmi, 0x7cb, 0x68); | |
| 180 | − hdmi_writeb(hdmi, 0x7f9, 0x02); | |
| 181 | − hdmi_writeb(hdmi, 0x7b6, 0x02); | |
| 182 | − hdmi_writeb(hdmi, 0x7f3, 0x0); | |
| 183 | −} | |
| 184 | − | |
| 185 | −static void zx_hdmi_hw_enable(struct zx_hdmi *hdmi) | |
| 186 | −{ | |
| 187 | − /* Enable pclk */ | |
| 188 | − hdmi_writeb_mask(hdmi, CLKPWD, CLKPWD_PDIDCK, CLKPWD_PDIDCK); | |
| 189 | − | |
| 190 | − /* Enable HDMI for TX */ | |
| 191 | − hdmi_writeb_mask(hdmi, FUNC_SEL, FUNC_HDMI_EN, FUNC_HDMI_EN); | |
| 192 | − | |
| 193 | − /* Enable deep color packet */ | |
| 194 | − hdmi_writeb_mask(hdmi, P2T_CTRL, P2T_DC_PKT_EN, P2T_DC_PKT_EN); | |
| 195 | − | |
| 196 | − /* Enable HDMI/MHL mode for output */ | |
| 197 | − hdmi_writeb_mask(hdmi, TEST_TXCTRL, TEST_TXCTRL_HDMI_MODE, | |
| 198 | − TEST_TXCTRL_HDMI_MODE); | |
| 199 | − | |
| 200 | − /* Configure reg_qc_sel */ | |
| 201 | − hdmi_writeb(hdmi, HDMICTL4, 0x3); | |
| 202 | − | |
| 203 | − /* Enable interrupt */ | |
| 204 | − hdmi_writeb_mask(hdmi, INTR1_MASK, INTR1_MONITOR_DETECT, | |
| 205 | − INTR1_MONITOR_DETECT); | |
| 206 | − | |
| 207 | − /* Start up phy */ | |
| 208 | − zx_hdmi_phy_start(hdmi); | |
| 209 | −} | |
| 210 | − | |
| 211 | −static void zx_hdmi_hw_disable(struct zx_hdmi *hdmi) | |
| 212 | −{ | |
| 213 | − /* Disable interrupt */ | |
| 214 | − hdmi_writeb_mask(hdmi, INTR1_MASK, INTR1_MONITOR_DETECT, 0); | |
| 215 | − | |
| 216 | − /* Disable deep color packet */ | |
| 217 | − hdmi_writeb_mask(hdmi, P2T_CTRL, P2T_DC_PKT_EN, P2T_DC_PKT_EN); | |
| 218 | − | |
| 219 | − /* Disable HDMI for TX */ | |
| 220 | − hdmi_writeb_mask(hdmi, FUNC_SEL, FUNC_HDMI_EN, 0); | |
| 221 | − | |
| 222 | − /* Disable pclk */ | |
| 223 | − hdmi_writeb_mask(hdmi, CLKPWD, CLKPWD_PDIDCK, 0); | |
| 224 | −} | |
| 225 | − | |
| 226 | −static void zx_hdmi_encoder_enable(struct drm_encoder *encoder) | |
| 227 | −{ | |
| 228 | − struct zx_hdmi *hdmi = to_zx_hdmi(encoder); | |
| 229 | − | |
| 230 | − clk_prepare_enable(hdmi->cec_clk); | |
| 231 | − clk_prepare_enable(hdmi->osc_clk); | |
| 232 | − clk_prepare_enable(hdmi->xclk); | |
| 233 | − | |
| 234 | − zx_hdmi_hw_enable(hdmi); | |
| 235 | − | |
| 236 | − vou_inf_enable(VOU_HDMI, encoder->crtc); | |
| 237 | −} | |
| 238 | − | |
| 239 | −static void zx_hdmi_encoder_disable(struct drm_encoder *encoder) | |
| 240 | −{ | |
| 241 | − struct zx_hdmi *hdmi = to_zx_hdmi(encoder); | |
| 242 | − | |
| 243 | − vou_inf_disable(VOU_HDMI, encoder->crtc); | |
| 244 | − | |
| 245 | − zx_hdmi_hw_disable(hdmi); | |
| 246 | − | |
| 247 | − clk_disable_unprepare(hdmi->xclk); | |
| 248 | − clk_disable_unprepare(hdmi->osc_clk); | |
| 249 | − clk_disable_unprepare(hdmi->cec_clk); | |
| 250 | −} | |
| 251 | − | |
| 252 | −static const struct drm_encoder_helper_funcs zx_hdmi_encoder_helper_funcs = { | |
| 253 | − .enable = zx_hdmi_encoder_enable, | |
| 254 | − .disable = zx_hdmi_encoder_disable, | |
| 255 | − .mode_set = zx_hdmi_encoder_mode_set, | |
| 256 | −}; | |
| 257 | − | |
| 258 | −static int zx_hdmi_connector_get_modes(struct drm_connector *connector) | |
| 259 | −{ | |
| 260 | − struct zx_hdmi *hdmi = to_zx_hdmi(connector); | |
| 261 | − struct edid *edid; | |
| 262 | − int ret; | |
| 263 | − | |
| 264 | − edid = drm_get_edid(connector, &hdmi->ddc->adap); | |
| 265 | − if (!edid) | |
| 266 | − return 0; | |
| 267 | − | |
| 268 | − hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid); | |
| 269 | − hdmi->sink_has_audio = drm_detect_monitor_audio(edid); | |
| 270 | − drm_connector_update_edid_property(connector, edid); | |
| 271 | − ret = drm_add_edid_modes(connector, edid); | |
| 272 | − kfree(edid); | |
| 273 | − | |
| 274 | − return ret; | |
| 275 | −} | |
| 276 | − | |
| 277 | −static enum drm_mode_status | |
| 278 | −zx_hdmi_connector_mode_valid(struct drm_connector *connector, | |
| 279 | − struct drm_display_mode *mode) | |
| 280 | −{ | |
| 281 | − return MODE_OK; | |
| 282 | −} | |
| 283 | − | |
| 284 | −static struct drm_connector_helper_funcs zx_hdmi_connector_helper_funcs = { | |
| 285 | − .get_modes = zx_hdmi_connector_get_modes, | |
| 286 | − .mode_valid = zx_hdmi_connector_mode_valid, | |
| 287 | −}; | |
| 288 | − | |
| 289 | −static enum drm_connector_status | |
| 290 | −zx_hdmi_connector_detect(struct drm_connector *connector, bool force) | |
| 291 | −{ | |
| 292 | − struct zx_hdmi *hdmi = to_zx_hdmi(connector); | |
| 293 | − | |
| 294 | − return (hdmi_readb(hdmi, TPI_HPD_RSEN) & TPI_HPD_CONNECTION) ? | |
| 295 | − connector_status_connected : connector_status_disconnected; | |
| 296 | −} | |
| 297 | − | |
| 298 | −static const struct drm_connector_funcs zx_hdmi_connector_funcs = { | |
| 299 | − .fill_modes = drm_helper_probe_single_connector_modes, | |
| 300 | − .detect = zx_hdmi_connector_detect, | |
| 301 | − .destroy = drm_connector_cleanup, | |
| 302 | − .reset = drm_atomic_helper_connector_reset, | |
| 303 | − .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 304 | − .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 305 | −}; | |
| 306 | − | |
| 307 | −static int zx_hdmi_register(struct drm_device *drm, struct zx_hdmi *hdmi) | |
| 308 | −{ | |
| 309 | − struct drm_encoder *encoder = &hdmi->encoder; | |
| 310 | − | |
| 311 | − encoder->possible_crtcs = VOU_CRTC_MASK; | |
| 312 | − | |
| 313 | − drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); | |
| 314 | − drm_encoder_helper_add(encoder, &zx_hdmi_encoder_helper_funcs); | |
| 315 | − | |
| 316 | − hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; | |
| 317 | − | |
| 318 | − drm_connector_init_with_ddc(drm, &hdmi->connector, | |
| 319 | − &zx_hdmi_connector_funcs, | |
| 320 | − DRM_MODE_CONNECTOR_HDMIA, | |
| 321 | − &hdmi->ddc->adap); | |
| 322 | − drm_connector_helper_add(&hdmi->connector, | |
| 323 | − &zx_hdmi_connector_helper_funcs); | |
| 324 | − | |
| 325 | − drm_connector_attach_encoder(&hdmi->connector, encoder); | |
| 326 | − | |
| 327 | − return 0; | |
| 328 | −} | |
| 329 | − | |
| 330 | −static irqreturn_t zx_hdmi_irq_thread(int irq, void *dev_id) | |
| 331 | −{ | |
| 332 | − struct zx_hdmi *hdmi = dev_id; | |
| 333 | − | |
| 334 | − drm_helper_hpd_irq_event(hdmi->connector.dev); | |
| 335 | − | |
| 336 | − return IRQ_HANDLED; | |
| 337 | −} | |
| 338 | − | |
| 339 | −static irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id) | |
| 340 | −{ | |
| 341 | − struct zx_hdmi *hdmi = dev_id; | |
| 342 | − u8 lstat; | |
| 343 | − | |
| 344 | − lstat = hdmi_readb(hdmi, L1_INTR_STAT); | |
| 345 | − | |
| 346 | − /* Monitor detect/HPD interrupt */ | |
| 347 | − if (lstat & L1_INTR_STAT_INTR1) { | |
| 348 | − u8 stat; | |
| 349 | − | |
| 350 | − stat = hdmi_readb(hdmi, INTR1_STAT); | |
| 351 | − hdmi_writeb(hdmi, INTR1_STAT, stat); | |
| 352 | − | |
| 353 | − if (stat & INTR1_MONITOR_DETECT) | |
| 354 | − return IRQ_WAKE_THREAD; | |
| 355 | − } | |
| 356 | − | |
| 357 | − return IRQ_NONE; | |
| 358 | −} | |
| 359 | − | |
| 360 | −static int zx_hdmi_audio_startup(struct device *dev, void *data) | |
| 361 | −{ | |
| 362 | − struct zx_hdmi *hdmi = dev_get_drvdata(dev); | |
| 363 | − struct drm_encoder *encoder = &hdmi->encoder; | |
| 364 | − | |
| 365 | − vou_inf_hdmi_audio_sel(encoder->crtc, VOU_HDMI_AUD_SPDIF); | |
| 366 | − | |
| 367 | − return 0; | |
| 368 | −} | |
| 369 | − | |
| 370 | −static void zx_hdmi_audio_shutdown(struct device *dev, void *data) | |
| 371 | −{ | |
| 372 | − struct zx_hdmi *hdmi = dev_get_drvdata(dev); | |
| 373 | − | |
| 374 | − /* Disable audio input */ | |
| 375 | − hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, 0); | |
| 376 | −} | |
| 377 | − | |
| 378 | −static inline int zx_hdmi_audio_get_n(unsigned int fs) | |
| 379 | −{ | |
| 380 | − unsigned int n; | |
| 381 | − | |
| 382 | − if (fs && (fs % 44100) == 0) | |
| 383 | − n = 6272 * (fs / 44100); | |
| 384 | − else | |
| 385 | − n = fs * 128 / 1000; | |
| 386 | − | |
| 387 | − return n; | |
| 388 | −} | |
| 389 | − | |
| 390 | −static int zx_hdmi_audio_hw_params(struct device *dev, | |
| 391 | − void *data, | |
| 392 | − struct hdmi_codec_daifmt *daifmt, | |
| 393 | − struct hdmi_codec_params *params) | |
| 394 | −{ | |
| 395 | − struct zx_hdmi *hdmi = dev_get_drvdata(dev); | |
| 396 | − struct hdmi_audio_infoframe *cea = ¶ms->cea; | |
| 397 | − union hdmi_infoframe frame; | |
| 398 | − int n; | |
| 399 | − | |
| 400 | − /* We only support spdif for now */ | |
| 401 | − if (daifmt->fmt != HDMI_SPDIF) { | |
| 402 | − DRM_DEV_ERROR(hdmi->dev, "invalid daifmt %d\n", daifmt->fmt); | |
| 403 | − return -EINVAL; | |
| 404 | − } | |
| 405 | − | |
| 406 | − switch (params->sample_width) { | |
| 407 | − case 16: | |
| 408 | − hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, | |
| 409 | − SPDIF_SAMPLE_SIZE_16BIT); | |
| 410 | − break; | |
| 411 | − case 20: | |
| 412 | − hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, | |
| 413 | − SPDIF_SAMPLE_SIZE_20BIT); | |
| 414 | − break; | |
| 415 | − case 24: | |
| 416 | − hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, SPDIF_SAMPLE_SIZE_MASK, | |
| 417 | − SPDIF_SAMPLE_SIZE_24BIT); | |
| 418 | − break; | |
| 419 | − default: | |
| 420 | − DRM_DEV_ERROR(hdmi->dev, "invalid sample width %d\n", | |
| 421 | − params->sample_width); | |
| 422 | − return -EINVAL; | |
| 423 | − } | |
| 424 | − | |
| 425 | − /* CTS is calculated by hardware, and we only need to take care of N */ | |
| 426 | − n = zx_hdmi_audio_get_n(params->sample_rate); | |
| 427 | − hdmi_writeb(hdmi, N_SVAL1, n & 0xff); | |
| 428 | − hdmi_writeb(hdmi, N_SVAL2, (n >> 8) & 0xff); | |
| 429 | − hdmi_writeb(hdmi, N_SVAL3, (n >> 16) & 0xf); | |
| 430 | − | |
| 431 | − /* Enable spdif mode */ | |
| 432 | − hdmi_writeb_mask(hdmi, AUD_MODE, SPDIF_EN, SPDIF_EN); | |
| 433 | − | |
| 434 | − /* Enable audio input */ | |
| 435 | − hdmi_writeb_mask(hdmi, AUD_EN, AUD_IN_EN, AUD_IN_EN); | |
| 436 | − | |
| 437 | − memcpy(&frame.audio, cea, sizeof(*cea)); | |
| 438 | − | |
| 439 | − return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AUDIO); | |
| 440 | −} | |
| 441 | − | |
| 442 | −static int zx_hdmi_audio_mute(struct device *dev, void *data, | |
| 443 | − bool enable, int direction) | |
| 444 | −{ | |
| 445 | − struct zx_hdmi *hdmi = dev_get_drvdata(dev); | |
| 446 | − | |
| 447 | − if (enable) | |
| 448 | − hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, | |
| 449 | − TPI_AUD_MUTE); | |
| 450 | − else | |
| 451 | − hdmi_writeb_mask(hdmi, TPI_AUD_CONFIG, TPI_AUD_MUTE, 0); | |
| 452 | − | |
| 453 | − return 0; | |
| 454 | −} | |
| 455 | − | |
| 456 | −static int zx_hdmi_audio_get_eld(struct device *dev, void *data, | |
| 457 | − uint8_t *buf, size_t len) | |
| 458 | −{ | |
| 459 | − struct zx_hdmi *hdmi = dev_get_drvdata(dev); | |
| 460 | − struct drm_connector *connector = &hdmi->connector; | |
| 461 | − | |
| 462 | − memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); | |
| 463 | − | |
| 464 | − return 0; | |
| 465 | −} | |
| 466 | − | |
| 467 | −static const struct hdmi_codec_ops zx_hdmi_codec_ops = { | |
| 468 | − .audio_startup = zx_hdmi_audio_startup, | |
| 469 | − .hw_params = zx_hdmi_audio_hw_params, | |
| 470 | − .audio_shutdown = zx_hdmi_audio_shutdown, | |
| 471 | − .mute_stream = zx_hdmi_audio_mute, | |
| 472 | − .get_eld = zx_hdmi_audio_get_eld, | |
| 473 | − .no_capture_mute = 1, | |
| 474 | −}; | |
| 475 | − | |
| 476 | −static struct hdmi_codec_pdata zx_hdmi_codec_pdata = { | |
| 477 | − .ops = &zx_hdmi_codec_ops, | |
| 478 | − .spdif = 1, | |
| 479 | −}; | |
| 480 | − | |
| 481 | −static int zx_hdmi_audio_register(struct zx_hdmi *hdmi) | |
| 482 | −{ | |
| 483 | − struct platform_device *pdev; | |
| 484 | − | |
| 485 | − pdev = platform_device_register_data(hdmi->dev, HDMI_CODEC_DRV_NAME, | |
| 486 | − PLATFORM_DEVID_AUTO, | |
| 487 | − &zx_hdmi_codec_pdata, | |
| 488 | − sizeof(zx_hdmi_codec_pdata)); | |
| 489 | − if (IS_ERR(pdev)) | |
| 490 | − return PTR_ERR(pdev); | |
| 491 | − | |
| 492 | − hdmi->audio_pdev = pdev; | |
| 493 | − | |
| 494 | − return 0; | |
| 495 | −} | |
| 496 | − | |
| 497 | −static int zx_hdmi_i2c_read(struct zx_hdmi *hdmi, struct i2c_msg *msg) | |
| 498 | −{ | |
| 499 | − int len = msg->len; | |
| 500 | − u8 *buf = msg->buf; | |
| 501 | − int retry = 0; | |
| 502 | − int ret = 0; | |
| 503 | − | |
| 504 | − /* Bits [9:8] of bytes */ | |
| 505 | − hdmi_writeb(hdmi, ZX_DDC_DIN_CNT2, (len >> 8) & 0xff); | |
| 506 | − /* Bits [7:0] of bytes */ | |
| 507 | − hdmi_writeb(hdmi, ZX_DDC_DIN_CNT1, len & 0xff); | |
| 508 | − | |
| 509 | − /* Clear FIFO */ | |
| 510 | − hdmi_writeb_mask(hdmi, ZX_DDC_CMD, DDC_CMD_MASK, DDC_CMD_CLEAR_FIFO); | |
| 511 | − | |
| 512 | − /* Kick off the read */ | |
| 513 | − hdmi_writeb_mask(hdmi, ZX_DDC_CMD, DDC_CMD_MASK, | |
| 514 | − DDC_CMD_SEQUENTIAL_READ); | |
| 515 | − | |
| 516 | − while (len > 0) { | |
| 517 | − int cnt, i; | |
| 518 | − | |
| 519 | − /* FIFO needs some time to get ready */ | |
| 520 | − usleep_range(500, 1000); | |
| 521 | − | |
| 522 | − cnt = hdmi_readb(hdmi, ZX_DDC_DOUT_CNT) & DDC_DOUT_CNT_MASK; | |
| 523 | − if (cnt == 0) { | |
| 524 | − if (++retry > 5) { | |
| 525 | − DRM_DEV_ERROR(hdmi->dev, | |
| 526 | − "DDC FIFO read timed out!"); | |
| 527 | − return -ETIMEDOUT; | |
| 528 | − } | |
| 529 | − continue; | |
| 530 | − } | |
| 531 | − | |
| 532 | − for (i = 0; i < cnt; i++) | |
| 533 | − *buf++ = hdmi_readb(hdmi, ZX_DDC_DATA); | |
| 534 | − len -= cnt; | |
| 535 | − } | |
| 536 | − | |
| 537 | − return ret; | |
| 538 | −} | |
| 539 | − | |
| 540 | −static int zx_hdmi_i2c_write(struct zx_hdmi *hdmi, struct i2c_msg *msg) | |
| 541 | −{ | |
| 542 | − /* | |
| 543 | − * The DDC I2C adapter is only for reading EDID data, so we assume | |
| 544 | − * that the write to this adapter must be the EDID data offset. | |
| 545 | − */ | |
| 546 | − if ((msg->len != 1) || | |
| 547 | − ((msg->addr != DDC_ADDR) && (msg->addr != DDC_SEGMENT_ADDR))) | |
| 548 | − return -EINVAL; | |
| 549 | − | |
| 550 | − if (msg->addr == DDC_SEGMENT_ADDR) | |
| 551 | − hdmi_writeb(hdmi, ZX_DDC_SEGM, msg->addr << 1); | |
| 552 | − else if (msg->addr == DDC_ADDR) | |
| 553 | − hdmi_writeb(hdmi, ZX_DDC_ADDR, msg->addr << 1); | |
| 554 | − | |
| 555 | − hdmi_writeb(hdmi, ZX_DDC_OFFSET, msg->buf[0]); | |
| 556 | − | |
| 557 | − return 0; | |
| 558 | −} | |
| 559 | − | |
| 560 | −static int zx_hdmi_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, | |
| 561 | − int num) | |
| 562 | −{ | |
| 563 | − struct zx_hdmi *hdmi = i2c_get_adapdata(adap); | |
| 564 | − struct zx_hdmi_i2c *ddc = hdmi->ddc; | |
| 565 | − int i, ret = 0; | |
| 566 | − | |
| 567 | − mutex_lock(&ddc->lock); | |
| 568 | − | |
| 569 | − /* Enable DDC master access */ | |
| 570 | − hdmi_writeb_mask(hdmi, TPI_DDC_MASTER_EN, HW_DDC_MASTER, HW_DDC_MASTER); | |
| 571 | − | |
| 572 | − for (i = 0; i < num; i++) { | |
| 573 | − DRM_DEV_DEBUG(hdmi->dev, | |
| 574 | − "xfer: num: %d/%d, len: %d, flags: %#x\n", | |
| 575 | − i + 1, num, msgs[i].len, msgs[i].flags); | |
| 576 | − | |
| 577 | − if (msgs[i].flags & I2C_M_RD) | |
| 578 | − ret = zx_hdmi_i2c_read(hdmi, &msgs[i]); | |
| 579 | − else | |
| 580 | − ret = zx_hdmi_i2c_write(hdmi, &msgs[i]); | |
| 581 | − | |
| 582 | − if (ret < 0) | |
| 583 | − break; | |
| 584 | − } | |
| 585 | − | |
| 586 | − if (!ret) | |
| 587 | − ret = num; | |
| 588 | − | |
| 589 | − /* Disable DDC master access */ | |
| 590 | − hdmi_writeb_mask(hdmi, TPI_DDC_MASTER_EN, HW_DDC_MASTER, 0); | |
| 591 | − | |
| 592 | − mutex_unlock(&ddc->lock); | |
| 593 | − | |
| 594 | − return ret; | |
| 595 | −} | |
| 596 | − | |
| 597 | −static u32 zx_hdmi_i2c_func(struct i2c_adapter *adapter) | |
| 598 | −{ | |
| 599 | − return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | |
| 600 | −} | |
| 601 | − | |
| 602 | −static const struct i2c_algorithm zx_hdmi_algorithm = { | |
| 603 | − .master_xfer = zx_hdmi_i2c_xfer, | |
| 604 | − .functionality = zx_hdmi_i2c_func, | |
| 605 | −}; | |
| 606 | − | |
| 607 | −static int zx_hdmi_ddc_register(struct zx_hdmi *hdmi) | |
| 608 | −{ | |
| 609 | − struct i2c_adapter *adap; | |
| 610 | − struct zx_hdmi_i2c *ddc; | |
| 611 | − int ret; | |
| 612 | − | |
| 613 | − ddc = devm_kzalloc(hdmi->dev, sizeof(*ddc), GFP_KERNEL); | |
| 614 | − if (!ddc) | |
| 615 | − return -ENOMEM; | |
| 616 | − | |
| 617 | − hdmi->ddc = ddc; | |
| 618 | − mutex_init(&ddc->lock); | |
| 619 | − | |
| 620 | − adap = &ddc->adap; | |
| 621 | − adap->owner = THIS_MODULE; | |
| 622 | − adap->class = I2C_CLASS_DDC; | |
| 623 | − adap->dev.parent = hdmi->dev; | |
| 624 | − adap->algo = &zx_hdmi_algorithm; | |
| 625 | − snprintf(adap->name, sizeof(adap->name), "zx hdmi i2c"); | |
| 626 | − | |
| 627 | − ret = i2c_add_adapter(adap); | |
| 628 | − if (ret) { | |
| 629 | − DRM_DEV_ERROR(hdmi->dev, "failed to add I2C adapter: %d\n", | |
| 630 | − ret); | |
| 631 | − return ret; | |
| 632 | − } | |
| 633 | − | |
| 634 | − i2c_set_adapdata(adap, hdmi); | |
| 635 | − | |
| 636 | − return 0; | |
| 637 | −} | |
| 638 | − | |
| 639 | −static int zx_hdmi_bind(struct device *dev, struct device *master, void *data) | |
| 640 | −{ | |
| 641 | − struct platform_device *pdev = to_platform_device(dev); | |
| 642 | − struct drm_device *drm = data; | |
| 643 | − struct resource *res; | |
| 644 | − struct zx_hdmi *hdmi; | |
| 645 | − int irq; | |
| 646 | − int ret; | |
| 647 | − | |
| 648 | − hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); | |
| 649 | − if (!hdmi) | |
| 650 | − return -ENOMEM; | |
| 651 | − | |
| 652 | − hdmi->dev = dev; | |
| 653 | − hdmi->drm = drm; | |
| 654 | − | |
| 655 | − dev_set_drvdata(dev, hdmi); | |
| 656 | − | |
| 657 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 658 | − hdmi->mmio = devm_ioremap_resource(dev, res); | |
| 659 | − if (IS_ERR(hdmi->mmio)) { | |
| 660 | − ret = PTR_ERR(hdmi->mmio); | |
| 661 | − DRM_DEV_ERROR(dev, "failed to remap hdmi region: %d\n", ret); | |
| 662 | − return ret; | |
| 663 | − } | |
| 664 | − | |
| 665 | − irq = platform_get_irq(pdev, 0); | |
| 666 | − if (irq < 0) | |
| 667 | − return irq; | |
| 668 | − | |
| 669 | − hdmi->cec_clk = devm_clk_get(hdmi->dev, "osc_cec"); | |
| 670 | − if (IS_ERR(hdmi->cec_clk)) { | |
| 671 | − ret = PTR_ERR(hdmi->cec_clk); | |
| 672 | − DRM_DEV_ERROR(dev, "failed to get cec_clk: %d\n", ret); | |
| 673 | − return ret; | |
| 674 | − } | |
| 675 | − | |
| 676 | − hdmi->osc_clk = devm_clk_get(hdmi->dev, "osc_clk"); | |
| 677 | − if (IS_ERR(hdmi->osc_clk)) { | |
| 678 | − ret = PTR_ERR(hdmi->osc_clk); | |
| 679 | − DRM_DEV_ERROR(dev, "failed to get osc_clk: %d\n", ret); | |
| 680 | − return ret; | |
| 681 | − } | |
| 682 | − | |
| 683 | − hdmi->xclk = devm_clk_get(hdmi->dev, "xclk"); | |
| 684 | − if (IS_ERR(hdmi->xclk)) { | |
| 685 | − ret = PTR_ERR(hdmi->xclk); | |
| 686 | − DRM_DEV_ERROR(dev, "failed to get xclk: %d\n", ret); | |
| 687 | − return ret; | |
| 688 | − } | |
| 689 | − | |
| 690 | − ret = zx_hdmi_ddc_register(hdmi); | |
| 691 | − if (ret) { | |
| 692 | − DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret); | |
| 693 | − return ret; | |
| 694 | − } | |
| 695 | − | |
| 696 | − ret = zx_hdmi_audio_register(hdmi); | |
| 697 | − if (ret) { | |
| 698 | − DRM_DEV_ERROR(dev, "failed to register audio: %d\n", ret); | |
| 699 | − return ret; | |
| 700 | − } | |
| 701 | − | |
| 702 | − ret = zx_hdmi_register(drm, hdmi); | |
| 703 | − if (ret) { | |
| 704 | − DRM_DEV_ERROR(dev, "failed to register hdmi: %d\n", ret); | |
| 705 | − return ret; | |
| 706 | − } | |
| 707 | − | |
| 708 | − ret = devm_request_threaded_irq(dev, irq, zx_hdmi_irq_handler, | |
| 709 | − zx_hdmi_irq_thread, IRQF_SHARED, | |
| 710 | − dev_name(dev), hdmi); | |
| 711 | − if (ret) { | |
| 712 | − DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret); | |
| 713 | − return ret; | |
| 714 | − } | |
| 715 | − | |
| 716 | − return 0; | |
| 717 | −} | |
| 718 | − | |
| 719 | −static void zx_hdmi_unbind(struct device *dev, struct device *master, | |
| 720 | − void *data) | |
| 721 | −{ | |
| 722 | − struct zx_hdmi *hdmi = dev_get_drvdata(dev); | |
| 723 | − | |
| 724 | − hdmi->connector.funcs->destroy(&hdmi->connector); | |
| 725 | − hdmi->encoder.funcs->destroy(&hdmi->encoder); | |
| 726 | − | |
| 727 | − if (hdmi->audio_pdev) | |
| 728 | − platform_device_unregister(hdmi->audio_pdev); | |
| 729 | −} | |
| 730 | − | |
| 731 | −static const struct component_ops zx_hdmi_component_ops = { | |
| 732 | − .bind = zx_hdmi_bind, | |
| 733 | − .unbind = zx_hdmi_unbind, | |
| 734 | −}; | |
| 735 | − | |
| 736 | −static int zx_hdmi_probe(struct platform_device *pdev) | |
| 737 | −{ | |
| 738 | − return component_add(&pdev->dev, &zx_hdmi_component_ops); | |
| 739 | −} | |
| 740 | − | |
| 741 | −static int zx_hdmi_remove(struct platform_device *pdev) | |
| 742 | −{ | |
| 743 | − component_del(&pdev->dev, &zx_hdmi_component_ops); | |
| 744 | − return 0; | |
| 745 | −} | |
| 746 | − | |
| 747 | −static const struct of_device_id zx_hdmi_of_match[] = { | |
| 748 | − { .compatible = "zte,zx296718-hdmi", }, | |
| 749 | − { /* end */ }, | |
| 750 | −}; | |
| 751 | −MODULE_DEVICE_TABLE(of, zx_hdmi_of_match); | |
| 752 | − | |
| 753 | −struct platform_driver zx_hdmi_driver = { | |
| 754 | − .probe = zx_hdmi_probe, | |
| 755 | − .remove = zx_hdmi_remove, | |
| 756 | − .driver = { | |
| 757 | − .name = "zx-hdmi", | |
| 758 | − .of_match_table = zx_hdmi_of_match, | |
| 759 | − }, | |
| 760 | −}; | |
| @@ -1,66 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_HDMI_REGS_H__ | |
| 8 | −#define __ZX_HDMI_REGS_H__ | |
| 9 | − | |
| 10 | −#define FUNC_SEL 0x000b | |
| 11 | −#define FUNC_HDMI_EN BIT(0) | |
| 12 | −#define CLKPWD 0x000d | |
| 13 | −#define CLKPWD_PDIDCK BIT(2) | |
| 14 | −#define P2T_CTRL 0x0066 | |
| 15 | −#define P2T_DC_PKT_EN BIT(7) | |
| 16 | −#define L1_INTR_STAT 0x007e | |
| 17 | −#define L1_INTR_STAT_INTR1 BIT(0) | |
| 18 | −#define INTR1_STAT 0x008f | |
| 19 | −#define INTR1_MASK 0x0095 | |
| 20 | −#define INTR1_MONITOR_DETECT (BIT(5) | BIT(6)) | |
| 21 | −#define ZX_DDC_ADDR 0x00ed | |
| 22 | −#define ZX_DDC_SEGM 0x00ee | |
| 23 | −#define ZX_DDC_OFFSET 0x00ef | |
| 24 | −#define ZX_DDC_DIN_CNT1 0x00f0 | |
| 25 | −#define ZX_DDC_DIN_CNT2 0x00f1 | |
| 26 | −#define ZX_DDC_CMD 0x00f3 | |
| 27 | −#define DDC_CMD_MASK 0xf | |
| 28 | −#define DDC_CMD_CLEAR_FIFO 0x9 | |
| 29 | −#define DDC_CMD_SEQUENTIAL_READ 0x2 | |
| 30 | −#define ZX_DDC_DATA 0x00f4 | |
| 31 | −#define ZX_DDC_DOUT_CNT 0x00f5 | |
| 32 | −#define DDC_DOUT_CNT_MASK 0x1f | |
| 33 | −#define TEST_TXCTRL 0x00f7 | |
| 34 | −#define TEST_TXCTRL_HDMI_MODE BIT(1) | |
| 35 | −#define HDMICTL4 0x0235 | |
| 36 | −#define TPI_HPD_RSEN 0x063b | |
| 37 | −#define TPI_HPD_CONNECTION (BIT(1) | BIT(2)) | |
| 38 | −#define TPI_INFO_FSEL 0x06bf | |
| 39 | −#define FSEL_AVI 0 | |
| 40 | −#define FSEL_GBD 1 | |
| 41 | −#define FSEL_AUDIO 2 | |
| 42 | −#define FSEL_SPD 3 | |
| 43 | −#define FSEL_MPEG 4 | |
| 44 | −#define FSEL_VSIF 5 | |
| 45 | −#define TPI_INFO_B0 0x06c0 | |
| 46 | −#define TPI_INFO_EN 0x06df | |
| 47 | −#define TPI_INFO_TRANS_EN BIT(7) | |
| 48 | −#define TPI_INFO_TRANS_RPT BIT(6) | |
| 49 | −#define TPI_DDC_MASTER_EN 0x06f8 | |
| 50 | −#define HW_DDC_MASTER BIT(7) | |
| 51 | −#define N_SVAL1 0xa03 | |
| 52 | −#define N_SVAL2 0xa04 | |
| 53 | −#define N_SVAL3 0xa05 | |
| 54 | −#define AUD_EN 0xa13 | |
| 55 | −#define AUD_IN_EN BIT(0) | |
| 56 | −#define AUD_MODE 0xa14 | |
| 57 | −#define SPDIF_EN BIT(1) | |
| 58 | −#define TPI_AUD_CONFIG 0xa62 | |
| 59 | −#define SPDIF_SAMPLE_SIZE_SHIFT 6 | |
| 60 | −#define SPDIF_SAMPLE_SIZE_MASK (0x3 << SPDIF_SAMPLE_SIZE_SHIFT) | |
| 61 | −#define SPDIF_SAMPLE_SIZE_16BIT (0x1 << SPDIF_SAMPLE_SIZE_SHIFT) | |
| 62 | −#define SPDIF_SAMPLE_SIZE_20BIT (0x2 << SPDIF_SAMPLE_SIZE_SHIFT) | |
| 63 | −#define SPDIF_SAMPLE_SIZE_24BIT (0x3 << SPDIF_SAMPLE_SIZE_SHIFT) | |
| 64 | −#define TPI_AUD_MUTE BIT(4) | |
| 65 | − | |
| 66 | −#endif /* __ZX_HDMI_REGS_H__ */ | |
| @@ -1,537 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#include <drm/drm_atomic.h> | |
| 8 | −#include <drm/drm_atomic_helper.h> | |
| 9 | −#include <drm/drm_fb_cma_helper.h> | |
| 10 | −#include <drm/drm_fourcc.h> | |
| 11 | −#include <drm/drm_gem_cma_helper.h> | |
| 12 | −#include <drm/drm_modeset_helper_vtables.h> | |
| 13 | −#include <drm/drm_plane_helper.h> | |
| 14 | − | |
| 15 | −#include "zx_common_regs.h" | |
| 16 | −#include "zx_drm_drv.h" | |
| 17 | −#include "zx_plane.h" | |
| 18 | −#include "zx_plane_regs.h" | |
| 19 | −#include "zx_vou.h" | |
| 20 | − | |
| 21 | −static const uint32_t gl_formats[] = { | |
| 22 | − DRM_FORMAT_ARGB8888, | |
| 23 | − DRM_FORMAT_XRGB8888, | |
| 24 | − DRM_FORMAT_RGB888, | |
| 25 | − DRM_FORMAT_RGB565, | |
| 26 | − DRM_FORMAT_ARGB1555, | |
| 27 | − DRM_FORMAT_ARGB4444, | |
| 28 | −}; | |
| 29 | − | |
| 30 | −static const uint32_t vl_formats[] = { | |
| 31 | − DRM_FORMAT_NV12, /* Semi-planar YUV420 */ | |
| 32 | − DRM_FORMAT_YUV420, /* Planar YUV420 */ | |
| 33 | − DRM_FORMAT_YUYV, /* Packed YUV422 */ | |
| 34 | − DRM_FORMAT_YVYU, | |
| 35 | − DRM_FORMAT_UYVY, | |
| 36 | − DRM_FORMAT_VYUY, | |
| 37 | − DRM_FORMAT_YUV444, /* YUV444 8bit */ | |
| 38 | − /* | |
| 39 | − * TODO: add formats below that HW supports: | |
| 40 | − * - YUV420 P010 | |
| 41 | − * - YUV420 Hantro | |
| 42 | − * - YUV444 10bit | |
| 43 | − */ | |
| 44 | −}; | |
| 45 | − | |
| 46 | −#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) | |
| 47 | − | |
| 48 | −static int zx_vl_plane_atomic_check(struct drm_plane *plane, | |
| 49 | − struct drm_atomic_state *state) | |
| 50 | −{ | |
| 51 | − struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, | |
| 52 | − plane); | |
| 53 | − struct drm_framebuffer *fb = plane_state->fb; | |
| 54 | − struct drm_crtc *crtc = plane_state->crtc; | |
| 55 | − struct drm_crtc_state *crtc_state; | |
| 56 | − int min_scale = FRAC_16_16(1, 8); | |
| 57 | − int max_scale = FRAC_16_16(8, 1); | |
| 58 | − | |
| 59 | − if (!crtc || WARN_ON(!fb)) | |
| 60 | − return 0; | |
| 61 | − | |
| 62 | − crtc_state = drm_atomic_get_existing_crtc_state(state, | |
| 63 | − crtc); | |
| 64 | − if (WARN_ON(!crtc_state)) | |
| 65 | − return -EINVAL; | |
| 66 | − | |
| 67 | − /* nothing to check when disabling or disabled */ | |
| 68 | − if (!crtc_state->enable) | |
| 69 | − return 0; | |
| 70 | − | |
| 71 | − /* plane must be enabled */ | |
| 72 | − if (!plane_state->crtc) | |
| 73 | − return -EINVAL; | |
| 74 | − | |
| 75 | − return drm_atomic_helper_check_plane_state(plane_state, crtc_state, | |
| 76 | − min_scale, max_scale, | |
| 77 | − true, true); | |
| 78 | −} | |
| 79 | − | |
| 80 | −static int zx_vl_get_fmt(uint32_t format) | |
| 81 | −{ | |
| 82 | − switch (format) { | |
| 83 | − case DRM_FORMAT_NV12: | |
| 84 | − return VL_FMT_YUV420; | |
| 85 | − case DRM_FORMAT_YUV420: | |
| 86 | − return VL_YUV420_PLANAR | VL_FMT_YUV420; | |
| 87 | − case DRM_FORMAT_YUYV: | |
| 88 | − return VL_YUV422_YUYV | VL_FMT_YUV422; | |
| 89 | − case DRM_FORMAT_YVYU: | |
| 90 | − return VL_YUV422_YVYU | VL_FMT_YUV422; | |
| 91 | − case DRM_FORMAT_UYVY: | |
| 92 | − return VL_YUV422_UYVY | VL_FMT_YUV422; | |
| 93 | − case DRM_FORMAT_VYUY: | |
| 94 | − return VL_YUV422_VYUY | VL_FMT_YUV422; | |
| 95 | − case DRM_FORMAT_YUV444: | |
| 96 | − return VL_FMT_YUV444_8BIT; | |
| 97 | − default: | |
| 98 | − WARN_ONCE(1, "invalid pixel format %d\n", format); | |
| 99 | − return -EINVAL; | |
| 100 | − } | |
| 101 | −} | |
| 102 | − | |
| 103 | −static inline void zx_vl_set_update(struct zx_plane *zplane) | |
| 104 | −{ | |
| 105 | − void __iomem *layer = zplane->layer; | |
| 106 | − | |
| 107 | − zx_writel_mask(layer + VL_CTRL0, VL_UPDATE, VL_UPDATE); | |
| 108 | −} | |
| 109 | − | |
| 110 | −static inline void zx_vl_rsz_set_update(struct zx_plane *zplane) | |
| 111 | −{ | |
| 112 | − zx_writel(zplane->rsz + RSZ_VL_ENABLE_CFG, 1); | |
| 113 | −} | |
| 114 | − | |
| 115 | −static int zx_vl_rsz_get_fmt(uint32_t format) | |
| 116 | −{ | |
| 117 | − switch (format) { | |
| 118 | − case DRM_FORMAT_NV12: | |
| 119 | − case DRM_FORMAT_YUV420: | |
| 120 | − return RSZ_VL_FMT_YCBCR420; | |
| 121 | − case DRM_FORMAT_YUYV: | |
| 122 | − case DRM_FORMAT_YVYU: | |
| 123 | − case DRM_FORMAT_UYVY: | |
| 124 | − case DRM_FORMAT_VYUY: | |
| 125 | − return RSZ_VL_FMT_YCBCR422; | |
| 126 | − case DRM_FORMAT_YUV444: | |
| 127 | − return RSZ_VL_FMT_YCBCR444; | |
| 128 | − default: | |
| 129 | − WARN_ONCE(1, "invalid pixel format %d\n", format); | |
| 130 | − return -EINVAL; | |
| 131 | − } | |
| 132 | −} | |
| 133 | − | |
| 134 | −static inline u32 rsz_step_value(u32 src, u32 dst) | |
| 135 | −{ | |
| 136 | − u32 val = 0; | |
| 137 | − | |
| 138 | − if (src == dst) | |
| 139 | − val = 0; | |
| 140 | − else if (src < dst) | |
| 141 | − val = RSZ_PARA_STEP((src << 16) / dst); | |
| 142 | − else if (src > dst) | |
| 143 | − val = RSZ_DATA_STEP(src / dst) | | |
| 144 | − RSZ_PARA_STEP(((src << 16) / dst) & 0xffff); | |
| 145 | − | |
| 146 | − return val; | |
| 147 | −} | |
| 148 | − | |
| 149 | −static void zx_vl_rsz_setup(struct zx_plane *zplane, uint32_t format, | |
| 150 | − u32 src_w, u32 src_h, u32 dst_w, u32 dst_h) | |
| 151 | −{ | |
| 152 | − void __iomem *rsz = zplane->rsz; | |
| 153 | − u32 src_chroma_w = src_w; | |
| 154 | − u32 src_chroma_h = src_h; | |
| 155 | − int fmt; | |
| 156 | − | |
| 157 | − /* Set up source and destination resolution */ | |
| 158 | − zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1)); | |
| 159 | − zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1)); | |
| 160 | − | |
| 161 | − /* Configure data format for VL RSZ */ | |
| 162 | − fmt = zx_vl_rsz_get_fmt(format); | |
| 163 | − if (fmt >= 0) | |
| 164 | − zx_writel_mask(rsz + RSZ_VL_CTRL_CFG, RSZ_VL_FMT_MASK, fmt); | |
| 165 | − | |
| 166 | − /* Calculate Chroma height and width */ | |
| 167 | − if (fmt == RSZ_VL_FMT_YCBCR420) { | |
| 168 | − src_chroma_w = src_w >> 1; | |
| 169 | − src_chroma_h = src_h >> 1; | |
| 170 | − } else if (fmt == RSZ_VL_FMT_YCBCR422) { | |
| 171 | − src_chroma_w = src_w >> 1; | |
| 172 | − } | |
| 173 | − | |
| 174 | − /* Set up Luma and Chroma step registers */ | |
| 175 | − zx_writel(rsz + RSZ_VL_LUMA_HOR, rsz_step_value(src_w, dst_w)); | |
| 176 | − zx_writel(rsz + RSZ_VL_LUMA_VER, rsz_step_value(src_h, dst_h)); | |
| 177 | − zx_writel(rsz + RSZ_VL_CHROMA_HOR, rsz_step_value(src_chroma_w, dst_w)); | |
| 178 | − zx_writel(rsz + RSZ_VL_CHROMA_VER, rsz_step_value(src_chroma_h, dst_h)); | |
| 179 | − | |
| 180 | − zx_vl_rsz_set_update(zplane); | |
| 181 | −} | |
| 182 | − | |
| 183 | −static void zx_vl_plane_atomic_update(struct drm_plane *plane, | |
| 184 | − struct drm_atomic_state *state) | |
| 185 | −{ | |
| 186 | − struct zx_plane *zplane = to_zx_plane(plane); | |
| 187 | − struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, | |
| 188 | − plane); | |
| 189 | − struct drm_framebuffer *fb = new_state->fb; | |
| 190 | − struct drm_rect *src = &new_state->src; | |
| 191 | − struct drm_rect *dst = &new_state->dst; | |
| 192 | − struct drm_gem_cma_object *cma_obj; | |
| 193 | − void __iomem *layer = zplane->layer; | |
| 194 | − void __iomem *hbsc = zplane->hbsc; | |
| 195 | − void __iomem *paddr_reg; | |
| 196 | − dma_addr_t paddr; | |
| 197 | − u32 src_x, src_y, src_w, src_h; | |
| 198 | − u32 dst_x, dst_y, dst_w, dst_h; | |
| 199 | − uint32_t format; | |
| 200 | − int fmt; | |
| 201 | − int i; | |
| 202 | − | |
| 203 | − if (!fb) | |
| 204 | − return; | |
| 205 | − | |
| 206 | − format = fb->format->format; | |
| 207 | − | |
| 208 | − src_x = src->x1 >> 16; | |
| 209 | − src_y = src->y1 >> 16; | |
| 210 | − src_w = drm_rect_width(src) >> 16; | |
| 211 | − src_h = drm_rect_height(src) >> 16; | |
| 212 | − | |
| 213 | − dst_x = dst->x1; | |
| 214 | − dst_y = dst->y1; | |
| 215 | − dst_w = drm_rect_width(dst); | |
| 216 | − dst_h = drm_rect_height(dst); | |
| 217 | − | |
| 218 | − /* Set up data address registers for Y, Cb and Cr planes */ | |
| 219 | − paddr_reg = layer + VL_Y; | |
| 220 | − for (i = 0; i < fb->format->num_planes; i++) { | |
| 221 | − cma_obj = drm_fb_cma_get_gem_obj(fb, i); | |
| 222 | − paddr = cma_obj->paddr + fb->offsets[i]; | |
| 223 | − paddr += src_y * fb->pitches[i]; | |
| 224 | − paddr += src_x * fb->format->cpp[i]; | |
| 225 | − zx_writel(paddr_reg, paddr); | |
| 226 | − paddr_reg += 4; | |
| 227 | − } | |
| 228 | − | |
| 229 | − /* Set up source height/width register */ | |
| 230 | − zx_writel(layer + VL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h)); | |
| 231 | − | |
| 232 | − /* Set up start position register */ | |
| 233 | − zx_writel(layer + VL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y)); | |
| 234 | − | |
| 235 | − /* Set up end position register */ | |
| 236 | − zx_writel(layer + VL_POS_END, | |
| 237 | − GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h)); | |
| 238 | − | |
| 239 | − /* Strides of Cb and Cr planes should be identical */ | |
| 240 | − zx_writel(layer + VL_STRIDE, LUMA_STRIDE(fb->pitches[0]) | | |
| 241 | − CHROMA_STRIDE(fb->pitches[1])); | |
| 242 | − | |
| 243 | − /* Set up video layer data format */ | |
| 244 | − fmt = zx_vl_get_fmt(format); | |
| 245 | − if (fmt >= 0) | |
| 246 | − zx_writel(layer + VL_CTRL1, fmt); | |
| 247 | − | |
| 248 | − /* Always use scaler since it exists (set for not bypass) */ | |
| 249 | − zx_writel_mask(layer + VL_CTRL2, VL_SCALER_BYPASS_MODE, | |
| 250 | − VL_SCALER_BYPASS_MODE); | |
| 251 | − | |
| 252 | − zx_vl_rsz_setup(zplane, format, src_w, src_h, dst_w, dst_h); | |
| 253 | − | |
| 254 | − /* Enable HBSC block */ | |
| 255 | − zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN); | |
| 256 | − | |
| 257 | − zx_vou_layer_enable(plane); | |
| 258 | − | |
| 259 | − zx_vl_set_update(zplane); | |
| 260 | −} | |
| 261 | − | |
| 262 | −static void zx_plane_atomic_disable(struct drm_plane *plane, | |
| 263 | − struct drm_atomic_state *state) | |
| 264 | −{ | |
| 265 | − struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, | |
| 266 | − plane); | |
| 267 | − struct zx_plane *zplane = to_zx_plane(plane); | |
| 268 | − void __iomem *hbsc = zplane->hbsc; | |
| 269 | − | |
| 270 | − zx_vou_layer_disable(plane, old_state); | |
| 271 | − | |
| 272 | − /* Disable HBSC block */ | |
| 273 | − zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, 0); | |
| 274 | −} | |
| 275 | − | |
| 276 | −static const struct drm_plane_helper_funcs zx_vl_plane_helper_funcs = { | |
| 277 | − .atomic_check = zx_vl_plane_atomic_check, | |
| 278 | − .atomic_update = zx_vl_plane_atomic_update, | |
| 279 | − .atomic_disable = zx_plane_atomic_disable, | |
| 280 | −}; | |
| 281 | − | |
| 282 | −static int zx_gl_plane_atomic_check(struct drm_plane *plane, | |
| 283 | − struct drm_atomic_state *state) | |
| 284 | −{ | |
| 285 | − struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, | |
| 286 | − plane); | |
| 287 | − struct drm_framebuffer *fb = plane_state->fb; | |
| 288 | − struct drm_crtc *crtc = plane_state->crtc; | |
| 289 | − struct drm_crtc_state *crtc_state; | |
| 290 | − | |
| 291 | − if (!crtc || WARN_ON(!fb)) | |
| 292 | − return 0; | |
| 293 | − | |
| 294 | − crtc_state = drm_atomic_get_existing_crtc_state(state, | |
| 295 | − crtc); | |
| 296 | − if (WARN_ON(!crtc_state)) | |
| 297 | − return -EINVAL; | |
| 298 | − | |
| 299 | − /* nothing to check when disabling or disabled */ | |
| 300 | − if (!crtc_state->enable) | |
| 301 | − return 0; | |
| 302 | − | |
| 303 | − /* plane must be enabled */ | |
| 304 | − if (!plane_state->crtc) | |
| 305 | − return -EINVAL; | |
| 306 | − | |
| 307 | − return drm_atomic_helper_check_plane_state(plane_state, crtc_state, | |
| 308 | − DRM_PLANE_HELPER_NO_SCALING, | |
| 309 | − DRM_PLANE_HELPER_NO_SCALING, | |
| 310 | − false, true); | |
| 311 | −} | |
| 312 | − | |
| 313 | −static int zx_gl_get_fmt(uint32_t format) | |
| 314 | −{ | |
| 315 | − switch (format) { | |
| 316 | − case DRM_FORMAT_ARGB8888: | |
| 317 | − case DRM_FORMAT_XRGB8888: | |
| 318 | − return GL_FMT_ARGB8888; | |
| 319 | − case DRM_FORMAT_RGB888: | |
| 320 | − return GL_FMT_RGB888; | |
| 321 | − case DRM_FORMAT_RGB565: | |
| 322 | − return GL_FMT_RGB565; | |
| 323 | − case DRM_FORMAT_ARGB1555: | |
| 324 | − return GL_FMT_ARGB1555; | |
| 325 | − case DRM_FORMAT_ARGB4444: | |
| 326 | − return GL_FMT_ARGB4444; | |
| 327 | − default: | |
| 328 | − WARN_ONCE(1, "invalid pixel format %d\n", format); | |
| 329 | − return -EINVAL; | |
| 330 | − } | |
| 331 | −} | |
| 332 | − | |
| 333 | −static inline void zx_gl_set_update(struct zx_plane *zplane) | |
| 334 | −{ | |
| 335 | − void __iomem *layer = zplane->layer; | |
| 336 | − | |
| 337 | − zx_writel_mask(layer + GL_CTRL0, GL_UPDATE, GL_UPDATE); | |
| 338 | −} | |
| 339 | − | |
| 340 | −static inline void zx_gl_rsz_set_update(struct zx_plane *zplane) | |
| 341 | −{ | |
| 342 | − zx_writel(zplane->rsz + RSZ_ENABLE_CFG, 1); | |
| 343 | −} | |
| 344 | − | |
| 345 | −static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h, | |
| 346 | − u32 dst_w, u32 dst_h) | |
| 347 | −{ | |
| 348 | − void __iomem *rsz = zplane->rsz; | |
| 349 | − | |
| 350 | − zx_writel(rsz + RSZ_SRC_CFG, RSZ_VER(src_h - 1) | RSZ_HOR(src_w - 1)); | |
| 351 | − zx_writel(rsz + RSZ_DEST_CFG, RSZ_VER(dst_h - 1) | RSZ_HOR(dst_w - 1)); | |
| 352 | − | |
| 353 | − zx_gl_rsz_set_update(zplane); | |
| 354 | −} | |
| 355 | − | |
| 356 | −static void zx_gl_plane_atomic_update(struct drm_plane *plane, | |
| 357 | − struct drm_atomic_state *state) | |
| 358 | −{ | |
| 359 | − struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, | |
| 360 | − plane); | |
| 361 | − struct zx_plane *zplane = to_zx_plane(plane); | |
| 362 | − struct drm_framebuffer *fb = new_state->fb; | |
| 363 | − struct drm_gem_cma_object *cma_obj; | |
| 364 | − void __iomem *layer = zplane->layer; | |
| 365 | − void __iomem *csc = zplane->csc; | |
| 366 | − void __iomem *hbsc = zplane->hbsc; | |
| 367 | − u32 src_x, src_y, src_w, src_h; | |
| 368 | − u32 dst_x, dst_y, dst_w, dst_h; | |
| 369 | − unsigned int bpp; | |
| 370 | − uint32_t format; | |
| 371 | − dma_addr_t paddr; | |
| 372 | − u32 stride; | |
| 373 | − int fmt; | |
| 374 | − | |
| 375 | − if (!fb) | |
| 376 | − return; | |
| 377 | − | |
| 378 | − format = fb->format->format; | |
| 379 | − stride = fb->pitches[0]; | |
| 380 | − | |
| 381 | − src_x = new_state->src_x >> 16; | |
| 382 | − src_y = new_state->src_y >> 16; | |
| 383 | − src_w = new_state->src_w >> 16; | |
| 384 | − src_h = new_state->src_h >> 16; | |
| 385 | − | |
| 386 | − dst_x = new_state->crtc_x; | |
| 387 | − dst_y = new_state->crtc_y; | |
| 388 | − dst_w = new_state->crtc_w; | |
| 389 | − dst_h = new_state->crtc_h; | |
| 390 | − | |
| 391 | − bpp = fb->format->cpp[0]; | |
| 392 | − | |
| 393 | − cma_obj = drm_fb_cma_get_gem_obj(fb, 0); | |
| 394 | − paddr = cma_obj->paddr + fb->offsets[0]; | |
| 395 | − paddr += src_y * stride + src_x * bpp / 8; | |
| 396 | − zx_writel(layer + GL_ADDR, paddr); | |
| 397 | − | |
| 398 | − /* Set up source height/width register */ | |
| 399 | − zx_writel(layer + GL_SRC_SIZE, GL_SRC_W(src_w) | GL_SRC_H(src_h)); | |
| 400 | − | |
| 401 | − /* Set up start position register */ | |
| 402 | − zx_writel(layer + GL_POS_START, GL_POS_X(dst_x) | GL_POS_Y(dst_y)); | |
| 403 | − | |
| 404 | − /* Set up end position register */ | |
| 405 | − zx_writel(layer + GL_POS_END, | |
| 406 | − GL_POS_X(dst_x + dst_w) | GL_POS_Y(dst_y + dst_h)); | |
| 407 | − | |
| 408 | − /* Set up stride register */ | |
| 409 | − zx_writel(layer + GL_STRIDE, stride & 0xffff); | |
| 410 | − | |
| 411 | − /* Set up graphic layer data format */ | |
| 412 | − fmt = zx_gl_get_fmt(format); | |
| 413 | − if (fmt >= 0) | |
| 414 | − zx_writel_mask(layer + GL_CTRL1, GL_DATA_FMT_MASK, | |
| 415 | − fmt << GL_DATA_FMT_SHIFT); | |
| 416 | − | |
| 417 | − /* Initialize global alpha with a sane value */ | |
| 418 | − zx_writel_mask(layer + GL_CTRL2, GL_GLOBAL_ALPHA_MASK, | |
| 419 | − 0xff << GL_GLOBAL_ALPHA_SHIFT); | |
| 420 | − | |
| 421 | − /* Setup CSC for the GL */ | |
| 422 | − if (dst_h > 720) | |
| 423 | − zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK, | |
| 424 | − CSC_BT709_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT); | |
| 425 | − else | |
| 426 | − zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK, | |
| 427 | − CSC_BT601_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT); | |
| 428 | − zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, CSC_WORK_ENABLE); | |
| 429 | − | |
| 430 | − /* Always use scaler since it exists (set for not bypass) */ | |
| 431 | − zx_writel_mask(layer + GL_CTRL3, GL_SCALER_BYPASS_MODE, | |
| 432 | − GL_SCALER_BYPASS_MODE); | |
| 433 | − | |
| 434 | − zx_gl_rsz_setup(zplane, src_w, src_h, dst_w, dst_h); | |
| 435 | − | |
| 436 | − /* Enable HBSC block */ | |
| 437 | − zx_writel_mask(hbsc + HBSC_CTRL0, HBSC_CTRL_EN, HBSC_CTRL_EN); | |
| 438 | − | |
| 439 | − zx_vou_layer_enable(plane); | |
| 440 | − | |
| 441 | − zx_gl_set_update(zplane); | |
| 442 | −} | |
| 443 | − | |
| 444 | −static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = { | |
| 445 | − .atomic_check = zx_gl_plane_atomic_check, | |
| 446 | − .atomic_update = zx_gl_plane_atomic_update, | |
| 447 | − .atomic_disable = zx_plane_atomic_disable, | |
| 448 | −}; | |
| 449 | − | |
| 450 | −static const struct drm_plane_funcs zx_plane_funcs = { | |
| 451 | − .update_plane = drm_atomic_helper_update_plane, | |
| 452 | − .disable_plane = drm_atomic_helper_disable_plane, | |
| 453 | − .destroy = drm_plane_cleanup, | |
| 454 | − .reset = drm_atomic_helper_plane_reset, | |
| 455 | − .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | |
| 456 | − .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | |
| 457 | −}; | |
| 458 | − | |
| 459 | −void zx_plane_set_update(struct drm_plane *plane) | |
| 460 | −{ | |
| 461 | − struct zx_plane *zplane = to_zx_plane(plane); | |
| 462 | − | |
| 463 | − /* Do nothing if the plane is not enabled */ | |
| 464 | − if (!plane->state->crtc) | |
| 465 | − return; | |
| 466 | − | |
| 467 | − switch (plane->type) { | |
| 468 | − case DRM_PLANE_TYPE_PRIMARY: | |
| 469 | − zx_gl_rsz_set_update(zplane); | |
| 470 | − zx_gl_set_update(zplane); | |
| 471 | − break; | |
| 472 | − case DRM_PLANE_TYPE_OVERLAY: | |
| 473 | − zx_vl_rsz_set_update(zplane); | |
| 474 | − zx_vl_set_update(zplane); | |
| 475 | − break; | |
| 476 | − default: | |
| 477 | − WARN_ONCE(1, "unsupported plane type %d\n", plane->type); | |
| 478 | − } | |
| 479 | −} | |
| 480 | − | |
| 481 | −static void zx_plane_hbsc_init(struct zx_plane *zplane) | |
| 482 | −{ | |
| 483 | − void __iomem *hbsc = zplane->hbsc; | |
| 484 | − | |
| 485 | − /* | |
| 486 | − * Initialize HBSC block with a sane configuration per recommedation | |
| 487 | − * from ZTE BSP code. | |
| 488 | − */ | |
| 489 | − zx_writel(hbsc + HBSC_SATURATION, 0x200); | |
| 490 | − zx_writel(hbsc + HBSC_HUE, 0x0); | |
| 491 | − zx_writel(hbsc + HBSC_BRIGHT, 0x0); | |
| 492 | − zx_writel(hbsc + HBSC_CONTRAST, 0x200); | |
| 493 | − | |
| 494 | − zx_writel(hbsc + HBSC_THRESHOLD_COL1, (0x3ac << 16) | 0x40); | |
| 495 | − zx_writel(hbsc + HBSC_THRESHOLD_COL2, (0x3c0 << 16) | 0x40); | |
| 496 | − zx_writel(hbsc + HBSC_THRESHOLD_COL3, (0x3c0 << 16) | 0x40); | |
| 497 | −} | |
| 498 | − | |
| 499 | −int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane, | |
| 500 | − enum drm_plane_type type) | |
| 501 | −{ | |
| 502 | − const struct drm_plane_helper_funcs *helper; | |
| 503 | − struct drm_plane *plane = &zplane->plane; | |
| 504 | − struct device *dev = zplane->dev; | |
| 505 | − const uint32_t *formats; | |
| 506 | − unsigned int format_count; | |
| 507 | − int ret; | |
| 508 | − | |
| 509 | − zx_plane_hbsc_init(zplane); | |
| 510 | − | |
| 511 | − switch (type) { | |
| 512 | − case DRM_PLANE_TYPE_PRIMARY: | |
| 513 | − helper = &zx_gl_plane_helper_funcs; | |
| 514 | − formats = gl_formats; | |
| 515 | − format_count = ARRAY_SIZE(gl_formats); | |
| 516 | − break; | |
| 517 | − case DRM_PLANE_TYPE_OVERLAY: | |
| 518 | − helper = &zx_vl_plane_helper_funcs; | |
| 519 | − formats = vl_formats; | |
| 520 | − format_count = ARRAY_SIZE(vl_formats); | |
| 521 | − break; | |
| 522 | − default: | |
| 523 | − return -ENODEV; | |
| 524 | − } | |
| 525 | − | |
| 526 | − ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK, | |
| 527 | − &zx_plane_funcs, formats, format_count, | |
| 528 | − NULL, type, NULL); | |
| 529 | − if (ret) { | |
| 530 | − DRM_DEV_ERROR(dev, "failed to init universal plane: %d\n", ret); | |
| 531 | − return ret; | |
| 532 | − } | |
| 533 | − | |
| 534 | − drm_plane_helper_add(plane, helper); | |
| 535 | − | |
| 536 | − return 0; | |
| 537 | −} | |
| @@ -1,26 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_PLANE_H__ | |
| 8 | −#define __ZX_PLANE_H__ | |
| 9 | − | |
| 10 | −struct zx_plane { | |
| 11 | − struct drm_plane plane; | |
| 12 | − struct device *dev; | |
| 13 | − void __iomem *layer; | |
| 14 | − void __iomem *csc; | |
| 15 | − void __iomem *hbsc; | |
| 16 | − void __iomem *rsz; | |
| 17 | − const struct vou_layer_bits *bits; | |
| 18 | −}; | |
| 19 | − | |
| 20 | −#define to_zx_plane(plane) container_of(plane, struct zx_plane, plane) | |
| 21 | − | |
| 22 | −int zx_plane_init(struct drm_device *drm, struct zx_plane *zplane, | |
| 23 | − enum drm_plane_type type); | |
| 24 | −void zx_plane_set_update(struct drm_plane *plane); | |
| 25 | − | |
| 26 | −#endif /* __ZX_PLANE_H__ */ | |
| @@ -1,120 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_PLANE_REGS_H__ | |
| 8 | −#define __ZX_PLANE_REGS_H__ | |
| 9 | − | |
| 10 | −/* GL registers */ | |
| 11 | −#define GL_CTRL0 0x00 | |
| 12 | −#define GL_UPDATE BIT(5) | |
| 13 | −#define GL_CTRL1 0x04 | |
| 14 | −#define GL_DATA_FMT_SHIFT 0 | |
| 15 | −#define GL_DATA_FMT_MASK (0xf << GL_DATA_FMT_SHIFT) | |
| 16 | −#define GL_FMT_ARGB8888 0 | |
| 17 | −#define GL_FMT_RGB888 1 | |
| 18 | −#define GL_FMT_RGB565 2 | |
| 19 | −#define GL_FMT_ARGB1555 3 | |
| 20 | −#define GL_FMT_ARGB4444 4 | |
| 21 | −#define GL_CTRL2 0x08 | |
| 22 | −#define GL_GLOBAL_ALPHA_SHIFT 8 | |
| 23 | −#define GL_GLOBAL_ALPHA_MASK (0xff << GL_GLOBAL_ALPHA_SHIFT) | |
| 24 | −#define GL_CTRL3 0x0c | |
| 25 | −#define GL_SCALER_BYPASS_MODE BIT(0) | |
| 26 | −#define GL_STRIDE 0x18 | |
| 27 | −#define GL_ADDR 0x1c | |
| 28 | −#define GL_SRC_SIZE 0x38 | |
| 29 | −#define GL_SRC_W_SHIFT 16 | |
| 30 | −#define GL_SRC_W_MASK (0x3fff << GL_SRC_W_SHIFT) | |
| 31 | −#define GL_SRC_H_SHIFT 0 | |
| 32 | −#define GL_SRC_H_MASK (0x3fff << GL_SRC_H_SHIFT) | |
| 33 | −#define GL_POS_START 0x9c | |
| 34 | −#define GL_POS_END 0xa0 | |
| 35 | −#define GL_POS_X_SHIFT 16 | |
| 36 | −#define GL_POS_X_MASK (0x1fff << GL_POS_X_SHIFT) | |
| 37 | −#define GL_POS_Y_SHIFT 0 | |
| 38 | −#define GL_POS_Y_MASK (0x1fff << GL_POS_Y_SHIFT) | |
| 39 | − | |
| 40 | −#define GL_SRC_W(x) (((x) << GL_SRC_W_SHIFT) & GL_SRC_W_MASK) | |
| 41 | −#define GL_SRC_H(x) (((x) << GL_SRC_H_SHIFT) & GL_SRC_H_MASK) | |
| 42 | −#define GL_POS_X(x) (((x) << GL_POS_X_SHIFT) & GL_POS_X_MASK) | |
| 43 | −#define GL_POS_Y(x) (((x) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK) | |
| 44 | − | |
| 45 | −/* VL registers */ | |
| 46 | −#define VL_CTRL0 0x00 | |
| 47 | −#define VL_UPDATE BIT(3) | |
| 48 | −#define VL_CTRL1 0x04 | |
| 49 | −#define VL_YUV420_PLANAR BIT(5) | |
| 50 | −#define VL_YUV422_SHIFT 3 | |
| 51 | −#define VL_YUV422_YUYV (0 << VL_YUV422_SHIFT) | |
| 52 | −#define VL_YUV422_YVYU (1 << VL_YUV422_SHIFT) | |
| 53 | −#define VL_YUV422_UYVY (2 << VL_YUV422_SHIFT) | |
| 54 | −#define VL_YUV422_VYUY (3 << VL_YUV422_SHIFT) | |
| 55 | −#define VL_FMT_YUV420 0 | |
| 56 | −#define VL_FMT_YUV422 1 | |
| 57 | −#define VL_FMT_YUV420_P010 2 | |
| 58 | −#define VL_FMT_YUV420_HANTRO 3 | |
| 59 | −#define VL_FMT_YUV444_8BIT 4 | |
| 60 | −#define VL_FMT_YUV444_10BIT 5 | |
| 61 | −#define VL_CTRL2 0x08 | |
| 62 | −#define VL_SCALER_BYPASS_MODE BIT(0) | |
| 63 | −#define VL_STRIDE 0x0c | |
| 64 | −#define LUMA_STRIDE_SHIFT 16 | |
| 65 | −#define LUMA_STRIDE_MASK (0xffff << LUMA_STRIDE_SHIFT) | |
| 66 | −#define CHROMA_STRIDE_SHIFT 0 | |
| 67 | −#define CHROMA_STRIDE_MASK (0xffff << CHROMA_STRIDE_SHIFT) | |
| 68 | −#define VL_SRC_SIZE 0x10 | |
| 69 | −#define VL_Y 0x14 | |
| 70 | −#define VL_POS_START 0x30 | |
| 71 | −#define VL_POS_END 0x34 | |
| 72 | − | |
| 73 | −#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK) | |
| 74 | −#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK) | |
| 75 | − | |
| 76 | −/* RSZ registers */ | |
| 77 | −#define RSZ_SRC_CFG 0x00 | |
| 78 | −#define RSZ_DEST_CFG 0x04 | |
| 79 | −#define RSZ_ENABLE_CFG 0x14 | |
| 80 | − | |
| 81 | −#define RSZ_VL_LUMA_HOR 0x08 | |
| 82 | −#define RSZ_VL_LUMA_VER 0x0c | |
| 83 | −#define RSZ_VL_CHROMA_HOR 0x10 | |
| 84 | −#define RSZ_VL_CHROMA_VER 0x14 | |
| 85 | −#define RSZ_VL_CTRL_CFG 0x18 | |
| 86 | −#define RSZ_VL_FMT_SHIFT 3 | |
| 87 | −#define RSZ_VL_FMT_MASK (0x3 << RSZ_VL_FMT_SHIFT) | |
| 88 | −#define RSZ_VL_FMT_YCBCR420 (0x0 << RSZ_VL_FMT_SHIFT) | |
| 89 | −#define RSZ_VL_FMT_YCBCR422 (0x1 << RSZ_VL_FMT_SHIFT) | |
| 90 | −#define RSZ_VL_FMT_YCBCR444 (0x2 << RSZ_VL_FMT_SHIFT) | |
| 91 | −#define RSZ_VL_ENABLE_CFG 0x1c | |
| 92 | − | |
| 93 | −#define RSZ_VER_SHIFT 16 | |
| 94 | −#define RSZ_VER_MASK (0xffff << RSZ_VER_SHIFT) | |
| 95 | −#define RSZ_HOR_SHIFT 0 | |
| 96 | −#define RSZ_HOR_MASK (0xffff << RSZ_HOR_SHIFT) | |
| 97 | − | |
| 98 | −#define RSZ_VER(x) (((x) << RSZ_VER_SHIFT) & RSZ_VER_MASK) | |
| 99 | −#define RSZ_HOR(x) (((x) << RSZ_HOR_SHIFT) & RSZ_HOR_MASK) | |
| 100 | − | |
| 101 | −#define RSZ_DATA_STEP_SHIFT 16 | |
| 102 | −#define RSZ_DATA_STEP_MASK (0xffff << RSZ_DATA_STEP_SHIFT) | |
| 103 | −#define RSZ_PARA_STEP_SHIFT 0 | |
| 104 | −#define RSZ_PARA_STEP_MASK (0xffff << RSZ_PARA_STEP_SHIFT) | |
| 105 | − | |
| 106 | −#define RSZ_DATA_STEP(x) (((x) << RSZ_DATA_STEP_SHIFT) & RSZ_DATA_STEP_MASK) | |
| 107 | −#define RSZ_PARA_STEP(x) (((x) << RSZ_PARA_STEP_SHIFT) & RSZ_PARA_STEP_MASK) | |
| 108 | − | |
| 109 | −/* HBSC registers */ | |
| 110 | −#define HBSC_SATURATION 0x00 | |
| 111 | −#define HBSC_HUE 0x04 | |
| 112 | −#define HBSC_BRIGHT 0x08 | |
| 113 | −#define HBSC_CONTRAST 0x0c | |
| 114 | −#define HBSC_THRESHOLD_COL1 0x10 | |
| 115 | −#define HBSC_THRESHOLD_COL2 0x14 | |
| 116 | −#define HBSC_THRESHOLD_COL3 0x18 | |
| 117 | −#define HBSC_CTRL0 0x28 | |
| 118 | −#define HBSC_CTRL_EN BIT(2) | |
| 119 | − | |
| 120 | −#endif /* __ZX_PLANE_REGS_H__ */ | |
| @@ -1,400 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* | |
| 3 | − * Copyright 2017 Linaro Ltd. | |
| 4 | − * Copyright 2017 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#include <linux/clk.h> | |
| 8 | −#include <linux/component.h> | |
| 9 | −#include <linux/mfd/syscon.h> | |
| 10 | −#include <linux/module.h> | |
| 11 | −#include <linux/platform_device.h> | |
| 12 | −#include <linux/regmap.h> | |
| 13 | − | |
| 14 | −#include <drm/drm_atomic_helper.h> | |
| 15 | −#include <drm/drm_print.h> | |
| 16 | −#include <drm/drm_probe_helper.h> | |
| 17 | −#include <drm/drm_simple_kms_helper.h> | |
| 18 | − | |
| 19 | −#include "zx_drm_drv.h" | |
| 20 | −#include "zx_tvenc_regs.h" | |
| 21 | −#include "zx_vou.h" | |
| 22 | − | |
| 23 | −struct zx_tvenc_pwrctrl { | |
| 24 | − struct regmap *regmap; | |
| 25 | − u32 reg; | |
| 26 | − u32 mask; | |
| 27 | −}; | |
| 28 | − | |
| 29 | −struct zx_tvenc { | |
| 30 | − struct drm_connector connector; | |
| 31 | − struct drm_encoder encoder; | |
| 32 | − struct device *dev; | |
| 33 | − void __iomem *mmio; | |
| 34 | − const struct vou_inf *inf; | |
| 35 | − struct zx_tvenc_pwrctrl pwrctrl; | |
| 36 | −}; | |
| 37 | − | |
| 38 | −#define to_zx_tvenc(x) container_of(x, struct zx_tvenc, x) | |
| 39 | − | |
| 40 | −struct zx_tvenc_mode { | |
| 41 | − struct drm_display_mode mode; | |
| 42 | − u32 video_info; | |
| 43 | − u32 video_res; | |
| 44 | − u32 field1_param; | |
| 45 | − u32 field2_param; | |
| 46 | − u32 burst_line_odd1; | |
| 47 | − u32 burst_line_even1; | |
| 48 | − u32 burst_line_odd2; | |
| 49 | − u32 burst_line_even2; | |
| 50 | − u32 line_timing_param; | |
| 51 | − u32 weight_value; | |
| 52 | − u32 blank_black_level; | |
| 53 | − u32 burst_level; | |
| 54 | − u32 control_param; | |
| 55 | − u32 sub_carrier_phase1; | |
| 56 | − u32 phase_line_incr_cvbs; | |
| 57 | −}; | |
| 58 | − | |
| 59 | −/* | |
| 60 | − * The CRM cannot directly provide a suitable frequency, and we have to | |
| 61 | − * ask a multiplied rate from CRM and use the divider in VOU to get the | |
| 62 | − * desired one. | |
| 63 | − */ | |
| 64 | −#define TVENC_CLOCK_MULTIPLIER 4 | |
| 65 | − | |
| 66 | −static const struct zx_tvenc_mode tvenc_mode_pal = { | |
| 67 | − .mode = { | |
| 68 | − .clock = 13500 * TVENC_CLOCK_MULTIPLIER, | |
| 69 | − .hdisplay = 720, | |
| 70 | − .hsync_start = 720 + 12, | |
| 71 | − .hsync_end = 720 + 12 + 2, | |
| 72 | − .htotal = 720 + 12 + 2 + 130, | |
| 73 | − .vdisplay = 576, | |
| 74 | − .vsync_start = 576 + 2, | |
| 75 | − .vsync_end = 576 + 2 + 2, | |
| 76 | − .vtotal = 576 + 2 + 2 + 20, | |
| 77 | − .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | |
| 78 | − DRM_MODE_FLAG_INTERLACE, | |
| 79 | − }, | |
| 80 | − .video_info = 0x00040040, | |
| 81 | − .video_res = 0x05a9c760, | |
| 82 | − .field1_param = 0x0004d416, | |
| 83 | − .field2_param = 0x0009b94f, | |
| 84 | − .burst_line_odd1 = 0x0004d406, | |
| 85 | − .burst_line_even1 = 0x0009b53e, | |
| 86 | − .burst_line_odd2 = 0x0004d805, | |
| 87 | − .burst_line_even2 = 0x0009b93f, | |
| 88 | − .line_timing_param = 0x06a96fdf, | |
| 89 | − .weight_value = 0x00c188a0, | |
| 90 | − .blank_black_level = 0x0000fcfc, | |
| 91 | − .burst_level = 0x00001595, | |
| 92 | − .control_param = 0x00000001, | |
| 93 | − .sub_carrier_phase1 = 0x1504c566, | |
| 94 | − .phase_line_incr_cvbs = 0xc068db8c, | |
| 95 | −}; | |
| 96 | − | |
| 97 | −static const struct zx_tvenc_mode tvenc_mode_ntsc = { | |
| 98 | − .mode = { | |
| 99 | − .clock = 13500 * TVENC_CLOCK_MULTIPLIER, | |
| 100 | − .hdisplay = 720, | |
| 101 | − .hsync_start = 720 + 16, | |
| 102 | − .hsync_end = 720 + 16 + 2, | |
| 103 | − .htotal = 720 + 16 + 2 + 120, | |
| 104 | − .vdisplay = 480, | |
| 105 | − .vsync_start = 480 + 3, | |
| 106 | − .vsync_end = 480 + 3 + 2, | |
| 107 | − .vtotal = 480 + 3 + 2 + 17, | |
| 108 | − .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | | |
| 109 | − DRM_MODE_FLAG_INTERLACE, | |
| 110 | − }, | |
| 111 | − .video_info = 0x00040080, | |
| 112 | − .video_res = 0x05a8375a, | |
| 113 | − .field1_param = 0x00041817, | |
| 114 | − .field2_param = 0x0008351e, | |
| 115 | − .burst_line_odd1 = 0x00041006, | |
| 116 | − .burst_line_even1 = 0x0008290d, | |
| 117 | − .burst_line_odd2 = 0x00000000, | |
| 118 | − .burst_line_even2 = 0x00000000, | |
| 119 | − .line_timing_param = 0x06a8ef9e, | |
| 120 | − .weight_value = 0x00b68197, | |
| 121 | − .blank_black_level = 0x0000f0f0, | |
| 122 | − .burst_level = 0x0000009c, | |
| 123 | − .control_param = 0x00000001, | |
| 124 | − .sub_carrier_phase1 = 0x10f83e10, | |
| 125 | − .phase_line_incr_cvbs = 0x80000000, | |
| 126 | −}; | |
| 127 | − | |
| 128 | −static const struct zx_tvenc_mode *tvenc_modes[] = { | |
| 129 | − &tvenc_mode_pal, | |
| 130 | − &tvenc_mode_ntsc, | |
| 131 | −}; | |
| 132 | − | |
| 133 | −static const struct zx_tvenc_mode * | |
| 134 | −zx_tvenc_find_zmode(struct drm_display_mode *mode) | |
| 135 | −{ | |
| 136 | − int i; | |
| 137 | − | |
| 138 | − for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) { | |
| 139 | − const struct zx_tvenc_mode *zmode = tvenc_modes[i]; | |
| 140 | − | |
| 141 | − if (drm_mode_equal(mode, &zmode->mode)) | |
| 142 | − return zmode; | |
| 143 | − } | |
| 144 | − | |
| 145 | − return NULL; | |
| 146 | −} | |
| 147 | − | |
| 148 | −static void zx_tvenc_encoder_mode_set(struct drm_encoder *encoder, | |
| 149 | − struct drm_display_mode *mode, | |
| 150 | − struct drm_display_mode *adj_mode) | |
| 151 | −{ | |
| 152 | − struct zx_tvenc *tvenc = to_zx_tvenc(encoder); | |
| 153 | − const struct zx_tvenc_mode *zmode; | |
| 154 | − struct vou_div_config configs[] = { | |
| 155 | − { VOU_DIV_INF, VOU_DIV_4 }, | |
| 156 | − { VOU_DIV_TVENC, VOU_DIV_1 }, | |
| 157 | − { VOU_DIV_LAYER, VOU_DIV_2 }, | |
| 158 | − }; | |
| 159 | − | |
| 160 | − zx_vou_config_dividers(encoder->crtc, configs, ARRAY_SIZE(configs)); | |
| 161 | − | |
| 162 | − zmode = zx_tvenc_find_zmode(mode); | |
| 163 | − if (!zmode) { | |
| 164 | − DRM_DEV_ERROR(tvenc->dev, "failed to find zmode\n"); | |
| 165 | − return; | |
| 166 | − } | |
| 167 | − | |
| 168 | − zx_writel(tvenc->mmio + VENC_VIDEO_INFO, zmode->video_info); | |
| 169 | − zx_writel(tvenc->mmio + VENC_VIDEO_RES, zmode->video_res); | |
| 170 | − zx_writel(tvenc->mmio + VENC_FIELD1_PARAM, zmode->field1_param); | |
| 171 | − zx_writel(tvenc->mmio + VENC_FIELD2_PARAM, zmode->field2_param); | |
| 172 | − zx_writel(tvenc->mmio + VENC_LINE_O_1, zmode->burst_line_odd1); | |
| 173 | − zx_writel(tvenc->mmio + VENC_LINE_E_1, zmode->burst_line_even1); | |
| 174 | − zx_writel(tvenc->mmio + VENC_LINE_O_2, zmode->burst_line_odd2); | |
| 175 | − zx_writel(tvenc->mmio + VENC_LINE_E_2, zmode->burst_line_even2); | |
| 176 | − zx_writel(tvenc->mmio + VENC_LINE_TIMING_PARAM, | |
| 177 | − zmode->line_timing_param); | |
| 178 | − zx_writel(tvenc->mmio + VENC_WEIGHT_VALUE, zmode->weight_value); | |
| 179 | − zx_writel(tvenc->mmio + VENC_BLANK_BLACK_LEVEL, | |
| 180 | − zmode->blank_black_level); | |
| 181 | − zx_writel(tvenc->mmio + VENC_BURST_LEVEL, zmode->burst_level); | |
| 182 | − zx_writel(tvenc->mmio + VENC_CONTROL_PARAM, zmode->control_param); | |
| 183 | − zx_writel(tvenc->mmio + VENC_SUB_CARRIER_PHASE1, | |
| 184 | − zmode->sub_carrier_phase1); | |
| 185 | − zx_writel(tvenc->mmio + VENC_PHASE_LINE_INCR_CVBS, | |
| 186 | − zmode->phase_line_incr_cvbs); | |
| 187 | −} | |
| 188 | − | |
| 189 | −static void zx_tvenc_encoder_enable(struct drm_encoder *encoder) | |
| 190 | −{ | |
| 191 | − struct zx_tvenc *tvenc = to_zx_tvenc(encoder); | |
| 192 | − struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl; | |
| 193 | − | |
| 194 | − /* Set bit to power up TVENC DAC */ | |
| 195 | − regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, | |
| 196 | − pwrctrl->mask); | |
| 197 | − | |
| 198 | − vou_inf_enable(VOU_TV_ENC, encoder->crtc); | |
| 199 | − | |
| 200 | − zx_writel(tvenc->mmio + VENC_ENABLE, 1); | |
| 201 | −} | |
| 202 | − | |
| 203 | −static void zx_tvenc_encoder_disable(struct drm_encoder *encoder) | |
| 204 | −{ | |
| 205 | − struct zx_tvenc *tvenc = to_zx_tvenc(encoder); | |
| 206 | − struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl; | |
| 207 | − | |
| 208 | − zx_writel(tvenc->mmio + VENC_ENABLE, 0); | |
| 209 | − | |
| 210 | − vou_inf_disable(VOU_TV_ENC, encoder->crtc); | |
| 211 | − | |
| 212 | − /* Clear bit to power down TVENC DAC */ | |
| 213 | − regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0); | |
| 214 | −} | |
| 215 | − | |
| 216 | −static const struct drm_encoder_helper_funcs zx_tvenc_encoder_helper_funcs = { | |
| 217 | − .enable = zx_tvenc_encoder_enable, | |
| 218 | − .disable = zx_tvenc_encoder_disable, | |
| 219 | − .mode_set = zx_tvenc_encoder_mode_set, | |
| 220 | −}; | |
| 221 | − | |
| 222 | −static int zx_tvenc_connector_get_modes(struct drm_connector *connector) | |
| 223 | −{ | |
| 224 | − struct zx_tvenc *tvenc = to_zx_tvenc(connector); | |
| 225 | − struct device *dev = tvenc->dev; | |
| 226 | − int i; | |
| 227 | − | |
| 228 | − for (i = 0; i < ARRAY_SIZE(tvenc_modes); i++) { | |
| 229 | − const struct zx_tvenc_mode *zmode = tvenc_modes[i]; | |
| 230 | − struct drm_display_mode *mode; | |
| 231 | − | |
| 232 | − mode = drm_mode_duplicate(connector->dev, &zmode->mode); | |
| 233 | − if (!mode) { | |
| 234 | − DRM_DEV_ERROR(dev, "failed to duplicate drm mode\n"); | |
| 235 | − continue; | |
| 236 | − } | |
| 237 | − | |
| 238 | − drm_mode_set_name(mode); | |
| 239 | − drm_mode_probed_add(connector, mode); | |
| 240 | − } | |
| 241 | − | |
| 242 | − return i; | |
| 243 | −} | |
| 244 | − | |
| 245 | −static enum drm_mode_status | |
| 246 | −zx_tvenc_connector_mode_valid(struct drm_connector *connector, | |
| 247 | − struct drm_display_mode *mode) | |
| 248 | −{ | |
| 249 | − struct zx_tvenc *tvenc = to_zx_tvenc(connector); | |
| 250 | − const struct zx_tvenc_mode *zmode; | |
| 251 | − | |
| 252 | − zmode = zx_tvenc_find_zmode(mode); | |
| 253 | − if (!zmode) { | |
| 254 | − DRM_DEV_ERROR(tvenc->dev, "unsupported mode: %s\n", mode->name); | |
| 255 | − return MODE_NOMODE; | |
| 256 | − } | |
| 257 | − | |
| 258 | − return MODE_OK; | |
| 259 | −} | |
| 260 | − | |
| 261 | −static struct drm_connector_helper_funcs zx_tvenc_connector_helper_funcs = { | |
| 262 | − .get_modes = zx_tvenc_connector_get_modes, | |
| 263 | − .mode_valid = zx_tvenc_connector_mode_valid, | |
| 264 | −}; | |
| 265 | − | |
| 266 | −static const struct drm_connector_funcs zx_tvenc_connector_funcs = { | |
| 267 | − .fill_modes = drm_helper_probe_single_connector_modes, | |
| 268 | − .destroy = drm_connector_cleanup, | |
| 269 | − .reset = drm_atomic_helper_connector_reset, | |
| 270 | − .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 271 | − .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 272 | −}; | |
| 273 | − | |
| 274 | −static int zx_tvenc_register(struct drm_device *drm, struct zx_tvenc *tvenc) | |
| 275 | −{ | |
| 276 | − struct drm_encoder *encoder = &tvenc->encoder; | |
| 277 | − struct drm_connector *connector = &tvenc->connector; | |
| 278 | − | |
| 279 | − /* | |
| 280 | − * The tvenc is designed to use aux channel, as there is a deflicker | |
| 281 | − * block for the channel. | |
| 282 | − */ | |
| 283 | − encoder->possible_crtcs = BIT(1); | |
| 284 | − | |
| 285 | − drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TVDAC); | |
| 286 | − drm_encoder_helper_add(encoder, &zx_tvenc_encoder_helper_funcs); | |
| 287 | − | |
| 288 | − connector->interlace_allowed = true; | |
| 289 | − | |
| 290 | − drm_connector_init(drm, connector, &zx_tvenc_connector_funcs, | |
| 291 | − DRM_MODE_CONNECTOR_Composite); | |
| 292 | − drm_connector_helper_add(connector, &zx_tvenc_connector_helper_funcs); | |
| 293 | − | |
| 294 | − drm_connector_attach_encoder(connector, encoder); | |
| 295 | − | |
| 296 | − return 0; | |
| 297 | −} | |
| 298 | − | |
| 299 | −static int zx_tvenc_pwrctrl_init(struct zx_tvenc *tvenc) | |
| 300 | −{ | |
| 301 | − struct zx_tvenc_pwrctrl *pwrctrl = &tvenc->pwrctrl; | |
| 302 | − struct device *dev = tvenc->dev; | |
| 303 | − struct of_phandle_args out_args; | |
| 304 | − struct regmap *regmap; | |
| 305 | − int ret; | |
| 306 | − | |
| 307 | − ret = of_parse_phandle_with_fixed_args(dev->of_node, | |
| 308 | − "zte,tvenc-power-control", 2, 0, &out_args); | |
| 309 | − if (ret) | |
| 310 | − return ret; | |
| 311 | − | |
| 312 | − regmap = syscon_node_to_regmap(out_args.np); | |
| 313 | − if (IS_ERR(regmap)) { | |
| 314 | − ret = PTR_ERR(regmap); | |
| 315 | − goto out; | |
| 316 | − } | |
| 317 | − | |
| 318 | − pwrctrl->regmap = regmap; | |
| 319 | − pwrctrl->reg = out_args.args[0]; | |
| 320 | − pwrctrl->mask = out_args.args[1]; | |
| 321 | − | |
| 322 | −out: | |
| 323 | − of_node_put(out_args.np); | |
| 324 | − return ret; | |
| 325 | −} | |
| 326 | − | |
| 327 | −static int zx_tvenc_bind(struct device *dev, struct device *master, void *data) | |
| 328 | −{ | |
| 329 | − struct platform_device *pdev = to_platform_device(dev); | |
| 330 | − struct drm_device *drm = data; | |
| 331 | − struct resource *res; | |
| 332 | − struct zx_tvenc *tvenc; | |
| 333 | − int ret; | |
| 334 | − | |
| 335 | − tvenc = devm_kzalloc(dev, sizeof(*tvenc), GFP_KERNEL); | |
| 336 | − if (!tvenc) | |
| 337 | − return -ENOMEM; | |
| 338 | − | |
| 339 | − tvenc->dev = dev; | |
| 340 | − dev_set_drvdata(dev, tvenc); | |
| 341 | − | |
| 342 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 343 | − tvenc->mmio = devm_ioremap_resource(dev, res); | |
| 344 | − if (IS_ERR(tvenc->mmio)) { | |
| 345 | − ret = PTR_ERR(tvenc->mmio); | |
| 346 | − DRM_DEV_ERROR(dev, "failed to remap tvenc region: %d\n", ret); | |
| 347 | − return ret; | |
| 348 | − } | |
| 349 | − | |
| 350 | − ret = zx_tvenc_pwrctrl_init(tvenc); | |
| 351 | − if (ret) { | |
| 352 | − DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret); | |
| 353 | − return ret; | |
| 354 | − } | |
| 355 | − | |
| 356 | − ret = zx_tvenc_register(drm, tvenc); | |
| 357 | − if (ret) { | |
| 358 | − DRM_DEV_ERROR(dev, "failed to register tvenc: %d\n", ret); | |
| 359 | − return ret; | |
| 360 | − } | |
| 361 | − | |
| 362 | − return 0; | |
| 363 | −} | |
| 364 | − | |
| 365 | −static void zx_tvenc_unbind(struct device *dev, struct device *master, | |
| 366 | − void *data) | |
| 367 | −{ | |
| 368 | − /* Nothing to do */ | |
| 369 | −} | |
| 370 | − | |
| 371 | −static const struct component_ops zx_tvenc_component_ops = { | |
| 372 | − .bind = zx_tvenc_bind, | |
| 373 | − .unbind = zx_tvenc_unbind, | |
| 374 | −}; | |
| 375 | − | |
| 376 | −static int zx_tvenc_probe(struct platform_device *pdev) | |
| 377 | −{ | |
| 378 | − return component_add(&pdev->dev, &zx_tvenc_component_ops); | |
| 379 | −} | |
| 380 | − | |
| 381 | −static int zx_tvenc_remove(struct platform_device *pdev) | |
| 382 | −{ | |
| 383 | − component_del(&pdev->dev, &zx_tvenc_component_ops); | |
| 384 | − return 0; | |
| 385 | −} | |
| 386 | − | |
| 387 | −static const struct of_device_id zx_tvenc_of_match[] = { | |
| 388 | − { .compatible = "zte,zx296718-tvenc", }, | |
| 389 | − { /* end */ }, | |
| 390 | −}; | |
| 391 | −MODULE_DEVICE_TABLE(of, zx_tvenc_of_match); | |
| 392 | − | |
| 393 | −struct platform_driver zx_tvenc_driver = { | |
| 394 | − .probe = zx_tvenc_probe, | |
| 395 | − .remove = zx_tvenc_remove, | |
| 396 | − .driver = { | |
| 397 | − .name = "zx-tvenc", | |
| 398 | − .of_match_table = zx_tvenc_of_match, | |
| 399 | − }, | |
| 400 | −}; | |
| @@ -1,27 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright 2017 Linaro Ltd. | |
| 4 | − * Copyright 2017 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_TVENC_REGS_H__ | |
| 8 | −#define __ZX_TVENC_REGS_H__ | |
| 9 | − | |
| 10 | −#define VENC_VIDEO_INFO 0x04 | |
| 11 | −#define VENC_VIDEO_RES 0x08 | |
| 12 | −#define VENC_FIELD1_PARAM 0x10 | |
| 13 | −#define VENC_FIELD2_PARAM 0x14 | |
| 14 | −#define VENC_LINE_O_1 0x18 | |
| 15 | −#define VENC_LINE_E_1 0x1c | |
| 16 | −#define VENC_LINE_O_2 0x20 | |
| 17 | −#define VENC_LINE_E_2 0x24 | |
| 18 | −#define VENC_LINE_TIMING_PARAM 0x28 | |
| 19 | −#define VENC_WEIGHT_VALUE 0x2c | |
| 20 | −#define VENC_BLANK_BLACK_LEVEL 0x30 | |
| 21 | −#define VENC_BURST_LEVEL 0x34 | |
| 22 | −#define VENC_CONTROL_PARAM 0x3c | |
| 23 | −#define VENC_SUB_CARRIER_PHASE1 0x40 | |
| 24 | −#define VENC_PHASE_LINE_INCR_CVBS 0x48 | |
| 25 | −#define VENC_ENABLE 0xa8 | |
| 26 | − | |
| 27 | −#endif /* __ZX_TVENC_REGS_H__ */ | |
| @@ -1,527 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* | |
| 3 | − * Copyright (C) 2017 Sanechips Technology Co., Ltd. | |
| 4 | − * Copyright 2017 Linaro Ltd. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#include <linux/clk.h> | |
| 8 | −#include <linux/component.h> | |
| 9 | −#include <linux/mfd/syscon.h> | |
| 10 | −#include <linux/module.h> | |
| 11 | −#include <linux/platform_device.h> | |
| 12 | −#include <linux/regmap.h> | |
| 13 | − | |
| 14 | −#include <drm/drm_atomic_helper.h> | |
| 15 | −#include <drm/drm_print.h> | |
| 16 | −#include <drm/drm_probe_helper.h> | |
| 17 | −#include <drm/drm_simple_kms_helper.h> | |
| 18 | − | |
| 19 | −#include "zx_drm_drv.h" | |
| 20 | −#include "zx_vga_regs.h" | |
| 21 | −#include "zx_vou.h" | |
| 22 | − | |
| 23 | −struct zx_vga_pwrctrl { | |
| 24 | − struct regmap *regmap; | |
| 25 | − u32 reg; | |
| 26 | − u32 mask; | |
| 27 | −}; | |
| 28 | − | |
| 29 | −struct zx_vga_i2c { | |
| 30 | − struct i2c_adapter adap; | |
| 31 | − struct mutex lock; | |
| 32 | −}; | |
| 33 | − | |
| 34 | −struct zx_vga { | |
| 35 | − struct drm_connector connector; | |
| 36 | − struct drm_encoder encoder; | |
| 37 | − struct zx_vga_i2c *ddc; | |
| 38 | − struct device *dev; | |
| 39 | − void __iomem *mmio; | |
| 40 | − struct clk *i2c_wclk; | |
| 41 | − struct zx_vga_pwrctrl pwrctrl; | |
| 42 | − struct completion complete; | |
| 43 | − bool connected; | |
| 44 | −}; | |
| 45 | − | |
| 46 | −#define to_zx_vga(x) container_of(x, struct zx_vga, x) | |
| 47 | − | |
| 48 | −static void zx_vga_encoder_enable(struct drm_encoder *encoder) | |
| 49 | −{ | |
| 50 | − struct zx_vga *vga = to_zx_vga(encoder); | |
| 51 | − struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl; | |
| 52 | − | |
| 53 | − /* Set bit to power up VGA DACs */ | |
| 54 | − regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, | |
| 55 | − pwrctrl->mask); | |
| 56 | − | |
| 57 | − vou_inf_enable(VOU_VGA, encoder->crtc); | |
| 58 | −} | |
| 59 | − | |
| 60 | −static void zx_vga_encoder_disable(struct drm_encoder *encoder) | |
| 61 | −{ | |
| 62 | − struct zx_vga *vga = to_zx_vga(encoder); | |
| 63 | − struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl; | |
| 64 | − | |
| 65 | − vou_inf_disable(VOU_VGA, encoder->crtc); | |
| 66 | − | |
| 67 | − /* Clear bit to power down VGA DACs */ | |
| 68 | − regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0); | |
| 69 | −} | |
| 70 | − | |
| 71 | −static const struct drm_encoder_helper_funcs zx_vga_encoder_helper_funcs = { | |
| 72 | − .enable = zx_vga_encoder_enable, | |
| 73 | − .disable = zx_vga_encoder_disable, | |
| 74 | −}; | |
| 75 | − | |
| 76 | −static int zx_vga_connector_get_modes(struct drm_connector *connector) | |
| 77 | −{ | |
| 78 | − struct zx_vga *vga = to_zx_vga(connector); | |
| 79 | − struct edid *edid; | |
| 80 | − int ret; | |
| 81 | − | |
| 82 | − /* | |
| 83 | − * Clear both detection bits to switch I2C bus from device | |
| 84 | − * detecting to EDID reading. | |
| 85 | − */ | |
| 86 | − zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0); | |
| 87 | − | |
| 88 | − edid = drm_get_edid(connector, &vga->ddc->adap); | |
| 89 | − if (!edid) { | |
| 90 | − /* | |
| 91 | − * If EDID reading fails, we set the device state into | |
| 92 | − * disconnected. Locking is not required here, since the | |
| 93 | − * VGA_AUTO_DETECT_SEL register write in irq handler cannot | |
| 94 | − * be triggered when both detection bits are cleared as above. | |
| 95 | − */ | |
| 96 | − zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, | |
| 97 | − VGA_DETECT_SEL_NO_DEVICE); | |
| 98 | − vga->connected = false; | |
| 99 | − return 0; | |
| 100 | − } | |
| 101 | − | |
| 102 | − /* | |
| 103 | − * As edid reading succeeds, device must be connected, so we set | |
| 104 | − * up detection bit for unplug interrupt here. | |
| 105 | − */ | |
| 106 | − zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE); | |
| 107 | − | |
| 108 | − drm_connector_update_edid_property(connector, edid); | |
| 109 | − ret = drm_add_edid_modes(connector, edid); | |
| 110 | − kfree(edid); | |
| 111 | − | |
| 112 | − return ret; | |
| 113 | −} | |
| 114 | − | |
| 115 | −static enum drm_mode_status | |
| 116 | −zx_vga_connector_mode_valid(struct drm_connector *connector, | |
| 117 | − struct drm_display_mode *mode) | |
| 118 | −{ | |
| 119 | − return MODE_OK; | |
| 120 | −} | |
| 121 | − | |
| 122 | −static struct drm_connector_helper_funcs zx_vga_connector_helper_funcs = { | |
| 123 | − .get_modes = zx_vga_connector_get_modes, | |
| 124 | − .mode_valid = zx_vga_connector_mode_valid, | |
| 125 | −}; | |
| 126 | − | |
| 127 | −static enum drm_connector_status | |
| 128 | −zx_vga_connector_detect(struct drm_connector *connector, bool force) | |
| 129 | −{ | |
| 130 | − struct zx_vga *vga = to_zx_vga(connector); | |
| 131 | − | |
| 132 | − return vga->connected ? connector_status_connected : | |
| 133 | − connector_status_disconnected; | |
| 134 | −} | |
| 135 | − | |
| 136 | −static const struct drm_connector_funcs zx_vga_connector_funcs = { | |
| 137 | − .fill_modes = drm_helper_probe_single_connector_modes, | |
| 138 | − .detect = zx_vga_connector_detect, | |
| 139 | − .destroy = drm_connector_cleanup, | |
| 140 | − .reset = drm_atomic_helper_connector_reset, | |
| 141 | − .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
| 142 | − .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
| 143 | −}; | |
| 144 | − | |
| 145 | −static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga) | |
| 146 | −{ | |
| 147 | − struct drm_encoder *encoder = &vga->encoder; | |
| 148 | − struct drm_connector *connector = &vga->connector; | |
| 149 | − struct device *dev = vga->dev; | |
| 150 | − int ret; | |
| 151 | − | |
| 152 | − encoder->possible_crtcs = VOU_CRTC_MASK; | |
| 153 | − | |
| 154 | − ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DAC); | |
| 155 | − if (ret) { | |
| 156 | − DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret); | |
| 157 | − return ret; | |
| 158 | − } | |
| 159 | − | |
| 160 | − drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs); | |
| 161 | − | |
| 162 | − vga->connector.polled = DRM_CONNECTOR_POLL_HPD; | |
| 163 | − | |
| 164 | − ret = drm_connector_init_with_ddc(drm, connector, | |
| 165 | − &zx_vga_connector_funcs, | |
| 166 | − DRM_MODE_CONNECTOR_VGA, | |
| 167 | − &vga->ddc->adap); | |
| 168 | − if (ret) { | |
| 169 | − DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret); | |
| 170 | − goto clean_encoder; | |
| 171 | − } | |
| 172 | − | |
| 173 | − drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs); | |
| 174 | − | |
| 175 | − ret = drm_connector_attach_encoder(connector, encoder); | |
| 176 | − if (ret) { | |
| 177 | − DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret); | |
| 178 | − goto clean_connector; | |
| 179 | − } | |
| 180 | − | |
| 181 | − return 0; | |
| 182 | − | |
| 183 | −clean_connector: | |
| 184 | − drm_connector_cleanup(connector); | |
| 185 | −clean_encoder: | |
| 186 | − drm_encoder_cleanup(encoder); | |
| 187 | − return ret; | |
| 188 | −} | |
| 189 | − | |
| 190 | −static int zx_vga_pwrctrl_init(struct zx_vga *vga) | |
| 191 | −{ | |
| 192 | − struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl; | |
| 193 | − struct device *dev = vga->dev; | |
| 194 | − struct of_phandle_args out_args; | |
| 195 | − struct regmap *regmap; | |
| 196 | − int ret; | |
| 197 | − | |
| 198 | − ret = of_parse_phandle_with_fixed_args(dev->of_node, | |
| 199 | − "zte,vga-power-control", 2, 0, &out_args); | |
| 200 | − if (ret) | |
| 201 | − return ret; | |
| 202 | − | |
| 203 | − regmap = syscon_node_to_regmap(out_args.np); | |
| 204 | − if (IS_ERR(regmap)) { | |
| 205 | − ret = PTR_ERR(regmap); | |
| 206 | − goto out; | |
| 207 | − } | |
| 208 | − | |
| 209 | − pwrctrl->regmap = regmap; | |
| 210 | − pwrctrl->reg = out_args.args[0]; | |
| 211 | − pwrctrl->mask = out_args.args[1]; | |
| 212 | − | |
| 213 | −out: | |
| 214 | − of_node_put(out_args.np); | |
| 215 | − return ret; | |
| 216 | −} | |
| 217 | − | |
| 218 | −static int zx_vga_i2c_read(struct zx_vga *vga, struct i2c_msg *msg) | |
| 219 | −{ | |
| 220 | − int len = msg->len; | |
| 221 | − u8 *buf = msg->buf; | |
| 222 | − u32 offset = 0; | |
| 223 | − int i; | |
| 224 | − | |
| 225 | − reinit_completion(&vga->complete); | |
| 226 | − | |
| 227 | − /* Select combo write */ | |
| 228 | − zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_COMBO, VGA_CMD_COMBO); | |
| 229 | − zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_RW, 0); | |
| 230 | − | |
| 231 | − while (len > 0) { | |
| 232 | − u32 cnt; | |
| 233 | − | |
| 234 | − /* Clear RX FIFO */ | |
| 235 | − zx_writel_mask(vga->mmio + VGA_RXF_CTRL, VGA_RX_FIFO_CLEAR, | |
| 236 | − VGA_RX_FIFO_CLEAR); | |
| 237 | − | |
| 238 | − /* Data offset to read from */ | |
| 239 | − zx_writel(vga->mmio + VGA_SUB_ADDR, offset); | |
| 240 | − | |
| 241 | − /* Kick off the transfer */ | |
| 242 | − zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, | |
| 243 | − VGA_CMD_TRANS); | |
| 244 | − | |
| 245 | − if (!wait_for_completion_timeout(&vga->complete, | |
| 246 | − msecs_to_jiffies(1000))) { | |
| 247 | − DRM_DEV_ERROR(vga->dev, "transfer timeout\n"); | |
| 248 | − return -ETIMEDOUT; | |
| 249 | − } | |
| 250 | − | |
| 251 | − cnt = zx_readl(vga->mmio + VGA_RXF_STATUS); | |
| 252 | − cnt = (cnt & VGA_RXF_COUNT_MASK) >> VGA_RXF_COUNT_SHIFT; | |
| 253 | − /* FIFO status may report more data than we need to read */ | |
| 254 | − cnt = min_t(u32, len, cnt); | |
| 255 | − | |
| 256 | − for (i = 0; i < cnt; i++) | |
| 257 | − *buf++ = zx_readl(vga->mmio + VGA_DATA); | |
| 258 | − | |
| 259 | − len -= cnt; | |
| 260 | − offset += cnt; | |
| 261 | − } | |
| 262 | − | |
| 263 | − return 0; | |
| 264 | −} | |
| 265 | − | |
| 266 | −static int zx_vga_i2c_write(struct zx_vga *vga, struct i2c_msg *msg) | |
| 267 | −{ | |
| 268 | − /* | |
| 269 | − * The DDC I2C adapter is only for reading EDID data, so we assume | |
| 270 | − * that the write to this adapter must be the EDID data offset. | |
| 271 | − */ | |
| 272 | − if ((msg->len != 1) || ((msg->addr != DDC_ADDR))) | |
| 273 | − return -EINVAL; | |
| 274 | − | |
| 275 | − /* Hardware will take care of the slave address shifting */ | |
| 276 | − zx_writel(vga->mmio + VGA_DEVICE_ADDR, msg->addr); | |
| 277 | − | |
| 278 | − return 0; | |
| 279 | −} | |
| 280 | − | |
| 281 | −static int zx_vga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, | |
| 282 | − int num) | |
| 283 | −{ | |
| 284 | − struct zx_vga *vga = i2c_get_adapdata(adap); | |
| 285 | − struct zx_vga_i2c *ddc = vga->ddc; | |
| 286 | − int ret = 0; | |
| 287 | − int i; | |
| 288 | − | |
| 289 | − mutex_lock(&ddc->lock); | |
| 290 | − | |
| 291 | − for (i = 0; i < num; i++) { | |
| 292 | − if (msgs[i].flags & I2C_M_RD) | |
| 293 | − ret = zx_vga_i2c_read(vga, &msgs[i]); | |
| 294 | − else | |
| 295 | − ret = zx_vga_i2c_write(vga, &msgs[i]); | |
| 296 | − | |
| 297 | − if (ret < 0) | |
| 298 | − break; | |
| 299 | − } | |
| 300 | − | |
| 301 | − if (!ret) | |
| 302 | − ret = num; | |
| 303 | − | |
| 304 | − mutex_unlock(&ddc->lock); | |
| 305 | − | |
| 306 | − return ret; | |
| 307 | −} | |
| 308 | − | |
| 309 | −static u32 zx_vga_i2c_func(struct i2c_adapter *adapter) | |
| 310 | −{ | |
| 311 | − return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | |
| 312 | −} | |
| 313 | − | |
| 314 | −static const struct i2c_algorithm zx_vga_algorithm = { | |
| 315 | − .master_xfer = zx_vga_i2c_xfer, | |
| 316 | − .functionality = zx_vga_i2c_func, | |
| 317 | −}; | |
| 318 | − | |
| 319 | −static int zx_vga_ddc_register(struct zx_vga *vga) | |
| 320 | −{ | |
| 321 | − struct device *dev = vga->dev; | |
| 322 | − struct i2c_adapter *adap; | |
| 323 | − struct zx_vga_i2c *ddc; | |
| 324 | − int ret; | |
| 325 | − | |
| 326 | − ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL); | |
| 327 | − if (!ddc) | |
| 328 | − return -ENOMEM; | |
| 329 | − | |
| 330 | − vga->ddc = ddc; | |
| 331 | − mutex_init(&ddc->lock); | |
| 332 | − | |
| 333 | − adap = &ddc->adap; | |
| 334 | − adap->owner = THIS_MODULE; | |
| 335 | − adap->class = I2C_CLASS_DDC; | |
| 336 | − adap->dev.parent = dev; | |
| 337 | − adap->algo = &zx_vga_algorithm; | |
| 338 | − snprintf(adap->name, sizeof(adap->name), "zx vga i2c"); | |
| 339 | − | |
| 340 | − ret = i2c_add_adapter(adap); | |
| 341 | − if (ret) { | |
| 342 | − DRM_DEV_ERROR(dev, "failed to add I2C adapter: %d\n", ret); | |
| 343 | − return ret; | |
| 344 | − } | |
| 345 | − | |
| 346 | − i2c_set_adapdata(adap, vga); | |
| 347 | − | |
| 348 | − return 0; | |
| 349 | −} | |
| 350 | − | |
| 351 | −static irqreturn_t zx_vga_irq_thread(int irq, void *dev_id) | |
| 352 | −{ | |
| 353 | − struct zx_vga *vga = dev_id; | |
| 354 | − | |
| 355 | − drm_helper_hpd_irq_event(vga->connector.dev); | |
| 356 | − | |
| 357 | − return IRQ_HANDLED; | |
| 358 | −} | |
| 359 | − | |
| 360 | −static irqreturn_t zx_vga_irq_handler(int irq, void *dev_id) | |
| 361 | −{ | |
| 362 | − struct zx_vga *vga = dev_id; | |
| 363 | − u32 status; | |
| 364 | − | |
| 365 | − status = zx_readl(vga->mmio + VGA_I2C_STATUS); | |
| 366 | − | |
| 367 | − /* Clear interrupt status */ | |
| 368 | − zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ, | |
| 369 | − VGA_CLEAR_IRQ); | |
| 370 | − | |
| 371 | − if (status & VGA_DEVICE_CONNECTED) { | |
| 372 | − /* | |
| 373 | − * Since VGA_DETECT_SEL bits need to be reset for switching DDC | |
| 374 | − * bus from device detection to EDID read, rather than setting | |
| 375 | − * up HAS_DEVICE bit here, we need to do that in .get_modes | |
| 376 | − * hook for unplug detecting after EDID read succeeds. | |
| 377 | − */ | |
| 378 | − vga->connected = true; | |
| 379 | − return IRQ_WAKE_THREAD; | |
| 380 | − } | |
| 381 | − | |
| 382 | − if (status & VGA_DEVICE_DISCONNECTED) { | |
| 383 | − zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, | |
| 384 | − VGA_DETECT_SEL_NO_DEVICE); | |
| 385 | − vga->connected = false; | |
| 386 | − return IRQ_WAKE_THREAD; | |
| 387 | − } | |
| 388 | − | |
| 389 | − if (status & VGA_TRANS_DONE) { | |
| 390 | − complete(&vga->complete); | |
| 391 | − return IRQ_HANDLED; | |
| 392 | − } | |
| 393 | − | |
| 394 | − return IRQ_NONE; | |
| 395 | −} | |
| 396 | − | |
| 397 | −static void zx_vga_hw_init(struct zx_vga *vga) | |
| 398 | −{ | |
| 399 | − unsigned long ref = clk_get_rate(vga->i2c_wclk); | |
| 400 | − int div; | |
| 401 | − | |
| 402 | − /* | |
| 403 | − * Set up I2C fast speed divider per formula below to get 400kHz. | |
| 404 | − * scl = ref / ((div + 1) * 4) | |
| 405 | − */ | |
| 406 | − div = DIV_ROUND_UP(ref / 1000, 400 * 4) - 1; | |
| 407 | − zx_writel(vga->mmio + VGA_CLK_DIV_FS, div); | |
| 408 | − | |
| 409 | − /* Set up device detection */ | |
| 410 | − zx_writel(vga->mmio + VGA_AUTO_DETECT_PARA, 0x80); | |
| 411 | − zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_NO_DEVICE); | |
| 412 | − | |
| 413 | − /* | |
| 414 | − * We need to poke monitor via DDC bus to get connection irq | |
| 415 | − * start working. | |
| 416 | − */ | |
| 417 | − zx_writel(vga->mmio + VGA_DEVICE_ADDR, DDC_ADDR); | |
| 418 | − zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, VGA_CMD_TRANS); | |
| 419 | −} | |
| 420 | − | |
| 421 | −static int zx_vga_bind(struct device *dev, struct device *master, void *data) | |
| 422 | −{ | |
| 423 | − struct platform_device *pdev = to_platform_device(dev); | |
| 424 | − struct drm_device *drm = data; | |
| 425 | − struct resource *res; | |
| 426 | − struct zx_vga *vga; | |
| 427 | − int irq; | |
| 428 | − int ret; | |
| 429 | − | |
| 430 | − vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL); | |
| 431 | − if (!vga) | |
| 432 | − return -ENOMEM; | |
| 433 | − | |
| 434 | − vga->dev = dev; | |
| 435 | − dev_set_drvdata(dev, vga); | |
| 436 | − | |
| 437 | − res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
| 438 | − vga->mmio = devm_ioremap_resource(dev, res); | |
| 439 | − if (IS_ERR(vga->mmio)) | |
| 440 | − return PTR_ERR(vga->mmio); | |
| 441 | − | |
| 442 | − irq = platform_get_irq(pdev, 0); | |
| 443 | − if (irq < 0) | |
| 444 | − return irq; | |
| 445 | − | |
| 446 | − vga->i2c_wclk = devm_clk_get(dev, "i2c_wclk"); | |
| 447 | − if (IS_ERR(vga->i2c_wclk)) { | |
| 448 | − ret = PTR_ERR(vga->i2c_wclk); | |
| 449 | − DRM_DEV_ERROR(dev, "failed to get i2c_wclk: %d\n", ret); | |
| 450 | − return ret; | |
| 451 | − } | |
| 452 | − | |
| 453 | − ret = zx_vga_pwrctrl_init(vga); | |
| 454 | − if (ret) { | |
| 455 | − DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret); | |
| 456 | − return ret; | |
| 457 | − } | |
| 458 | − | |
| 459 | − ret = zx_vga_ddc_register(vga); | |
| 460 | − if (ret) { | |
| 461 | − DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret); | |
| 462 | − return ret; | |
| 463 | − } | |
| 464 | − | |
| 465 | − ret = zx_vga_register(drm, vga); | |
| 466 | − if (ret) { | |
| 467 | − DRM_DEV_ERROR(dev, "failed to register vga: %d\n", ret); | |
| 468 | − return ret; | |
| 469 | − } | |
| 470 | − | |
| 471 | − init_completion(&vga->complete); | |
| 472 | − | |
| 473 | − ret = devm_request_threaded_irq(dev, irq, zx_vga_irq_handler, | |
| 474 | − zx_vga_irq_thread, IRQF_SHARED, | |
| 475 | − dev_name(dev), vga); | |
| 476 | − if (ret) { | |
| 477 | − DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret); | |
| 478 | − return ret; | |
| 479 | − } | |
| 480 | − | |
| 481 | − ret = clk_prepare_enable(vga->i2c_wclk); | |
| 482 | − if (ret) | |
| 483 | − return ret; | |
| 484 | − | |
| 485 | − zx_vga_hw_init(vga); | |
| 486 | − | |
| 487 | − return 0; | |
| 488 | −} | |
| 489 | − | |
| 490 | −static void zx_vga_unbind(struct device *dev, struct device *master, | |
| 491 | − void *data) | |
| 492 | −{ | |
| 493 | − struct zx_vga *vga = dev_get_drvdata(dev); | |
| 494 | − | |
| 495 | − clk_disable_unprepare(vga->i2c_wclk); | |
| 496 | −} | |
| 497 | − | |
| 498 | −static const struct component_ops zx_vga_component_ops = { | |
| 499 | − .bind = zx_vga_bind, | |
| 500 | − .unbind = zx_vga_unbind, | |
| 501 | −}; | |
| 502 | − | |
| 503 | −static int zx_vga_probe(struct platform_device *pdev) | |
| 504 | −{ | |
| 505 | − return component_add(&pdev->dev, &zx_vga_component_ops); | |
| 506 | −} | |
| 507 | − | |
| 508 | −static int zx_vga_remove(struct platform_device *pdev) | |
| 509 | −{ | |
| 510 | − component_del(&pdev->dev, &zx_vga_component_ops); | |
| 511 | − return 0; | |
| 512 | −} | |
| 513 | − | |
| 514 | −static const struct of_device_id zx_vga_of_match[] = { | |
| 515 | − { .compatible = "zte,zx296718-vga", }, | |
| 516 | − { /* end */ }, | |
| 517 | −}; | |
| 518 | −MODULE_DEVICE_TABLE(of, zx_vga_of_match); | |
| 519 | − | |
| 520 | −struct platform_driver zx_vga_driver = { | |
| 521 | − .probe = zx_vga_probe, | |
| 522 | − .remove = zx_vga_remove, | |
| 523 | − .driver = { | |
| 524 | − .name = "zx-vga", | |
| 525 | − .of_match_table = zx_vga_of_match, | |
| 526 | − }, | |
| 527 | −}; | |
| @@ -1,33 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright (C) 2017 Sanechips Technology Co., Ltd. | |
| 4 | − * Copyright 2017 Linaro Ltd. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_VGA_REGS_H__ | |
| 8 | −#define __ZX_VGA_REGS_H__ | |
| 9 | − | |
| 10 | −#define VGA_CMD_CFG 0x04 | |
| 11 | −#define VGA_CMD_TRANS BIT(6) | |
| 12 | −#define VGA_CMD_COMBO BIT(5) | |
| 13 | −#define VGA_CMD_RW BIT(4) | |
| 14 | −#define VGA_SUB_ADDR 0x0c | |
| 15 | −#define VGA_DEVICE_ADDR 0x10 | |
| 16 | −#define VGA_CLK_DIV_FS 0x14 | |
| 17 | −#define VGA_RXF_CTRL 0x20 | |
| 18 | −#define VGA_RX_FIFO_CLEAR BIT(7) | |
| 19 | −#define VGA_DATA 0x24 | |
| 20 | −#define VGA_I2C_STATUS 0x28 | |
| 21 | −#define VGA_DEVICE_DISCONNECTED BIT(7) | |
| 22 | −#define VGA_DEVICE_CONNECTED BIT(6) | |
| 23 | −#define VGA_CLEAR_IRQ BIT(4) | |
| 24 | −#define VGA_TRANS_DONE BIT(0) | |
| 25 | −#define VGA_RXF_STATUS 0x30 | |
| 26 | −#define VGA_RXF_COUNT_SHIFT 2 | |
| 27 | −#define VGA_RXF_COUNT_MASK GENMASK(7, 2) | |
| 28 | −#define VGA_AUTO_DETECT_PARA 0x34 | |
| 29 | −#define VGA_AUTO_DETECT_SEL 0x38 | |
| 30 | −#define VGA_DETECT_SEL_HAS_DEVICE BIT(1) | |
| 31 | −#define VGA_DETECT_SEL_NO_DEVICE BIT(0) | |
| 32 | − | |
| 33 | −#endif /* __ZX_VGA_REGS_H__ */ | |
| @@ -1,921 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0-only | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#include <linux/clk.h> | |
| 8 | −#include <linux/component.h> | |
| 9 | −#include <linux/module.h> | |
| 10 | −#include <linux/of_address.h> | |
| 11 | −#include <linux/platform_device.h> | |
| 12 | − | |
| 13 | −#include <video/videomode.h> | |
| 14 | − | |
| 15 | −#include <drm/drm_atomic_helper.h> | |
| 16 | −#include <drm/drm_crtc.h> | |
| 17 | −#include <drm/drm_fb_cma_helper.h> | |
| 18 | −#include <drm/drm_fb_helper.h> | |
| 19 | −#include <drm/drm_gem_cma_helper.h> | |
| 20 | −#include <drm/drm_of.h> | |
| 21 | −#include <drm/drm_plane_helper.h> | |
| 22 | −#include <drm/drm_probe_helper.h> | |
| 23 | −#include <drm/drm_vblank.h> | |
| 24 | − | |
| 25 | −#include "zx_common_regs.h" | |
| 26 | −#include "zx_drm_drv.h" | |
| 27 | −#include "zx_plane.h" | |
| 28 | −#include "zx_vou.h" | |
| 29 | −#include "zx_vou_regs.h" | |
| 30 | − | |
| 31 | −#define GL_NUM 2 | |
| 32 | −#define VL_NUM 3 | |
| 33 | − | |
| 34 | −enum vou_chn_type { | |
| 35 | − VOU_CHN_MAIN, | |
| 36 | − VOU_CHN_AUX, | |
| 37 | −}; | |
| 38 | − | |
| 39 | −struct zx_crtc_regs { | |
| 40 | − u32 fir_active; | |
| 41 | − u32 fir_htiming; | |
| 42 | − u32 fir_vtiming; | |
| 43 | − u32 sec_vtiming; | |
| 44 | − u32 timing_shift; | |
| 45 | − u32 timing_pi_shift; | |
| 46 | −}; | |
| 47 | − | |
| 48 | −static const struct zx_crtc_regs main_crtc_regs = { | |
| 49 | − .fir_active = FIR_MAIN_ACTIVE, | |
| 50 | − .fir_htiming = FIR_MAIN_H_TIMING, | |
| 51 | − .fir_vtiming = FIR_MAIN_V_TIMING, | |
| 52 | − .sec_vtiming = SEC_MAIN_V_TIMING, | |
| 53 | − .timing_shift = TIMING_MAIN_SHIFT, | |
| 54 | − .timing_pi_shift = TIMING_MAIN_PI_SHIFT, | |
| 55 | −}; | |
| 56 | − | |
| 57 | −static const struct zx_crtc_regs aux_crtc_regs = { | |
| 58 | − .fir_active = FIR_AUX_ACTIVE, | |
| 59 | − .fir_htiming = FIR_AUX_H_TIMING, | |
| 60 | − .fir_vtiming = FIR_AUX_V_TIMING, | |
| 61 | − .sec_vtiming = SEC_AUX_V_TIMING, | |
| 62 | − .timing_shift = TIMING_AUX_SHIFT, | |
| 63 | − .timing_pi_shift = TIMING_AUX_PI_SHIFT, | |
| 64 | −}; | |
| 65 | − | |
| 66 | −struct zx_crtc_bits { | |
| 67 | − u32 polarity_mask; | |
| 68 | − u32 polarity_shift; | |
| 69 | − u32 int_frame_mask; | |
| 70 | − u32 tc_enable; | |
| 71 | − u32 sec_vactive_shift; | |
| 72 | − u32 sec_vactive_mask; | |
| 73 | − u32 interlace_select; | |
| 74 | − u32 pi_enable; | |
| 75 | − u32 div_vga_shift; | |
| 76 | − u32 div_pic_shift; | |
| 77 | − u32 div_tvenc_shift; | |
| 78 | − u32 div_hdmi_pnx_shift; | |
| 79 | − u32 div_hdmi_shift; | |
| 80 | − u32 div_inf_shift; | |
| 81 | − u32 div_layer_shift; | |
| 82 | −}; | |
| 83 | − | |
| 84 | −static const struct zx_crtc_bits main_crtc_bits = { | |
| 85 | − .polarity_mask = MAIN_POL_MASK, | |
| 86 | − .polarity_shift = MAIN_POL_SHIFT, | |
| 87 | − .int_frame_mask = TIMING_INT_MAIN_FRAME, | |
| 88 | − .tc_enable = MAIN_TC_EN, | |
| 89 | − .sec_vactive_shift = SEC_VACT_MAIN_SHIFT, | |
| 90 | − .sec_vactive_mask = SEC_VACT_MAIN_MASK, | |
| 91 | − .interlace_select = MAIN_INTERLACE_SEL, | |
| 92 | − .pi_enable = MAIN_PI_EN, | |
| 93 | − .div_vga_shift = VGA_MAIN_DIV_SHIFT, | |
| 94 | − .div_pic_shift = PIC_MAIN_DIV_SHIFT, | |
| 95 | − .div_tvenc_shift = TVENC_MAIN_DIV_SHIFT, | |
| 96 | − .div_hdmi_pnx_shift = HDMI_MAIN_PNX_DIV_SHIFT, | |
| 97 | − .div_hdmi_shift = HDMI_MAIN_DIV_SHIFT, | |
| 98 | − .div_inf_shift = INF_MAIN_DIV_SHIFT, | |
| 99 | − .div_layer_shift = LAYER_MAIN_DIV_SHIFT, | |
| 100 | −}; | |
| 101 | − | |
| 102 | −static const struct zx_crtc_bits aux_crtc_bits = { | |
| 103 | − .polarity_mask = AUX_POL_MASK, | |
| 104 | − .polarity_shift = AUX_POL_SHIFT, | |
| 105 | − .int_frame_mask = TIMING_INT_AUX_FRAME, | |
| 106 | − .tc_enable = AUX_TC_EN, | |
| 107 | − .sec_vactive_shift = SEC_VACT_AUX_SHIFT, | |
| 108 | − .sec_vactive_mask = SEC_VACT_AUX_MASK, | |
| 109 | − .interlace_select = AUX_INTERLACE_SEL, | |
| 110 | − .pi_enable = AUX_PI_EN, | |
| 111 | − .div_vga_shift = VGA_AUX_DIV_SHIFT, | |
| 112 | − .div_pic_shift = PIC_AUX_DIV_SHIFT, | |
| 113 | − .div_tvenc_shift = TVENC_AUX_DIV_SHIFT, | |
| 114 | − .div_hdmi_pnx_shift = HDMI_AUX_PNX_DIV_SHIFT, | |
| 115 | − .div_hdmi_shift = HDMI_AUX_DIV_SHIFT, | |
| 116 | − .div_inf_shift = INF_AUX_DIV_SHIFT, | |
| 117 | − .div_layer_shift = LAYER_AUX_DIV_SHIFT, | |
| 118 | −}; | |
| 119 | − | |
| 120 | −struct zx_crtc { | |
| 121 | − struct drm_crtc crtc; | |
| 122 | − struct drm_plane *primary; | |
| 123 | − struct zx_vou_hw *vou; | |
| 124 | − void __iomem *chnreg; | |
| 125 | − void __iomem *chncsc; | |
| 126 | − void __iomem *dither; | |
| 127 | − const struct zx_crtc_regs *regs; | |
| 128 | − const struct zx_crtc_bits *bits; | |
| 129 | − enum vou_chn_type chn_type; | |
| 130 | − struct clk *pixclk; | |
| 131 | −}; | |
| 132 | − | |
| 133 | −#define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc) | |
| 134 | − | |
| 135 | −struct vou_layer_bits { | |
| 136 | − u32 enable; | |
| 137 | − u32 chnsel; | |
| 138 | − u32 clksel; | |
| 139 | −}; | |
| 140 | − | |
| 141 | −static const struct vou_layer_bits zx_gl_bits[GL_NUM] = { | |
| 142 | − { | |
| 143 | − .enable = OSD_CTRL0_GL0_EN, | |
| 144 | − .chnsel = OSD_CTRL0_GL0_SEL, | |
| 145 | − .clksel = VOU_CLK_GL0_SEL, | |
| 146 | − }, { | |
| 147 | − .enable = OSD_CTRL0_GL1_EN, | |
| 148 | − .chnsel = OSD_CTRL0_GL1_SEL, | |
| 149 | − .clksel = VOU_CLK_GL1_SEL, | |
| 150 | − }, | |
| 151 | −}; | |
| 152 | − | |
| 153 | −static const struct vou_layer_bits zx_vl_bits[VL_NUM] = { | |
| 154 | − { | |
| 155 | − .enable = OSD_CTRL0_VL0_EN, | |
| 156 | − .chnsel = OSD_CTRL0_VL0_SEL, | |
| 157 | − .clksel = VOU_CLK_VL0_SEL, | |
| 158 | − }, { | |
| 159 | − .enable = OSD_CTRL0_VL1_EN, | |
| 160 | − .chnsel = OSD_CTRL0_VL1_SEL, | |
| 161 | − .clksel = VOU_CLK_VL1_SEL, | |
| 162 | − }, { | |
| 163 | − .enable = OSD_CTRL0_VL2_EN, | |
| 164 | − .chnsel = OSD_CTRL0_VL2_SEL, | |
| 165 | − .clksel = VOU_CLK_VL2_SEL, | |
| 166 | − }, | |
| 167 | −}; | |
| 168 | − | |
| 169 | −struct zx_vou_hw { | |
| 170 | − struct device *dev; | |
| 171 | − void __iomem *osd; | |
| 172 | − void __iomem *timing; | |
| 173 | − void __iomem *vouctl; | |
| 174 | − void __iomem *otfppu; | |
| 175 | − void __iomem *dtrc; | |
| 176 | − struct clk *axi_clk; | |
| 177 | − struct clk *ppu_clk; | |
| 178 | − struct clk *main_clk; | |
| 179 | − struct clk *aux_clk; | |
| 180 | − struct zx_crtc *main_crtc; | |
| 181 | − struct zx_crtc *aux_crtc; | |
| 182 | −}; | |
| 183 | − | |
| 184 | −enum vou_inf_data_sel { | |
| 185 | − VOU_YUV444 = 0, | |
| 186 | − VOU_RGB_101010 = 1, | |
| 187 | − VOU_RGB_888 = 2, | |
| 188 | − VOU_RGB_666 = 3, | |
| 189 | −}; | |
| 190 | − | |
| 191 | −struct vou_inf { | |
| 192 | − enum vou_inf_id id; | |
| 193 | − enum vou_inf_data_sel data_sel; | |
| 194 | − u32 clocks_en_bits; | |
| 195 | − u32 clocks_sel_bits; | |
| 196 | −}; | |
| 197 | − | |
| 198 | −static struct vou_inf vou_infs[] = { | |
| 199 | − [VOU_HDMI] = { | |
| 200 | − .data_sel = VOU_YUV444, | |
| 201 | − .clocks_en_bits = BIT(24) | BIT(18) | BIT(6), | |
| 202 | − .clocks_sel_bits = BIT(13) | BIT(2), | |
| 203 | − }, | |
| 204 | − [VOU_TV_ENC] = { | |
| 205 | − .data_sel = VOU_YUV444, | |
| 206 | − .clocks_en_bits = BIT(15), | |
| 207 | − .clocks_sel_bits = BIT(11) | BIT(0), | |
| 208 | − }, | |
| 209 | − [VOU_VGA] = { | |
| 210 | − .data_sel = VOU_RGB_888, | |
| 211 | − .clocks_en_bits = BIT(1), | |
| 212 | − .clocks_sel_bits = BIT(10), | |
| 213 | − }, | |
| 214 | −}; | |
| 215 | − | |
| 216 | −static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc) | |
| 217 | −{ | |
| 218 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 219 | − | |
| 220 | − return zcrtc->vou; | |
| 221 | −} | |
| 222 | − | |
| 223 | −void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc, | |
| 224 | − enum vou_inf_hdmi_audio aud) | |
| 225 | −{ | |
| 226 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 227 | − struct zx_vou_hw *vou = zcrtc->vou; | |
| 228 | − | |
| 229 | − zx_writel_mask(vou->vouctl + VOU_INF_HDMI_CTRL, VOU_HDMI_AUD_MASK, aud); | |
| 230 | −} | |
| 231 | − | |
| 232 | −void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc) | |
| 233 | −{ | |
| 234 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 235 | − struct zx_vou_hw *vou = zcrtc->vou; | |
| 236 | − struct vou_inf *inf = &vou_infs[id]; | |
| 237 | − void __iomem *dither = zcrtc->dither; | |
| 238 | − void __iomem *csc = zcrtc->chncsc; | |
| 239 | − bool is_main = zcrtc->chn_type == VOU_CHN_MAIN; | |
| 240 | − u32 data_sel_shift = id << 1; | |
| 241 | − | |
| 242 | − if (inf->data_sel != VOU_YUV444) { | |
| 243 | − /* Enable channel CSC for RGB output */ | |
| 244 | − zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK, | |
| 245 | − CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT); | |
| 246 | − zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, | |
| 247 | − CSC_WORK_ENABLE); | |
| 248 | − | |
| 249 | − /* Bypass Dither block for RGB output */ | |
| 250 | − zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, | |
| 251 | − DITHER_BYSPASS); | |
| 252 | − } else { | |
| 253 | − zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0); | |
| 254 | − zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0); | |
| 255 | − } | |
| 256 | − | |
| 257 | − /* Select data format */ | |
| 258 | − zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift, | |
| 259 | − inf->data_sel << data_sel_shift); | |
| 260 | − | |
| 261 | − /* Select channel */ | |
| 262 | − zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << id, | |
| 263 | − zcrtc->chn_type << id); | |
| 264 | − | |
| 265 | − /* Select interface clocks */ | |
| 266 | − zx_writel_mask(vou->vouctl + VOU_CLK_SEL, inf->clocks_sel_bits, | |
| 267 | − is_main ? 0 : inf->clocks_sel_bits); | |
| 268 | − | |
| 269 | − /* Enable interface clocks */ | |
| 270 | − zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, | |
| 271 | − inf->clocks_en_bits); | |
| 272 | − | |
| 273 | − /* Enable the device */ | |
| 274 | − zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 1 << id); | |
| 275 | −} | |
| 276 | − | |
| 277 | −void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc) | |
| 278 | −{ | |
| 279 | − struct zx_vou_hw *vou = crtc_to_vou(crtc); | |
| 280 | − struct vou_inf *inf = &vou_infs[id]; | |
| 281 | − | |
| 282 | − /* Disable the device */ | |
| 283 | − zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 0); | |
| 284 | − | |
| 285 | − /* Disable interface clocks */ | |
| 286 | − zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0); | |
| 287 | −} | |
| 288 | − | |
| 289 | −void zx_vou_config_dividers(struct drm_crtc *crtc, | |
| 290 | − struct vou_div_config *configs, int num) | |
| 291 | −{ | |
| 292 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 293 | − struct zx_vou_hw *vou = zcrtc->vou; | |
| 294 | − const struct zx_crtc_bits *bits = zcrtc->bits; | |
| 295 | − int i; | |
| 296 | − | |
| 297 | − /* Clear update flag bit */ | |
| 298 | − zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, 0); | |
| 299 | − | |
| 300 | − for (i = 0; i < num; i++) { | |
| 301 | − struct vou_div_config *cfg = configs + i; | |
| 302 | − u32 reg, shift; | |
| 303 | − | |
| 304 | − switch (cfg->id) { | |
| 305 | − case VOU_DIV_VGA: | |
| 306 | − reg = VOU_CLK_SEL; | |
| 307 | − shift = bits->div_vga_shift; | |
| 308 | − break; | |
| 309 | − case VOU_DIV_PIC: | |
| 310 | − reg = VOU_CLK_SEL; | |
| 311 | − shift = bits->div_pic_shift; | |
| 312 | − break; | |
| 313 | − case VOU_DIV_TVENC: | |
| 314 | − reg = VOU_DIV_PARA; | |
| 315 | − shift = bits->div_tvenc_shift; | |
| 316 | − break; | |
| 317 | − case VOU_DIV_HDMI_PNX: | |
| 318 | − reg = VOU_DIV_PARA; | |
| 319 | − shift = bits->div_hdmi_pnx_shift; | |
| 320 | − break; | |
| 321 | − case VOU_DIV_HDMI: | |
| 322 | − reg = VOU_DIV_PARA; | |
| 323 | − shift = bits->div_hdmi_shift; | |
| 324 | − break; | |
| 325 | − case VOU_DIV_INF: | |
| 326 | − reg = VOU_DIV_PARA; | |
| 327 | − shift = bits->div_inf_shift; | |
| 328 | − break; | |
| 329 | − case VOU_DIV_LAYER: | |
| 330 | − reg = VOU_DIV_PARA; | |
| 331 | − shift = bits->div_layer_shift; | |
| 332 | − break; | |
| 333 | − default: | |
| 334 | − continue; | |
| 335 | − } | |
| 336 | − | |
| 337 | − /* Each divider occupies 3 bits */ | |
| 338 | − zx_writel_mask(vou->vouctl + reg, 0x7 << shift, | |
| 339 | − cfg->val << shift); | |
| 340 | − } | |
| 341 | − | |
| 342 | − /* Set update flag bit to get dividers effected */ | |
| 343 | − zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, | |
| 344 | − DIV_PARA_UPDATE); | |
| 345 | −} | |
| 346 | − | |
| 347 | −static inline void vou_chn_set_update(struct zx_crtc *zcrtc) | |
| 348 | −{ | |
| 349 | − zx_writel(zcrtc->chnreg + CHN_UPDATE, 1); | |
| 350 | −} | |
| 351 | − | |
| 352 | −static void zx_crtc_atomic_enable(struct drm_crtc *crtc, | |
| 353 | − struct drm_atomic_state *state) | |
| 354 | −{ | |
| 355 | − struct drm_display_mode *mode = &crtc->state->adjusted_mode; | |
| 356 | − bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; | |
| 357 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 358 | − struct zx_vou_hw *vou = zcrtc->vou; | |
| 359 | − const struct zx_crtc_regs *regs = zcrtc->regs; | |
| 360 | − const struct zx_crtc_bits *bits = zcrtc->bits; | |
| 361 | − struct videomode vm; | |
| 362 | − u32 scan_mask; | |
| 363 | − u32 pol = 0; | |
| 364 | − u32 val; | |
| 365 | − int ret; | |
| 366 | − | |
| 367 | − drm_display_mode_to_videomode(mode, &vm); | |
| 368 | − | |
| 369 | − /* Set up timing parameters */ | |
| 370 | − val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1); | |
| 371 | − val |= H_ACTIVE(vm.hactive - 1); | |
| 372 | − zx_writel(vou->timing + regs->fir_active, val); | |
| 373 | − | |
| 374 | − val = SYNC_WIDE(vm.hsync_len - 1); | |
| 375 | − val |= BACK_PORCH(vm.hback_porch - 1); | |
| 376 | − val |= FRONT_PORCH(vm.hfront_porch - 1); | |
| 377 | − zx_writel(vou->timing + regs->fir_htiming, val); | |
| 378 | − | |
| 379 | − val = SYNC_WIDE(vm.vsync_len - 1); | |
| 380 | − val |= BACK_PORCH(vm.vback_porch - 1); | |
| 381 | − val |= FRONT_PORCH(vm.vfront_porch - 1); | |
| 382 | − zx_writel(vou->timing + regs->fir_vtiming, val); | |
| 383 | − | |
| 384 | − if (interlaced) { | |
| 385 | − u32 shift = bits->sec_vactive_shift; | |
| 386 | − u32 mask = bits->sec_vactive_mask; | |
| 387 | − | |
| 388 | − val = zx_readl(vou->timing + SEC_V_ACTIVE); | |
| 389 | − val &= ~mask; | |
| 390 | − val |= ((vm.vactive / 2 - 1) << shift) & mask; | |
| 391 | − zx_writel(vou->timing + SEC_V_ACTIVE, val); | |
| 392 | − | |
| 393 | − val = SYNC_WIDE(vm.vsync_len - 1); | |
| 394 | − /* | |
| 395 | − * The vback_porch for the second field needs to shift one on | |
| 396 | − * the value for the first field. | |
| 397 | − */ | |
| 398 | − val |= BACK_PORCH(vm.vback_porch); | |
| 399 | − val |= FRONT_PORCH(vm.vfront_porch - 1); | |
| 400 | − zx_writel(vou->timing + regs->sec_vtiming, val); | |
| 401 | − } | |
| 402 | − | |
| 403 | − /* Set up polarities */ | |
| 404 | − if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) | |
| 405 | − pol |= 1 << POL_VSYNC_SHIFT; | |
| 406 | − if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW) | |
| 407 | − pol |= 1 << POL_HSYNC_SHIFT; | |
| 408 | − | |
| 409 | − zx_writel_mask(vou->timing + TIMING_CTRL, bits->polarity_mask, | |
| 410 | − pol << bits->polarity_shift); | |
| 411 | − | |
| 412 | − /* Setup SHIFT register by following what ZTE BSP does */ | |
| 413 | − val = H_SHIFT_VAL; | |
| 414 | − if (interlaced) | |
| 415 | − val |= V_SHIFT_VAL << 16; | |
| 416 | − zx_writel(vou->timing + regs->timing_shift, val); | |
| 417 | − zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL); | |
| 418 | − | |
| 419 | − /* Progressive or interlace scan select */ | |
| 420 | − scan_mask = bits->interlace_select | bits->pi_enable; | |
| 421 | − zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask, | |
| 422 | − interlaced ? scan_mask : 0); | |
| 423 | − | |
| 424 | − /* Enable TIMING_CTRL */ | |
| 425 | − zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, | |
| 426 | − bits->tc_enable); | |
| 427 | − | |
| 428 | − /* Configure channel screen size */ | |
| 429 | − zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_W_MASK, | |
| 430 | − vm.hactive << CHN_SCREEN_W_SHIFT); | |
| 431 | − zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK, | |
| 432 | − vm.vactive << CHN_SCREEN_H_SHIFT); | |
| 433 | − | |
| 434 | − /* Configure channel interlace buffer control */ | |
| 435 | − zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN, | |
| 436 | − interlaced ? CHN_INTERLACE_EN : 0); | |
| 437 | − | |
| 438 | − /* Update channel */ | |
| 439 | − vou_chn_set_update(zcrtc); | |
| 440 | − | |
| 441 | − /* Enable channel */ | |
| 442 | − zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, CHN_ENABLE); | |
| 443 | − | |
| 444 | − drm_crtc_vblank_on(crtc); | |
| 445 | − | |
| 446 | − ret = clk_set_rate(zcrtc->pixclk, mode->clock * 1000); | |
| 447 | − if (ret) { | |
| 448 | − DRM_DEV_ERROR(vou->dev, "failed to set pixclk rate: %d\n", ret); | |
| 449 | − return; | |
| 450 | − } | |
| 451 | − | |
| 452 | − ret = clk_prepare_enable(zcrtc->pixclk); | |
| 453 | − if (ret) | |
| 454 | − DRM_DEV_ERROR(vou->dev, "failed to enable pixclk: %d\n", ret); | |
| 455 | −} | |
| 456 | − | |
| 457 | −static void zx_crtc_atomic_disable(struct drm_crtc *crtc, | |
| 458 | − struct drm_atomic_state *state) | |
| 459 | −{ | |
| 460 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 461 | − const struct zx_crtc_bits *bits = zcrtc->bits; | |
| 462 | − struct zx_vou_hw *vou = zcrtc->vou; | |
| 463 | − | |
| 464 | − clk_disable_unprepare(zcrtc->pixclk); | |
| 465 | − | |
| 466 | − drm_crtc_vblank_off(crtc); | |
| 467 | − | |
| 468 | − /* Disable channel */ | |
| 469 | − zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, 0); | |
| 470 | − | |
| 471 | − /* Disable TIMING_CTRL */ | |
| 472 | − zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, 0); | |
| 473 | −} | |
| 474 | − | |
| 475 | −static void zx_crtc_atomic_flush(struct drm_crtc *crtc, | |
| 476 | − struct drm_atomic_state *state) | |
| 477 | −{ | |
| 478 | − struct drm_pending_vblank_event *event = crtc->state->event; | |
| 479 | − | |
| 480 | − if (!event) | |
| 481 | − return; | |
| 482 | − | |
| 483 | − crtc->state->event = NULL; | |
| 484 | − | |
| 485 | − spin_lock_irq(&crtc->dev->event_lock); | |
| 486 | − if (drm_crtc_vblank_get(crtc) == 0) | |
| 487 | − drm_crtc_arm_vblank_event(crtc, event); | |
| 488 | − else | |
| 489 | − drm_crtc_send_vblank_event(crtc, event); | |
| 490 | − spin_unlock_irq(&crtc->dev->event_lock); | |
| 491 | −} | |
| 492 | − | |
| 493 | −static const struct drm_crtc_helper_funcs zx_crtc_helper_funcs = { | |
| 494 | − .atomic_flush = zx_crtc_atomic_flush, | |
| 495 | − .atomic_enable = zx_crtc_atomic_enable, | |
| 496 | − .atomic_disable = zx_crtc_atomic_disable, | |
| 497 | −}; | |
| 498 | − | |
| 499 | −static int zx_vou_enable_vblank(struct drm_crtc *crtc) | |
| 500 | −{ | |
| 501 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 502 | − struct zx_vou_hw *vou = crtc_to_vou(crtc); | |
| 503 | − u32 int_frame_mask = zcrtc->bits->int_frame_mask; | |
| 504 | − | |
| 505 | − zx_writel_mask(vou->timing + TIMING_INT_CTRL, int_frame_mask, | |
| 506 | − int_frame_mask); | |
| 507 | − | |
| 508 | − return 0; | |
| 509 | −} | |
| 510 | − | |
| 511 | −static void zx_vou_disable_vblank(struct drm_crtc *crtc) | |
| 512 | −{ | |
| 513 | − struct zx_crtc *zcrtc = to_zx_crtc(crtc); | |
| 514 | − struct zx_vou_hw *vou = crtc_to_vou(crtc); | |
| 515 | − | |
| 516 | − zx_writel_mask(vou->timing + TIMING_INT_CTRL, | |
| 517 | − zcrtc->bits->int_frame_mask, 0); | |
| 518 | −} | |
| 519 | − | |
| 520 | −static const struct drm_crtc_funcs zx_crtc_funcs = { | |
| 521 | − .destroy = drm_crtc_cleanup, | |
| 522 | − .set_config = drm_atomic_helper_set_config, | |
| 523 | − .page_flip = drm_atomic_helper_page_flip, | |
| 524 | − .reset = drm_atomic_helper_crtc_reset, | |
| 525 | − .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | |
| 526 | − .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
| 527 | − .enable_vblank = zx_vou_enable_vblank, | |
| 528 | − .disable_vblank = zx_vou_disable_vblank, | |
| 529 | −}; | |
| 530 | − | |
| 531 | −static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou, | |
| 532 | − enum vou_chn_type chn_type) | |
| 533 | −{ | |
| 534 | − struct device *dev = vou->dev; | |
| 535 | − struct zx_plane *zplane; | |
| 536 | − struct zx_crtc *zcrtc; | |
| 537 | − int ret; | |
| 538 | − | |
| 539 | − zcrtc = devm_kzalloc(dev, sizeof(*zcrtc), GFP_KERNEL); | |
| 540 | − if (!zcrtc) | |
| 541 | − return -ENOMEM; | |
| 542 | − | |
| 543 | − zcrtc->vou = vou; | |
| 544 | − zcrtc->chn_type = chn_type; | |
| 545 | − | |
| 546 | − zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL); | |
| 547 | − if (!zplane) | |
| 548 | − return -ENOMEM; | |
| 549 | − | |
| 550 | − zplane->dev = dev; | |
| 551 | − | |
| 552 | − if (chn_type == VOU_CHN_MAIN) { | |
| 553 | − zplane->layer = vou->osd + MAIN_GL_OFFSET; | |
| 554 | − zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET; | |
| 555 | − zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET; | |
| 556 | − zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET; | |
| 557 | − zplane->bits = &zx_gl_bits[0]; | |
| 558 | − zcrtc->chnreg = vou->osd + OSD_MAIN_CHN; | |
| 559 | − zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET; | |
| 560 | − zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET; | |
| 561 | − zcrtc->regs = &main_crtc_regs; | |
| 562 | − zcrtc->bits = &main_crtc_bits; | |
| 563 | − } else { | |
| 564 | − zplane->layer = vou->osd + AUX_GL_OFFSET; | |
| 565 | − zplane->csc = vou->osd + AUX_GL_CSC_OFFSET; | |
| 566 | − zplane->hbsc = vou->osd + AUX_HBSC_OFFSET; | |
| 567 | − zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET; | |
| 568 | − zplane->bits = &zx_gl_bits[1]; | |
| 569 | − zcrtc->chnreg = vou->osd + OSD_AUX_CHN; | |
| 570 | − zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET; | |
| 571 | − zcrtc->dither = vou->osd + AUX_DITHER_OFFSET; | |
| 572 | − zcrtc->regs = &aux_crtc_regs; | |
| 573 | − zcrtc->bits = &aux_crtc_bits; | |
| 574 | − } | |
| 575 | − | |
| 576 | − zcrtc->pixclk = devm_clk_get(dev, (chn_type == VOU_CHN_MAIN) ? | |
| 577 | − "main_wclk" : "aux_wclk"); | |
| 578 | − if (IS_ERR(zcrtc->pixclk)) { | |
| 579 | − ret = PTR_ERR(zcrtc->pixclk); | |
| 580 | − DRM_DEV_ERROR(dev, "failed to get pix clk: %d\n", ret); | |
| 581 | − return ret; | |
| 582 | − } | |
| 583 | − | |
| 584 | − ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_PRIMARY); | |
| 585 | − if (ret) { | |
| 586 | − DRM_DEV_ERROR(dev, "failed to init primary plane: %d\n", ret); | |
| 587 | − return ret; | |
| 588 | − } | |
| 589 | − | |
| 590 | − zcrtc->primary = &zplane->plane; | |
| 591 | − | |
| 592 | − ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL, | |
| 593 | − &zx_crtc_funcs, NULL); | |
| 594 | − if (ret) { | |
| 595 | − DRM_DEV_ERROR(dev, "failed to init drm crtc: %d\n", ret); | |
| 596 | − return ret; | |
| 597 | − } | |
| 598 | − | |
| 599 | − drm_crtc_helper_add(&zcrtc->crtc, &zx_crtc_helper_funcs); | |
| 600 | − | |
| 601 | − if (chn_type == VOU_CHN_MAIN) | |
| 602 | − vou->main_crtc = zcrtc; | |
| 603 | − else | |
| 604 | − vou->aux_crtc = zcrtc; | |
| 605 | − | |
| 606 | − return 0; | |
| 607 | −} | |
| 608 | − | |
| 609 | −void zx_vou_layer_enable(struct drm_plane *plane) | |
| 610 | −{ | |
| 611 | − struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc); | |
| 612 | − struct zx_vou_hw *vou = zcrtc->vou; | |
| 613 | − struct zx_plane *zplane = to_zx_plane(plane); | |
| 614 | − const struct vou_layer_bits *bits = zplane->bits; | |
| 615 | − | |
| 616 | − if (zcrtc->chn_type == VOU_CHN_MAIN) { | |
| 617 | − zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0); | |
| 618 | − zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0); | |
| 619 | − } else { | |
| 620 | − zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, | |
| 621 | − bits->chnsel); | |
| 622 | − zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, | |
| 623 | − bits->clksel); | |
| 624 | − } | |
| 625 | − | |
| 626 | − zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable); | |
| 627 | −} | |
| 628 | − | |
| 629 | −void zx_vou_layer_disable(struct drm_plane *plane, | |
| 630 | − struct drm_plane_state *old_state) | |
| 631 | −{ | |
| 632 | − struct zx_crtc *zcrtc = to_zx_crtc(old_state->crtc); | |
| 633 | − struct zx_vou_hw *vou = zcrtc->vou; | |
| 634 | − struct zx_plane *zplane = to_zx_plane(plane); | |
| 635 | − const struct vou_layer_bits *bits = zplane->bits; | |
| 636 | − | |
| 637 | − zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0); | |
| 638 | −} | |
| 639 | − | |
| 640 | −static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou) | |
| 641 | −{ | |
| 642 | − struct device *dev = vou->dev; | |
| 643 | − struct zx_plane *zplane; | |
| 644 | − int i; | |
| 645 | − int ret; | |
| 646 | − | |
| 647 | − /* | |
| 648 | − * VL0 has some quirks on scaling support which need special handling. | |
| 649 | − * Let's leave it out for now. | |
| 650 | − */ | |
| 651 | − for (i = 1; i < VL_NUM; i++) { | |
| 652 | − zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL); | |
| 653 | − if (!zplane) { | |
| 654 | − DRM_DEV_ERROR(dev, "failed to allocate zplane %d\n", i); | |
| 655 | − return; | |
| 656 | − } | |
| 657 | − | |
| 658 | − zplane->layer = vou->osd + OSD_VL_OFFSET(i); | |
| 659 | − zplane->hbsc = vou->osd + HBSC_VL_OFFSET(i); | |
| 660 | − zplane->rsz = vou->otfppu + RSZ_VL_OFFSET(i); | |
| 661 | − zplane->bits = &zx_vl_bits[i]; | |
| 662 | − | |
| 663 | − ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_OVERLAY); | |
| 664 | − if (ret) { | |
| 665 | − DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i); | |
| 666 | − continue; | |
| 667 | − } | |
| 668 | − } | |
| 669 | −} | |
| 670 | − | |
| 671 | −static inline void zx_osd_int_update(struct zx_crtc *zcrtc) | |
| 672 | −{ | |
| 673 | − struct drm_crtc *crtc = &zcrtc->crtc; | |
| 674 | − struct drm_plane *plane; | |
| 675 | − | |
| 676 | − vou_chn_set_update(zcrtc); | |
| 677 | − | |
| 678 | − drm_for_each_plane_mask(plane, crtc->dev, crtc->state->plane_mask) | |
| 679 | − zx_plane_set_update(plane); | |
| 680 | −} | |
| 681 | − | |
| 682 | −static irqreturn_t vou_irq_handler(int irq, void *dev_id) | |
| 683 | −{ | |
| 684 | − struct zx_vou_hw *vou = dev_id; | |
| 685 | − u32 state; | |
| 686 | − | |
| 687 | − /* Handle TIMING_CTRL frame interrupts */ | |
| 688 | − state = zx_readl(vou->timing + TIMING_INT_STATE); | |
| 689 | − zx_writel(vou->timing + TIMING_INT_STATE, state); | |
| 690 | − | |
| 691 | − if (state & TIMING_INT_MAIN_FRAME) | |
| 692 | − drm_crtc_handle_vblank(&vou->main_crtc->crtc); | |
| 693 | − | |
| 694 | − if (state & TIMING_INT_AUX_FRAME) | |
| 695 | − drm_crtc_handle_vblank(&vou->aux_crtc->crtc); | |
| 696 | − | |
| 697 | − /* Handle OSD interrupts */ | |
| 698 | − state = zx_readl(vou->osd + OSD_INT_STA); | |
| 699 | − zx_writel(vou->osd + OSD_INT_CLRSTA, state); | |
| 700 | − | |
| 701 | − if (state & OSD_INT_MAIN_UPT) | |
| 702 | − zx_osd_int_update(vou->main_crtc); | |
| 703 | − | |
| 704 | − if (state & OSD_INT_AUX_UPT) | |
| 705 | − zx_osd_int_update(vou->aux_crtc); | |
| 706 | − | |
| 707 | − if (state & OSD_INT_ERROR) | |
| 708 | − DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state); | |
| 709 | − | |
| 710 | − return IRQ_HANDLED; | |
| 711 | −} | |
| 712 | − | |
| 713 | −static void vou_dtrc_init(struct zx_vou_hw *vou) | |
| 714 | −{ | |
| 715 | − /* Clear bit for bypass by ID */ | |
| 716 | − zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL, | |
| 717 | − TILE2RASTESCAN_BYPASS_MODE, 0); | |
| 718 | − | |
| 719 | − /* Select ARIDR mode */ | |
| 720 | − zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL, DETILE_ARIDR_MODE_MASK, | |
| 721 | − DETILE_ARID_IN_ARIDR); | |
| 722 | − | |
| 723 | − /* Bypass decompression for both frames */ | |
| 724 | − zx_writel_mask(vou->dtrc + DTRC_F0_CTRL, DTRC_DECOMPRESS_BYPASS, | |
| 725 | − DTRC_DECOMPRESS_BYPASS); | |
| 726 | − zx_writel_mask(vou->dtrc + DTRC_F1_CTRL, DTRC_DECOMPRESS_BYPASS, | |
| 727 | − DTRC_DECOMPRESS_BYPASS); | |
| 728 | − | |
| 729 | − /* Set up ARID register */ | |
| 730 | − zx_writel(vou->dtrc + DTRC_ARID, DTRC_ARID3(0xf) | DTRC_ARID2(0xe) | | |
| 731 | − DTRC_ARID1(0xf) | DTRC_ARID0(0xe)); | |
| 732 | −} | |
| 733 | − | |
| 734 | −static void vou_hw_init(struct zx_vou_hw *vou) | |
| 735 | −{ | |
| 736 | − /* Release reset for all VOU modules */ | |
| 737 | − zx_writel(vou->vouctl + VOU_SOFT_RST, ~0); | |
| 738 | − | |
| 739 | − /* Enable all VOU module clocks */ | |
| 740 | − zx_writel(vou->vouctl + VOU_CLK_EN, ~0); | |
| 741 | − | |
| 742 | − /* Clear both OSD and TIMING_CTRL interrupt state */ | |
| 743 | − zx_writel(vou->osd + OSD_INT_CLRSTA, ~0); | |
| 744 | − zx_writel(vou->timing + TIMING_INT_STATE, ~0); | |
| 745 | − | |
| 746 | − /* Enable OSD and TIMING_CTRL interrrupts */ | |
| 747 | − zx_writel(vou->osd + OSD_INT_MSK, OSD_INT_ENABLE); | |
| 748 | − zx_writel(vou->timing + TIMING_INT_CTRL, TIMING_INT_ENABLE); | |
| 749 | − | |
| 750 | − /* Select GPC as input to gl/vl scaler as a sane default setting */ | |
| 751 | − zx_writel(vou->otfppu + OTFPPU_RSZ_DATA_SOURCE, 0x2a); | |
| 752 | − | |
| 753 | − /* | |
| 754 | − * Needs to reset channel and layer logic per frame when frame starts | |
| 755 | − * to get VOU work properly. | |
| 756 | − */ | |
| 757 | − zx_writel_mask(vou->osd + OSD_RST_CLR, RST_PER_FRAME, RST_PER_FRAME); | |
| 758 | − | |
| 759 | − vou_dtrc_init(vou); | |
| 760 | −} | |
| 761 | − | |
| 762 | −static int zx_crtc_bind(struct device *dev, struct device *master, void *data) | |
| 763 | −{ | |
| 764 | − struct platform_device *pdev = to_platform_device(dev); | |
| 765 | − struct drm_device *drm = data; | |
| 766 | − struct zx_vou_hw *vou; | |
| 767 | − struct resource *res; | |
| 768 | − int irq; | |
| 769 | − int ret; | |
| 770 | − | |
| 771 | − vou = devm_kzalloc(dev, sizeof(*vou), GFP_KERNEL); | |
| 772 | − if (!vou) | |
| 773 | − return -ENOMEM; | |
| 774 | − | |
| 775 | − res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "osd"); | |
| 776 | − vou->osd = devm_ioremap_resource(dev, res); | |
| 777 | − if (IS_ERR(vou->osd)) { | |
| 778 | − ret = PTR_ERR(vou->osd); | |
| 779 | − DRM_DEV_ERROR(dev, "failed to remap osd region: %d\n", ret); | |
| 780 | − return ret; | |
| 781 | − } | |
| 782 | − | |
| 783 | − res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "timing_ctrl"); | |
| 784 | − vou->timing = devm_ioremap_resource(dev, res); | |
| 785 | − if (IS_ERR(vou->timing)) { | |
| 786 | − ret = PTR_ERR(vou->timing); | |
| 787 | − DRM_DEV_ERROR(dev, "failed to remap timing_ctrl region: %d\n", | |
| 788 | − ret); | |
| 789 | − return ret; | |
| 790 | − } | |
| 791 | − | |
| 792 | − res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dtrc"); | |
| 793 | − vou->dtrc = devm_ioremap_resource(dev, res); | |
| 794 | − if (IS_ERR(vou->dtrc)) { | |
| 795 | − ret = PTR_ERR(vou->dtrc); | |
| 796 | − DRM_DEV_ERROR(dev, "failed to remap dtrc region: %d\n", ret); | |
| 797 | − return ret; | |
| 798 | − } | |
| 799 | − | |
| 800 | − res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vou_ctrl"); | |
| 801 | − vou->vouctl = devm_ioremap_resource(dev, res); | |
| 802 | − if (IS_ERR(vou->vouctl)) { | |
| 803 | − ret = PTR_ERR(vou->vouctl); | |
| 804 | − DRM_DEV_ERROR(dev, "failed to remap vou_ctrl region: %d\n", | |
| 805 | − ret); | |
| 806 | − return ret; | |
| 807 | − } | |
| 808 | − | |
| 809 | − res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otfppu"); | |
| 810 | − vou->otfppu = devm_ioremap_resource(dev, res); | |
| 811 | − if (IS_ERR(vou->otfppu)) { | |
| 812 | − ret = PTR_ERR(vou->otfppu); | |
| 813 | − DRM_DEV_ERROR(dev, "failed to remap otfppu region: %d\n", ret); | |
| 814 | − return ret; | |
| 815 | − } | |
| 816 | − | |
| 817 | − irq = platform_get_irq(pdev, 0); | |
| 818 | − if (irq < 0) | |
| 819 | − return irq; | |
| 820 | − | |
| 821 | − vou->axi_clk = devm_clk_get(dev, "aclk"); | |
| 822 | − if (IS_ERR(vou->axi_clk)) { | |
| 823 | − ret = PTR_ERR(vou->axi_clk); | |
| 824 | − DRM_DEV_ERROR(dev, "failed to get axi_clk: %d\n", ret); | |
| 825 | − return ret; | |
| 826 | − } | |
| 827 | − | |
| 828 | − vou->ppu_clk = devm_clk_get(dev, "ppu_wclk"); | |
| 829 | − if (IS_ERR(vou->ppu_clk)) { | |
| 830 | − ret = PTR_ERR(vou->ppu_clk); | |
| 831 | − DRM_DEV_ERROR(dev, "failed to get ppu_clk: %d\n", ret); | |
| 832 | − return ret; | |
| 833 | − } | |
| 834 | − | |
| 835 | − ret = clk_prepare_enable(vou->axi_clk); | |
| 836 | − if (ret) { | |
| 837 | − DRM_DEV_ERROR(dev, "failed to enable axi_clk: %d\n", ret); | |
| 838 | − return ret; | |
| 839 | − } | |
| 840 | − | |
| 841 | − clk_prepare_enable(vou->ppu_clk); | |
| 842 | − if (ret) { | |
| 843 | − DRM_DEV_ERROR(dev, "failed to enable ppu_clk: %d\n", ret); | |
| 844 | − goto disable_axi_clk; | |
| 845 | − } | |
| 846 | − | |
| 847 | − vou->dev = dev; | |
| 848 | − dev_set_drvdata(dev, vou); | |
| 849 | − | |
| 850 | − vou_hw_init(vou); | |
| 851 | − | |
| 852 | − ret = devm_request_irq(dev, irq, vou_irq_handler, 0, "zx_vou", vou); | |
| 853 | − if (ret < 0) { | |
| 854 | − DRM_DEV_ERROR(dev, "failed to request vou irq: %d\n", ret); | |
| 855 | − goto disable_ppu_clk; | |
| 856 | − } | |
| 857 | − | |
| 858 | − ret = zx_crtc_init(drm, vou, VOU_CHN_MAIN); | |
| 859 | − if (ret) { | |
| 860 | − DRM_DEV_ERROR(dev, "failed to init main channel crtc: %d\n", | |
| 861 | − ret); | |
| 862 | − goto disable_ppu_clk; | |
| 863 | − } | |
| 864 | − | |
| 865 | − ret = zx_crtc_init(drm, vou, VOU_CHN_AUX); | |
| 866 | − if (ret) { | |
| 867 | − DRM_DEV_ERROR(dev, "failed to init aux channel crtc: %d\n", | |
| 868 | − ret); | |
| 869 | − goto disable_ppu_clk; | |
| 870 | − } | |
| 871 | − | |
| 872 | − zx_overlay_init(drm, vou); | |
| 873 | − | |
| 874 | − return 0; | |
| 875 | − | |
| 876 | −disable_ppu_clk: | |
| 877 | − clk_disable_unprepare(vou->ppu_clk); | |
| 878 | −disable_axi_clk: | |
| 879 | − clk_disable_unprepare(vou->axi_clk); | |
| 880 | − return ret; | |
| 881 | −} | |
| 882 | − | |
| 883 | −static void zx_crtc_unbind(struct device *dev, struct device *master, | |
| 884 | − void *data) | |
| 885 | −{ | |
| 886 | − struct zx_vou_hw *vou = dev_get_drvdata(dev); | |
| 887 | − | |
| 888 | − clk_disable_unprepare(vou->axi_clk); | |
| 889 | − clk_disable_unprepare(vou->ppu_clk); | |
| 890 | −} | |
| 891 | − | |
| 892 | −static const struct component_ops zx_crtc_component_ops = { | |
| 893 | − .bind = zx_crtc_bind, | |
| 894 | − .unbind = zx_crtc_unbind, | |
| 895 | −}; | |
| 896 | − | |
| 897 | −static int zx_crtc_probe(struct platform_device *pdev) | |
| 898 | −{ | |
| 899 | − return component_add(&pdev->dev, &zx_crtc_component_ops); | |
| 900 | −} | |
| 901 | − | |
| 902 | −static int zx_crtc_remove(struct platform_device *pdev) | |
| 903 | −{ | |
| 904 | − component_del(&pdev->dev, &zx_crtc_component_ops); | |
| 905 | − return 0; | |
| 906 | −} | |
| 907 | − | |
| 908 | −static const struct of_device_id zx_crtc_of_match[] = { | |
| 909 | − { .compatible = "zte,zx296718-dpc", }, | |
| 910 | − { /* end */ }, | |
| 911 | −}; | |
| 912 | −MODULE_DEVICE_TABLE(of, zx_crtc_of_match); | |
| 913 | − | |
| 914 | −struct platform_driver zx_crtc_driver = { | |
| 915 | − .probe = zx_crtc_probe, | |
| 916 | − .remove = zx_crtc_remove, | |
| 917 | − .driver = { | |
| 918 | − .name = "zx-crtc", | |
| 919 | − .of_match_table = zx_crtc_of_match, | |
| 920 | − }, | |
| 921 | −}; | |
| @@ -1,64 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_VOU_H__ | |
| 8 | −#define __ZX_VOU_H__ | |
| 9 | − | |
| 10 | −#define VOU_CRTC_MASK 0x3 | |
| 11 | − | |
| 12 | −/* VOU output interfaces */ | |
| 13 | −enum vou_inf_id { | |
| 14 | − VOU_HDMI = 0, | |
| 15 | − VOU_RGB_LCD = 1, | |
| 16 | − VOU_TV_ENC = 2, | |
| 17 | − VOU_MIPI_DSI = 3, | |
| 18 | − VOU_LVDS = 4, | |
| 19 | − VOU_VGA = 5, | |
| 20 | −}; | |
| 21 | − | |
| 22 | −enum vou_inf_hdmi_audio { | |
| 23 | − VOU_HDMI_AUD_SPDIF = BIT(0), | |
| 24 | − VOU_HDMI_AUD_I2S = BIT(1), | |
| 25 | − VOU_HDMI_AUD_DSD = BIT(2), | |
| 26 | − VOU_HDMI_AUD_HBR = BIT(3), | |
| 27 | − VOU_HDMI_AUD_PARALLEL = BIT(4), | |
| 28 | −}; | |
| 29 | − | |
| 30 | −void vou_inf_hdmi_audio_sel(struct drm_crtc *crtc, | |
| 31 | − enum vou_inf_hdmi_audio aud); | |
| 32 | −void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc); | |
| 33 | −void vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc); | |
| 34 | − | |
| 35 | −enum vou_div_id { | |
| 36 | − VOU_DIV_VGA, | |
| 37 | − VOU_DIV_PIC, | |
| 38 | − VOU_DIV_TVENC, | |
| 39 | − VOU_DIV_HDMI_PNX, | |
| 40 | − VOU_DIV_HDMI, | |
| 41 | − VOU_DIV_INF, | |
| 42 | − VOU_DIV_LAYER, | |
| 43 | −}; | |
| 44 | − | |
| 45 | −enum vou_div_val { | |
| 46 | − VOU_DIV_1 = 0, | |
| 47 | − VOU_DIV_2 = 1, | |
| 48 | − VOU_DIV_4 = 3, | |
| 49 | − VOU_DIV_8 = 7, | |
| 50 | −}; | |
| 51 | − | |
| 52 | −struct vou_div_config { | |
| 53 | − enum vou_div_id id; | |
| 54 | − enum vou_div_val val; | |
| 55 | −}; | |
| 56 | − | |
| 57 | −void zx_vou_config_dividers(struct drm_crtc *crtc, | |
| 58 | − struct vou_div_config *configs, int num); | |
| 59 | − | |
| 60 | −void zx_vou_layer_enable(struct drm_plane *plane); | |
| 61 | −void zx_vou_layer_disable(struct drm_plane *plane, | |
| 62 | − struct drm_plane_state *old_state); | |
| 63 | − | |
| 64 | −#endif /* __ZX_VOU_H__ */ | |
| @@ -1,212 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * Copyright 2016 Linaro Ltd. | |
| 4 | − * Copyright 2016 ZTE Corporation. | |
| 5 | − */ | |
| 6 | − | |
| 7 | −#ifndef __ZX_VOU_REGS_H__ | |
| 8 | −#define __ZX_VOU_REGS_H__ | |
| 9 | − | |
| 10 | −/* Sub-module offset */ | |
| 11 | −#define MAIN_GL_OFFSET 0x130 | |
| 12 | −#define MAIN_GL_CSC_OFFSET 0x580 | |
| 13 | −#define MAIN_CHN_CSC_OFFSET 0x6c0 | |
| 14 | −#define MAIN_HBSC_OFFSET 0x820 | |
| 15 | −#define MAIN_DITHER_OFFSET 0x960 | |
| 16 | −#define MAIN_RSZ_OFFSET 0x600 /* OTFPPU sub-module */ | |
| 17 | − | |
| 18 | −#define AUX_GL_OFFSET 0x200 | |
| 19 | −#define AUX_GL_CSC_OFFSET 0x5d0 | |
| 20 | −#define AUX_CHN_CSC_OFFSET 0x710 | |
| 21 | −#define AUX_HBSC_OFFSET 0x860 | |
| 22 | −#define AUX_DITHER_OFFSET 0x970 | |
| 23 | −#define AUX_RSZ_OFFSET 0x800 | |
| 24 | − | |
| 25 | −#define OSD_VL0_OFFSET 0x040 | |
| 26 | −#define OSD_VL_OFFSET(i) (OSD_VL0_OFFSET + 0x050 * (i)) | |
| 27 | − | |
| 28 | −#define HBSC_VL0_OFFSET 0x760 | |
| 29 | −#define HBSC_VL_OFFSET(i) (HBSC_VL0_OFFSET + 0x040 * (i)) | |
| 30 | − | |
| 31 | −#define RSZ_VL1_U0 0xa00 | |
| 32 | −#define RSZ_VL_OFFSET(i) (RSZ_VL1_U0 + 0x200 * (i)) | |
| 33 | − | |
| 34 | −/* OSD (GPC_GLOBAL) registers */ | |
| 35 | −#define OSD_INT_STA 0x04 | |
| 36 | −#define OSD_INT_CLRSTA 0x08 | |
| 37 | −#define OSD_INT_MSK 0x0c | |
| 38 | −#define OSD_INT_AUX_UPT BIT(14) | |
| 39 | −#define OSD_INT_MAIN_UPT BIT(13) | |
| 40 | −#define OSD_INT_GL1_LBW BIT(10) | |
| 41 | −#define OSD_INT_GL0_LBW BIT(9) | |
| 42 | −#define OSD_INT_VL2_LBW BIT(8) | |
| 43 | −#define OSD_INT_VL1_LBW BIT(7) | |
| 44 | −#define OSD_INT_VL0_LBW BIT(6) | |
| 45 | −#define OSD_INT_BUS_ERR BIT(3) | |
| 46 | −#define OSD_INT_CFG_ERR BIT(2) | |
| 47 | −#define OSD_INT_ERROR (\ | |
| 48 | − OSD_INT_GL1_LBW | OSD_INT_GL0_LBW | \ | |
| 49 | − OSD_INT_VL2_LBW | OSD_INT_VL1_LBW | OSD_INT_VL0_LBW | \ | |
| 50 | − OSD_INT_BUS_ERR | OSD_INT_CFG_ERR \ | |
| 51 | −) | |
| 52 | −#define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT) | |
| 53 | −#define OSD_CTRL0 0x10 | |
| 54 | −#define OSD_CTRL0_VL0_EN BIT(13) | |
| 55 | −#define OSD_CTRL0_VL0_SEL BIT(12) | |
| 56 | −#define OSD_CTRL0_VL1_EN BIT(11) | |
| 57 | −#define OSD_CTRL0_VL1_SEL BIT(10) | |
| 58 | −#define OSD_CTRL0_VL2_EN BIT(9) | |
| 59 | −#define OSD_CTRL0_VL2_SEL BIT(8) | |
| 60 | −#define OSD_CTRL0_GL0_EN BIT(7) | |
| 61 | −#define OSD_CTRL0_GL0_SEL BIT(6) | |
| 62 | −#define OSD_CTRL0_GL1_EN BIT(5) | |
| 63 | −#define OSD_CTRL0_GL1_SEL BIT(4) | |
| 64 | −#define OSD_RST_CLR 0x1c | |
| 65 | −#define RST_PER_FRAME BIT(19) | |
| 66 | − | |
| 67 | −/* Main/Aux channel registers */ | |
| 68 | −#define OSD_MAIN_CHN 0x470 | |
| 69 | −#define OSD_AUX_CHN 0x4d0 | |
| 70 | −#define CHN_CTRL0 0x00 | |
| 71 | −#define CHN_ENABLE BIT(0) | |
| 72 | −#define CHN_CTRL1 0x04 | |
| 73 | −#define CHN_SCREEN_W_SHIFT 18 | |
| 74 | −#define CHN_SCREEN_W_MASK (0x1fff << CHN_SCREEN_W_SHIFT) | |
| 75 | −#define CHN_SCREEN_H_SHIFT 5 | |
| 76 | −#define CHN_SCREEN_H_MASK (0x1fff << CHN_SCREEN_H_SHIFT) | |
| 77 | −#define CHN_UPDATE 0x08 | |
| 78 | −#define CHN_INTERLACE_BUF_CTRL 0x24 | |
| 79 | −#define CHN_INTERLACE_EN BIT(2) | |
| 80 | − | |
| 81 | −/* Dither registers */ | |
| 82 | −#define OSD_DITHER_CTRL0 0x00 | |
| 83 | −#define DITHER_BYSPASS BIT(31) | |
| 84 | − | |
| 85 | −/* TIMING_CTRL registers */ | |
| 86 | −#define TIMING_TC_ENABLE 0x04 | |
| 87 | −#define AUX_TC_EN BIT(1) | |
| 88 | −#define MAIN_TC_EN BIT(0) | |
| 89 | −#define FIR_MAIN_ACTIVE 0x08 | |
| 90 | −#define FIR_AUX_ACTIVE 0x0c | |
| 91 | −#define V_ACTIVE_SHIFT 16 | |
| 92 | −#define V_ACTIVE_MASK (0xffff << V_ACTIVE_SHIFT) | |
| 93 | −#define H_ACTIVE_SHIFT 0 | |
| 94 | −#define H_ACTIVE_MASK (0xffff << H_ACTIVE_SHIFT) | |
| 95 | −#define FIR_MAIN_H_TIMING 0x10 | |
| 96 | −#define FIR_MAIN_V_TIMING 0x14 | |
| 97 | −#define FIR_AUX_H_TIMING 0x18 | |
| 98 | −#define FIR_AUX_V_TIMING 0x1c | |
| 99 | −#define SYNC_WIDE_SHIFT 22 | |
| 100 | −#define SYNC_WIDE_MASK (0x3ff << SYNC_WIDE_SHIFT) | |
| 101 | −#define BACK_PORCH_SHIFT 11 | |
| 102 | −#define BACK_PORCH_MASK (0x7ff << BACK_PORCH_SHIFT) | |
| 103 | −#define FRONT_PORCH_SHIFT 0 | |
| 104 | −#define FRONT_PORCH_MASK (0x7ff << FRONT_PORCH_SHIFT) | |
| 105 | −#define TIMING_CTRL 0x20 | |
| 106 | −#define AUX_POL_SHIFT 3 | |
| 107 | −#define AUX_POL_MASK (0x7 << AUX_POL_SHIFT) | |
| 108 | −#define MAIN_POL_SHIFT 0 | |
| 109 | −#define MAIN_POL_MASK (0x7 << MAIN_POL_SHIFT) | |
| 110 | −#define POL_DE_SHIFT 2 | |
| 111 | −#define POL_VSYNC_SHIFT 1 | |
| 112 | −#define POL_HSYNC_SHIFT 0 | |
| 113 | −#define TIMING_INT_CTRL 0x24 | |
| 114 | −#define TIMING_INT_STATE 0x28 | |
| 115 | −#define TIMING_INT_AUX_FRAME BIT(3) | |
| 116 | −#define TIMING_INT_MAIN_FRAME BIT(1) | |
| 117 | −#define TIMING_INT_AUX_FRAME_SEL_VSW (0x2 << 10) | |
| 118 | −#define TIMING_INT_MAIN_FRAME_SEL_VSW (0x2 << 6) | |
| 119 | −#define TIMING_INT_ENABLE (\ | |
| 120 | − TIMING_INT_MAIN_FRAME_SEL_VSW | TIMING_INT_AUX_FRAME_SEL_VSW | \ | |
| 121 | − TIMING_INT_MAIN_FRAME | TIMING_INT_AUX_FRAME \ | |
| 122 | −) | |
| 123 | −#define TIMING_MAIN_SHIFT 0x2c | |
| 124 | −#define TIMING_AUX_SHIFT 0x30 | |
| 125 | −#define H_SHIFT_VAL 0x0048 | |
| 126 | −#define V_SHIFT_VAL 0x0001 | |
| 127 | −#define SCAN_CTRL 0x34 | |
| 128 | −#define AUX_PI_EN BIT(19) | |
| 129 | −#define MAIN_PI_EN BIT(18) | |
| 130 | −#define AUX_INTERLACE_SEL BIT(1) | |
| 131 | −#define MAIN_INTERLACE_SEL BIT(0) | |
| 132 | −#define SEC_V_ACTIVE 0x38 | |
| 133 | −#define SEC_VACT_MAIN_SHIFT 0 | |
| 134 | −#define SEC_VACT_MAIN_MASK (0xffff << SEC_VACT_MAIN_SHIFT) | |
| 135 | −#define SEC_VACT_AUX_SHIFT 16 | |
| 136 | −#define SEC_VACT_AUX_MASK (0xffff << SEC_VACT_AUX_SHIFT) | |
| 137 | −#define SEC_MAIN_V_TIMING 0x3c | |
| 138 | −#define SEC_AUX_V_TIMING 0x40 | |
| 139 | −#define TIMING_MAIN_PI_SHIFT 0x68 | |
| 140 | −#define TIMING_AUX_PI_SHIFT 0x6c | |
| 141 | −#define H_PI_SHIFT_VAL 0x000f | |
| 142 | − | |
| 143 | −#define V_ACTIVE(x) (((x) << V_ACTIVE_SHIFT) & V_ACTIVE_MASK) | |
| 144 | −#define H_ACTIVE(x) (((x) << H_ACTIVE_SHIFT) & H_ACTIVE_MASK) | |
| 145 | − | |
| 146 | −#define SYNC_WIDE(x) (((x) << SYNC_WIDE_SHIFT) & SYNC_WIDE_MASK) | |
| 147 | −#define BACK_PORCH(x) (((x) << BACK_PORCH_SHIFT) & BACK_PORCH_MASK) | |
| 148 | −#define FRONT_PORCH(x) (((x) << FRONT_PORCH_SHIFT) & FRONT_PORCH_MASK) | |
| 149 | − | |
| 150 | −/* DTRC registers */ | |
| 151 | −#define DTRC_F0_CTRL 0x2c | |
| 152 | −#define DTRC_F1_CTRL 0x5c | |
| 153 | −#define DTRC_DECOMPRESS_BYPASS BIT(17) | |
| 154 | −#define DTRC_DETILE_CTRL 0x68 | |
| 155 | −#define TILE2RASTESCAN_BYPASS_MODE BIT(30) | |
| 156 | −#define DETILE_ARIDR_MODE_MASK (0x3 << 0) | |
| 157 | −#define DETILE_ARID_ALL 0 | |
| 158 | −#define DETILE_ARID_IN_ARIDR 1 | |
| 159 | −#define DETILE_ARID_BYP_BUT_ARIDR 2 | |
| 160 | −#define DETILE_ARID_IN_ARIDR2 3 | |
| 161 | −#define DTRC_ARID 0x6c | |
| 162 | −#define DTRC_ARID3_SHIFT 24 | |
| 163 | −#define DTRC_ARID3_MASK (0xff << DTRC_ARID3_SHIFT) | |
| 164 | −#define DTRC_ARID2_SHIFT 16 | |
| 165 | −#define DTRC_ARID2_MASK (0xff << DTRC_ARID2_SHIFT) | |
| 166 | −#define DTRC_ARID1_SHIFT 8 | |
| 167 | −#define DTRC_ARID1_MASK (0xff << DTRC_ARID1_SHIFT) | |
| 168 | −#define DTRC_ARID0_SHIFT 0 | |
| 169 | −#define DTRC_ARID0_MASK (0xff << DTRC_ARID0_SHIFT) | |
| 170 | −#define DTRC_DEC2DDR_ARID 0x70 | |
| 171 | − | |
| 172 | −#define DTRC_ARID3(x) (((x) << DTRC_ARID3_SHIFT) & DTRC_ARID3_MASK) | |
| 173 | −#define DTRC_ARID2(x) (((x) << DTRC_ARID2_SHIFT) & DTRC_ARID2_MASK) | |
| 174 | −#define DTRC_ARID1(x) (((x) << DTRC_ARID1_SHIFT) & DTRC_ARID1_MASK) | |
| 175 | −#define DTRC_ARID0(x) (((x) << DTRC_ARID0_SHIFT) & DTRC_ARID0_MASK) | |
| 176 | − | |
| 177 | −/* VOU_CTRL registers */ | |
| 178 | −#define VOU_INF_EN 0x00 | |
| 179 | −#define VOU_INF_CH_SEL 0x04 | |
| 180 | −#define VOU_INF_DATA_SEL 0x08 | |
| 181 | −#define VOU_SOFT_RST 0x14 | |
| 182 | −#define VOU_CLK_SEL 0x18 | |
| 183 | −#define VGA_AUX_DIV_SHIFT 29 | |
| 184 | −#define VGA_MAIN_DIV_SHIFT 26 | |
| 185 | −#define PIC_MAIN_DIV_SHIFT 23 | |
| 186 | −#define PIC_AUX_DIV_SHIFT 20 | |
| 187 | −#define VOU_CLK_VL2_SEL BIT(8) | |
| 188 | −#define VOU_CLK_VL1_SEL BIT(7) | |
| 189 | −#define VOU_CLK_VL0_SEL BIT(6) | |
| 190 | −#define VOU_CLK_GL1_SEL BIT(5) | |
| 191 | −#define VOU_CLK_GL0_SEL BIT(4) | |
| 192 | −#define VOU_DIV_PARA 0x1c | |
| 193 | −#define DIV_PARA_UPDATE BIT(31) | |
| 194 | −#define TVENC_AUX_DIV_SHIFT 28 | |
| 195 | −#define HDMI_AUX_PNX_DIV_SHIFT 25 | |
| 196 | −#define HDMI_MAIN_PNX_DIV_SHIFT 22 | |
| 197 | −#define HDMI_AUX_DIV_SHIFT 19 | |
| 198 | −#define HDMI_MAIN_DIV_SHIFT 16 | |
| 199 | −#define TVENC_MAIN_DIV_SHIFT 13 | |
| 200 | −#define INF_AUX_DIV_SHIFT 9 | |
| 201 | −#define INF_MAIN_DIV_SHIFT 6 | |
| 202 | −#define LAYER_AUX_DIV_SHIFT 3 | |
| 203 | −#define LAYER_MAIN_DIV_SHIFT 0 | |
| 204 | −#define VOU_CLK_REQEN 0x20 | |
| 205 | −#define VOU_CLK_EN 0x24 | |
| 206 | −#define VOU_INF_HDMI_CTRL 0x30 | |
| 207 | −#define VOU_HDMI_AUD_MASK 0x1f | |
| 208 | − | |
| 209 | −/* OTFPPU_CTRL registers */ | |
| 210 | −#define OTFPPU_RSZ_DATA_SOURCE 0x04 | |
| 211 | − | |
| 212 | −#endif /* __ZX_VOU_REGS_H__ */ | |
| @@ -4,6 +4,7 @@ menu "USB Type-C Alternate Mode drivers" · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | config TYPEC_DP_ALTMODE |
| 6 | 6 | tristate "DisplayPort Alternate Mode driver" |
| 7 | + depends on DRM | |
| 7 | 8 | help |
| 8 | 9 | DisplayPort USB Type-C Alternate Mode allows DisplayPort |
| 9 | 10 | displays and adapters to be attached to the USB Type-C |
| @@ -11,8 +11,10 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <linux/delay.h> |
| 12 | 12 | #include <linux/mutex.h> |
| 13 | 13 | #include <linux/module.h> |
| 14 | +#include <linux/property.h> | |
| 14 | 15 | #include <linux/usb/pd_vdo.h> |
| 15 | 16 | #include <linux/usb/typec_dp.h> |
| 17 | +#include <drm/drm_connector.h> | |
| 16 | 18 | #include "displayport.h" |
| 17 | 19 | |
| 18 | 20 | #define DP_HEADER(_dp, ver, cmd) (VDO((_dp)->alt->svid, 1, ver, cmd) \ |
| @@ -57,19 +59,28 @@ struct dp_altmode { · 38 unmodified lines | ||
| 57 | 59 | struct typec_displayport_data data; |
| 58 | 60 | |
| 59 | 61 | enum dp_state state; |
| 62 | + bool hpd; | |
| 60 | 63 | |
| 61 | 64 | struct mutex lock; /* device lock */ |
| 62 | 65 | struct work_struct work; |
| 63 | 66 | struct typec_altmode *alt; |
| 64 | 67 | const struct typec_altmode *port; |
| 68 | + struct fwnode_handle *connector_fwnode; | |
| 65 | 69 | }; |
| 66 | 70 | |
| 67 | 71 | static int dp_altmode_notify(struct dp_altmode *dp) |
| 68 | 72 | { |
| 69 | − u8 state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); | |
| 73 | + unsigned long conf; | |
| 74 | + u8 state; | |
| 75 | + | |
| 76 | + if (dp->data.conf) { | |
| 77 | + state = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf)); | |
| 78 | + conf = TYPEC_MODAL_STATE(state); | |
| 79 | + } else { | |
| 80 | + conf = TYPEC_STATE_USB; | |
| 81 | + } | |
| 70 | 82 | |
| 71 | − return typec_altmode_notify(dp->alt, TYPEC_MODAL_STATE(state), | |
| 72 | − &dp->data); | |
| 83 | + return typec_altmode_notify(dp->alt, conf, &dp->data); | |
| 73 | 84 | } |
| 74 | 85 | |
| 75 | 86 | static int dp_altmode_configure(struct dp_altmode *dp, u8 con) |
| @@ -118,6 +129,7 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con) · 42 unmodified lines | ||
| 118 | 129 | static int dp_altmode_status_update(struct dp_altmode *dp) |
| 119 | 130 | { |
| 120 | 131 | bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf); |
| 132 | + bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE); | |
| 121 | 133 | u8 con = DP_STATUS_CONNECTION(dp->data.status); |
| 122 | 134 | int ret = 0; |
| 123 | 135 | |
| @@ -130,6 +142,11 @@ static int dp_altmode_status_update(struct dp_altmode *dp) · 6 unmodified lines | ||
| 130 | 142 | ret = dp_altmode_configure(dp, con); |
| 131 | 143 | if (!ret) |
| 132 | 144 | dp->state = DP_STATE_CONFIGURE; |
| 145 | + } else { | |
| 146 | + if (dp->hpd != hpd) { | |
| 147 | + drm_connector_oob_hotplug_event(dp->connector_fwnode); | |
| 148 | + dp->hpd = hpd; | |
| 149 | + } | |
| 133 | 150 | } |
| 134 | 151 | |
| 135 | 152 | return ret; |
| @@ -137,21 +154,10 @@ static int dp_altmode_status_update(struct dp_altmode *dp) · 1 unmodified line | ||
| 137 | 154 | |
| 138 | 155 | static int dp_altmode_configured(struct dp_altmode *dp) |
| 139 | 156 | { |
| 140 | − int ret; | |
| 141 | − | |
| 142 | 157 | sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); |
| 143 | − | |
| 144 | − if (!dp->data.conf) | |
| 145 | − return typec_altmode_notify(dp->alt, TYPEC_STATE_USB, | |
| 146 | − &dp->data); | |
| 147 | − | |
| 148 | − ret = dp_altmode_notify(dp); | |
| 149 | − if (ret) | |
| 150 | − return ret; | |
| 151 | − | |
| 152 | 158 | sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment"); |
| 153 | 159 | |
| 154 | − return 0; | |
| 160 | + return dp_altmode_notify(dp); | |
| 155 | 161 | } |
| 156 | 162 | |
| 157 | 163 | static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) |
| @@ -172,13 +178,8 @@ static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) · 14 unmodified lines | ||
| 172 | 178 | } |
| 173 | 179 | |
| 174 | 180 | ret = typec_altmode_vdm(dp->alt, header, &conf, 2); |
| 175 | − if (ret) { | |
| 176 | − if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) | |
| 177 | − dp_altmode_notify(dp); | |
| 178 | − else | |
| 179 | − typec_altmode_notify(dp->alt, TYPEC_STATE_USB, | |
| 180 | − &dp->data); | |
| 181 | − } | |
| 181 | + if (ret) | |
| 182 | + dp_altmode_notify(dp); | |
| 182 | 183 | |
| 183 | 184 | return ret; |
| 184 | 185 | } |
| @@ -521,6 +522,7 @@ static const struct attribute_group dp_altmode_group = { · 336 unmodified lines | ||
| 521 | 522 | int dp_altmode_probe(struct typec_altmode *alt) |
| 522 | 523 | { |
| 523 | 524 | const struct typec_altmode *port = typec_altmode_get_partner(alt); |
| 525 | + struct fwnode_handle *fwnode; | |
| 524 | 526 | struct dp_altmode *dp; |
| 525 | 527 | int ret; |
| 526 | 528 | |
| @@ -549,6 +551,11 @@ int dp_altmode_probe(struct typec_altmode *alt) · 22 unmodified lines | ||
| 549 | 551 | alt->desc = "DisplayPort"; |
| 550 | 552 | alt->ops = &dp_altmode_ops; |
| 551 | 553 | |
| 554 | + fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */ | |
| 555 | + dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0); | |
| 556 | + if (IS_ERR(dp->connector_fwnode)) | |
| 557 | + dp->connector_fwnode = NULL; | |
| 558 | + | |
| 552 | 559 | typec_altmode_set_drvdata(alt, dp); |
| 553 | 560 | |
| 554 | 561 | dp->state = DP_STATE_ENTER; |
| @@ -564,6 +571,13 @@ void dp_altmode_remove(struct typec_altmode *alt) · 9 unmodified lines | ||
| 564 | 571 | |
| 565 | 572 | sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); |
| 566 | 573 | cancel_work_sync(&dp->work); |
| 574 | + | |
| 575 | + if (dp->connector_fwnode) { | |
| 576 | + if (dp->hpd) | |
| 577 | + drm_connector_oob_hotplug_event(dp->connector_fwnode); | |
| 578 | + | |
| 579 | + fwnode_handle_put(dp->connector_fwnode); | |
| 580 | + } | |
| 567 | 581 | } |
| 568 | 582 | EXPORT_SYMBOL_GPL(dp_altmode_remove); |
| 569 | 583 | |
| @@ -911,6 +911,8 @@ struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, · 910 unmodified lines | ||
| 911 | 911 | struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, |
| 912 | 912 | struct drm_panel *panel, |
| 913 | 913 | u32 connector_type); |
| 914 | +struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node *node, | |
| 915 | + u32 port, u32 endpoint); | |
| 914 | 916 | struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge); |
| 915 | 917 | #endif |
| 916 | 918 | |
| @@ -1084,6 +1084,14 @@ struct drm_connector_funcs { · 1083 unmodified lines | ||
| 1084 | 1084 | */ |
| 1085 | 1085 | void (*atomic_print_state)(struct drm_printer *p, |
| 1086 | 1086 | const struct drm_connector_state *state); |
| 1087 | + | |
| 1088 | + /** | |
| 1089 | + * @oob_hotplug_event: | |
| 1090 | + * | |
| 1091 | + * This will get called when a hotplug-event for a drm-connector | |
| 1092 | + * has been received from a source outside the display driver / device. | |
| 1093 | + */ | |
| 1094 | + void (*oob_hotplug_event)(struct drm_connector *connector); | |
| 1087 | 1095 | }; |
| 1088 | 1096 | |
| 1089 | 1097 | /** |
| @@ -1228,6 +1236,14 @@ struct drm_connector { · 138 unmodified lines | ||
| 1228 | 1236 | struct device *kdev; |
| 1229 | 1237 | /** @attr: sysfs attributes */ |
| 1230 | 1238 | struct device_attribute *attr; |
| 1239 | + /** | |
| 1240 | + * @fwnode: associated fwnode supplied by platform firmware | |
| 1241 | + * | |
| 1242 | + * Drivers can set this to associate a fwnode with a connector, drivers | |
| 1243 | + * are expected to get a reference on the fwnode when setting this. | |
| 1244 | + * drm_connector_cleanup() will call fwnode_handle_put() on this. | |
| 1245 | + */ | |
| 1246 | + struct fwnode_handle *fwnode; | |
| 1231 | 1247 | |
| 1232 | 1248 | /** |
| 1233 | 1249 | * @head: |
| @@ -1239,6 +1255,14 @@ struct drm_connector { · 5 unmodified lines | ||
| 1239 | 1255 | */ |
| 1240 | 1256 | struct list_head head; |
| 1241 | 1257 | |
| 1258 | + /** | |
| 1259 | + * @global_connector_list_entry: | |
| 1260 | + * | |
| 1261 | + * Connector entry in the global connector-list, used by | |
| 1262 | + * drm_connector_find_by_fwnode(). | |
| 1263 | + */ | |
| 1264 | + struct list_head global_connector_list_entry; | |
| 1265 | + | |
| 1242 | 1266 | /** @base: base KMS object */ |
| 1243 | 1267 | struct drm_mode_object base; |
| 1244 | 1268 | |
| @@ -1650,6 +1674,7 @@ drm_connector_is_unregistered(struct drm_connector *connector) · 405 unmodified lines | ||
| 1650 | 1674 | DRM_CONNECTOR_UNREGISTERED; |
| 1651 | 1675 | } |
| 1652 | 1676 | |
| 1677 | +void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode); | |
| 1653 | 1678 | const char *drm_get_connector_type_name(unsigned int connector_type); |
| 1654 | 1679 | const char *drm_get_connector_status_name(enum drm_connector_status status); |
| 1655 | 1680 | const char *drm_get_subpixel_order_name(enum subpixel_order order); |
| @@ -167,7 +167,6 @@ struct drm_ioctl_desc { · 166 unmodified lines | ||
| 167 | 167 | .name = #ioctl \ |
| 168 | 168 | } |
| 169 | 169 | |
| 170 | −int drm_ioctl_permit(u32 flags, struct drm_file *file_priv); | |
| 171 | 170 | long drm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); |
| 172 | 171 | long drm_ioctl_kernel(struct file *, drm_ioctl_t, void *, u32); |
| 173 | 172 | #ifdef CONFIG_COMPAT |
| @@ -103,14 +103,13 @@ struct drm_mode_config_funcs { · 102 unmodified lines | ||
| 103 | 103 | * Callback used by helpers to inform the driver of output configuration |
| 104 | 104 | * changes. |
| 105 | 105 | * |
| 106 | − * Drivers implementing fbdev emulation with the helpers can call | |
| 107 | − * drm_fb_helper_hotplug_changed from this hook to inform the fbdev | |
| 108 | − * helper of output changes. | |
| 106 | + * Drivers implementing fbdev emulation use drm_kms_helper_hotplug_event() | |
| 107 | + * to call this hook to inform the fbdev helper of output changes. | |
| 109 | 108 | * |
| 110 | − * FIXME: | |
| 111 | − * | |
| 112 | − * Except that there's no vtable for device-level helper callbacks | |
| 113 | − * there's no reason this is a core function. | |
| 109 | + * This hook is deprecated, drivers should instead use | |
| 110 | + * drm_fbdev_generic_setup() which takes care of any necessary | |
| 111 | + * hotplug event forwarding already without further involvement by | |
| 112 | + * the driver. | |
| 114 | 113 | */ |
| 115 | 114 | void (*output_poll_changed)(struct drm_device *dev); |
| 116 | 115 | |
| @@ -43,7 +43,7 @@ enum drm_scaling_filter { · 42 unmodified lines | ||
| 43 | 43 | /** |
| 44 | 44 | * struct drm_plane_state - mutable plane state |
| 45 | 45 | * |
| 46 | − * Please not that the destination coordinates @crtc_x, @crtc_y, @crtc_h and | |
| 46 | + * Please note that the destination coordinates @crtc_x, @crtc_y, @crtc_h and | |
| 47 | 47 | * @crtc_w and the source coordinates @src_x, @src_y, @src_h and @src_w are the |
| 48 | 48 | * raw coordinates provided by userspace. Drivers should use |
| 49 | 49 | * drm_atomic_helper_check_plane_state() and only use the derived rectangles in |
| @@ -27,9 +27,12 @@ · 26 unmodified lines | ||
| 27 | 27 | #include <drm/spsc_queue.h> |
| 28 | 28 | #include <linux/dma-fence.h> |
| 29 | 29 | #include <linux/completion.h> |
| 30 | +#include <linux/xarray.h> | |
| 30 | 31 | |
| 31 | 32 | #define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000) |
| 32 | 33 | |
| 34 | +struct drm_gem_object; | |
| 35 | + | |
| 33 | 36 | struct drm_gpu_scheduler; |
| 34 | 37 | struct drm_sched_rq; |
| 35 | 38 | |
| @@ -50,56 +53,147 @@ enum drm_sched_priority { · 14 unmodified lines | ||
| 50 | 53 | * struct drm_sched_entity - A wrapper around a job queue (typically |
| 51 | 54 | * attached to the DRM file_priv). |
| 52 | 55 | * |
| 53 | − * @list: used to append this struct to the list of entities in the | |
| 54 | − * runqueue. | |
| 55 | − * @rq: runqueue on which this entity is currently scheduled. | |
| 56 | − * @sched_list: A list of schedulers (drm_gpu_schedulers). | |
| 57 | − * Jobs from this entity can be scheduled on any scheduler | |
| 58 | − * on this list. | |
| 59 | − * @num_sched_list: number of drm_gpu_schedulers in the sched_list. | |
| 60 | − * @priority: priority of the entity | |
| 61 | − * @rq_lock: lock to modify the runqueue to which this entity belongs. | |
| 62 | − * @job_queue: the list of jobs of this entity. | |
| 63 | − * @fence_seq: a linearly increasing seqno incremented with each | |
| 64 | − * new &drm_sched_fence which is part of the entity. | |
| 65 | − * @fence_context: a unique context for all the fences which belong | |
| 66 | − * to this entity. | |
| 67 | − * The &drm_sched_fence.scheduled uses the | |
| 68 | − * fence_context but &drm_sched_fence.finished uses | |
| 69 | − * fence_context + 1. | |
| 70 | − * @dependency: the dependency fence of the job which is on the top | |
| 71 | − * of the job queue. | |
| 72 | − * @cb: callback for the dependency fence above. | |
| 73 | − * @guilty: points to ctx's guilty. | |
| 74 | − * @fini_status: contains the exit status in case the process was signalled. | |
| 75 | − * @last_scheduled: points to the finished fence of the last scheduled job. | |
| 76 | − * @last_user: last group leader pushing a job into the entity. | |
| 77 | − * @stopped: Marks the enity as removed from rq and destined for termination. | |
| 78 | − * @entity_idle: Signals when enityt is not in use | |
| 79 | − * | |
| 80 | 56 | * Entities will emit jobs in order to their corresponding hardware |
| 81 | 57 | * ring, and the scheduler will alternate between entities based on |
| 82 | 58 | * scheduling policy. |
| 83 | 59 | */ |
| 84 | 60 | struct drm_sched_entity { |
| 61 | + /** | |
| 62 | + * @list: | |
| 63 | + * | |
| 64 | + * Used to append this struct to the list of entities in the runqueue | |
| 65 | + * @rq under &drm_sched_rq.entities. | |
| 66 | + * | |
| 67 | + * Protected by &drm_sched_rq.lock of @rq. | |
| 68 | + */ | |
| 85 | 69 | struct list_head list; |
| 70 | + | |
| 71 | + /** | |
| 72 | + * @rq: | |
| 73 | + * | |
| 74 | + * Runqueue on which this entity is currently scheduled. | |
| 75 | + * | |
| 76 | + * FIXME: Locking is very unclear for this. Writers are protected by | |
| 77 | + * @rq_lock, but readers are generally lockless and seem to just race | |
| 78 | + * with not even a READ_ONCE. | |
| 79 | + */ | |
| 86 | 80 | struct drm_sched_rq *rq; |
| 81 | + | |
| 82 | + /** | |
| 83 | + * @sched_list: | |
| 84 | + * | |
| 85 | + * A list of schedulers (struct drm_gpu_scheduler). Jobs from this entity can | |
| 86 | + * be scheduled on any scheduler on this list. | |
| 87 | + * | |
| 88 | + * This can be modified by calling drm_sched_entity_modify_sched(). | |
| 89 | + * Locking is entirely up to the driver, see the above function for more | |
| 90 | + * details. | |
| 91 | + * | |
| 92 | + * This will be set to NULL if &num_sched_list equals 1 and @rq has been | |
| 93 | + * set already. | |
| 94 | + * | |
| 95 | + * FIXME: This means priority changes through | |
| 96 | + * drm_sched_entity_set_priority() will be lost henceforth in this case. | |
| 97 | + */ | |
| 87 | 98 | struct drm_gpu_scheduler **sched_list; |
| 99 | + | |
| 100 | + /** | |
| 101 | + * @num_sched_list: | |
| 102 | + * | |
| 103 | + * Number of drm_gpu_schedulers in the @sched_list. | |
| 104 | + */ | |
| 88 | 105 | unsigned int num_sched_list; |
| 106 | + | |
| 107 | + /** | |
| 108 | + * @priority: | |
| 109 | + * | |
| 110 | + * Priority of the entity. This can be modified by calling | |
| 111 | + * drm_sched_entity_set_priority(). Protected by &rq_lock. | |
| 112 | + */ | |
| 89 | 113 | enum drm_sched_priority priority; |
| 114 | + | |
| 115 | + /** | |
| 116 | + * @rq_lock: | |
| 117 | + * | |
| 118 | + * Lock to modify the runqueue to which this entity belongs. | |
| 119 | + */ | |
| 90 | 120 | spinlock_t rq_lock; |
| 91 | 121 | |
| 122 | + /** | |
| 123 | + * @job_queue: the list of jobs of this entity. | |
| 124 | + */ | |
| 92 | 125 | struct spsc_queue job_queue; |
| 93 | 126 | |
| 127 | + /** | |
| 128 | + * @fence_seq: | |
| 129 | + * | |
| 130 | + * A linearly increasing seqno incremented with each new | |
| 131 | + * &drm_sched_fence which is part of the entity. | |
| 132 | + * | |
| 133 | + * FIXME: Callers of drm_sched_job_arm() need to ensure correct locking, | |
| 134 | + * this doesn't need to be atomic. | |
| 135 | + */ | |
| 94 | 136 | atomic_t fence_seq; |
| 137 | + | |
| 138 | + /** | |
| 139 | + * @fence_context: | |
| 140 | + * | |
| 141 | + * A unique context for all the fences which belong to this entity. The | |
| 142 | + * &drm_sched_fence.scheduled uses the fence_context but | |
| 143 | + * &drm_sched_fence.finished uses fence_context + 1. | |
| 144 | + */ | |
| 95 | 145 | uint64_t fence_context; |
| 96 | 146 | |
| 147 | + /** | |
| 148 | + * @dependency: | |
| 149 | + * | |
| 150 | + * The dependency fence of the job which is on the top of the job queue. | |
| 151 | + */ | |
| 97 | 152 | struct dma_fence *dependency; |
| 153 | + | |
| 154 | + /** | |
| 155 | + * @cb: | |
| 156 | + * | |
| 157 | + * Callback for the dependency fence above. | |
| 158 | + */ | |
| 98 | 159 | struct dma_fence_cb cb; |
| 160 | + | |
| 161 | + /** | |
| 162 | + * @guilty: | |
| 163 | + * | |
| 164 | + * Points to entities' guilty. | |
| 165 | + */ | |
| 99 | 166 | atomic_t *guilty; |
| 167 | + | |
| 168 | + /** | |
| 169 | + * @last_scheduled: | |
| 170 | + * | |
| 171 | + * Points to the finished fence of the last scheduled job. Only written | |
| 172 | + * by the scheduler thread, can be accessed locklessly from | |
| 173 | + * drm_sched_job_arm() iff the queue is empty. | |
| 174 | + */ | |
| 100 | 175 | struct dma_fence *last_scheduled; |
| 176 | + | |
| 177 | + /** | |
| 178 | + * @last_user: last group leader pushing a job into the entity. | |
| 179 | + */ | |
| 101 | 180 | struct task_struct *last_user; |
| 181 | + | |
| 182 | + /** | |
| 183 | + * @stopped: | |
| 184 | + * | |
| 185 | + * Marks the enity as removed from rq and destined for | |
| 186 | + * termination. This is set by calling drm_sched_entity_flush() and by | |
| 187 | + * drm_sched_fini(). | |
| 188 | + */ | |
| 102 | 189 | bool stopped; |
| 190 | + | |
| 191 | + /** | |
| 192 | + * @entity_idle: | |
| 193 | + * | |
| 194 | + * Signals when entity is not in use, used to sequence entity cleanup in | |
| 195 | + * drm_sched_entity_fini(). | |
| 196 | + */ | |
| 103 | 197 | struct completion entity_idle; |
| 104 | 198 | }; |
| 105 | 199 | |
| @@ -198,6 +292,17 @@ struct drm_sched_job { · 92 unmodified lines | ||
| 198 | 292 | enum drm_sched_priority s_priority; |
| 199 | 293 | struct drm_sched_entity *entity; |
| 200 | 294 | struct dma_fence_cb cb; |
| 295 | + /** | |
| 296 | + * @dependencies: | |
| 297 | + * | |
| 298 | + * Contains the dependencies as struct dma_fence for this job, see | |
| 299 | + * drm_sched_job_add_dependency() and | |
| 300 | + * drm_sched_job_add_implicit_dependencies(). | |
| 301 | + */ | |
| 302 | + struct xarray dependencies; | |
| 303 | + | |
| 304 | + /** @last_dependency: tracks @dependencies as they signal */ | |
| 305 | + unsigned long last_dependency; | |
| 201 | 306 | }; |
| 202 | 307 | |
| 203 | 308 | static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, |
| @@ -220,9 +325,15 @@ enum drm_gpu_sched_stat { · 16 unmodified lines | ||
| 220 | 325 | */ |
| 221 | 326 | struct drm_sched_backend_ops { |
| 222 | 327 | /** |
| 223 | − * @dependency: Called when the scheduler is considering scheduling | |
| 224 | − * this job next, to get another struct dma_fence for this job to | |
| 225 | − * block on. Once it returns NULL, run_job() may be called. | |
| 328 | + * @dependency: | |
| 329 | + * | |
| 330 | + * Called when the scheduler is considering scheduling this job next, to | |
| 331 | + * get another struct dma_fence for this job to block on. Once it | |
| 332 | + * returns NULL, run_job() may be called. | |
| 333 | + * | |
| 334 | + * If a driver exclusively uses drm_sched_job_add_dependency() and | |
| 335 | + * drm_sched_job_add_implicit_dependencies() this can be ommitted and | |
| 336 | + * left as NULL. | |
| 226 | 337 | */ |
| 227 | 338 | struct dma_fence *(*dependency)(struct drm_sched_job *sched_job, |
| 228 | 339 | struct drm_sched_entity *s_entity); |
| @@ -348,6 +459,14 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched); · 119 unmodified lines | ||
| 348 | 459 | int drm_sched_job_init(struct drm_sched_job *job, |
| 349 | 460 | struct drm_sched_entity *entity, |
| 350 | 461 | void *owner); |
| 462 | +void drm_sched_job_arm(struct drm_sched_job *job); | |
| 463 | +int drm_sched_job_add_dependency(struct drm_sched_job *job, | |
| 464 | + struct dma_fence *fence); | |
| 465 | +int drm_sched_job_add_implicit_dependencies(struct drm_sched_job *job, | |
| 466 | + struct drm_gem_object *obj, | |
| 467 | + bool write); | |
| 468 | + | |
| 469 | + | |
| 351 | 470 | void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, |
| 352 | 471 | struct drm_gpu_scheduler **sched_list, |
| 353 | 472 | unsigned int num_sched_list); |
| @@ -381,14 +500,17 @@ void drm_sched_entity_fini(struct drm_sched_entity *entity); · 27 unmodified lines | ||
| 381 | 500 | void drm_sched_entity_destroy(struct drm_sched_entity *entity); |
| 382 | 501 | void drm_sched_entity_select_rq(struct drm_sched_entity *entity); |
| 383 | 502 | struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); |
| 384 | −void drm_sched_entity_push_job(struct drm_sched_job *sched_job, | |
| 385 | − struct drm_sched_entity *entity); | |
| 503 | +void drm_sched_entity_push_job(struct drm_sched_job *sched_job); | |
| 386 | 504 | void drm_sched_entity_set_priority(struct drm_sched_entity *entity, |
| 387 | 505 | enum drm_sched_priority priority); |
| 388 | 506 | bool drm_sched_entity_is_ready(struct drm_sched_entity *entity); |
| 389 | 507 | |
| 390 | −struct drm_sched_fence *drm_sched_fence_create( | |
| 508 | +struct drm_sched_fence *drm_sched_fence_alloc( | |
| 391 | 509 | struct drm_sched_entity *s_entity, void *owner); |
| 510 | +void drm_sched_fence_init(struct drm_sched_fence *fence, | |
| 511 | + struct drm_sched_entity *entity); | |
| 512 | +void drm_sched_fence_free(struct drm_sched_fence *fence); | |
| 513 | + | |
| 392 | 514 | void drm_sched_fence_scheduled(struct drm_sched_fence *fence); |
| 393 | 515 | void drm_sched_fence_finished(struct drm_sched_fence *fence); |
| 394 | 516 | |
| @@ -264,18 +264,6 @@ static inline int ttm_bo_wait_ctx(struct ttm_buffer_object *bo, struct ttm_opera · 263 unmodified lines | ||
| 264 | 264 | return ttm_bo_wait(bo, ctx->interruptible, ctx->no_wait_gpu); |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | −/** | |
| 268 | − * ttm_bo_mem_compat - Check if proposed placement is compatible with a bo | |
| 269 | − * | |
| 270 | − * @placement: Return immediately if buffer is busy. | |
| 271 | − * @mem: The struct ttm_resource indicating the region where the bo resides | |
| 272 | − * @new_flags: Describes compatible placement found | |
| 273 | − * | |
| 274 | − * Returns true if the placement is compatible | |
| 275 | − */ | |
| 276 | −bool ttm_bo_mem_compat(struct ttm_placement *placement, struct ttm_resource *mem, | |
| 277 | − uint32_t *new_flags); | |
| 278 | − | |
| 279 | 267 | /** |
| 280 | 268 | * ttm_bo_validate |
| 281 | 269 | * |
| @@ -27,9 +27,26 @@ · 26 unmodified lines | ||
| 27 | 27 | |
| 28 | 28 | #define TTM_NUM_CACHING_TYPES 3 |
| 29 | 29 | |
| 30 | +/** | |
| 31 | + * enum ttm_caching - CPU caching and BUS snooping behavior. | |
| 32 | + */ | |
| 30 | 33 | enum ttm_caching { |
| 34 | + /** | |
| 35 | + * @ttm_uncached: Most defensive option for device mappings, | |
| 36 | + * don't even allow write combining. | |
| 37 | + */ | |
| 31 | 38 | ttm_uncached, |
| 39 | + | |
| 40 | + /** | |
| 41 | + * @ttm_write_combined: Don't cache read accesses, but allow at least | |
| 42 | + * writes to be combined. | |
| 43 | + */ | |
| 32 | 44 | ttm_write_combined, |
| 45 | + | |
| 46 | + /** | |
| 47 | + * @ttm_cached: Fully cached like normal system memory, requires that | |
| 48 | + * devices snoop the CPU cache on accesses. | |
| 49 | + */ | |
| 33 | 50 | ttm_cached |
| 34 | 51 | }; |
| 35 | 52 | |
| @@ -39,31 +39,23 @@ struct ttm_operation_ctx; · 38 unmodified lines | ||
| 39 | 39 | |
| 40 | 40 | /** |
| 41 | 41 | * struct ttm_global - Buffer object driver global data. |
| 42 | − * | |
| 43 | − * @dummy_read_page: Pointer to a dummy page used for mapping requests | |
| 44 | − * of unpopulated pages. | |
| 45 | − * @shrink: A shrink callback object used for buffer object swap. | |
| 46 | − * @device_list_mutex: Mutex protecting the device list. | |
| 47 | − * This mutex is held while traversing the device list for pm options. | |
| 48 | − * @lru_lock: Spinlock protecting the bo subsystem lru lists. | |
| 49 | − * @device_list: List of buffer object devices. | |
| 50 | − * @swap_lru: Lru list of buffer objects used for swapping. | |
| 51 | 42 | */ |
| 52 | 43 | extern struct ttm_global { |
| 53 | 44 | |
| 54 | 45 | /** |
| 55 | − * Constant after init. | |
| 46 | + * @dummy_read_page: Pointer to a dummy page used for mapping requests | |
| 47 | + * of unpopulated pages. Constant after init. | |
| 56 | 48 | */ |
| 57 | − | |
| 58 | 49 | struct page *dummy_read_page; |
| 59 | 50 | |
| 60 | 51 | /** |
| 61 | − * Protected by ttm_global_mutex. | |
| 52 | + * @device_list: List of buffer object devices. Protected by | |
| 53 | + * ttm_global_mutex. | |
| 62 | 54 | */ |
| 63 | 55 | struct list_head device_list; |
| 64 | 56 | |
| 65 | 57 | /** |
| 66 | − * Internal protection. | |
| 58 | + * @bo_count: Number of buffer objects allocated by devices. | |
| 67 | 59 | */ |
| 68 | 60 | atomic_t bo_count; |
| 69 | 61 | } ttm_glob; |
| @@ -230,49 +222,64 @@ struct ttm_device_funcs { · 160 unmodified lines | ||
| 230 | 222 | |
| 231 | 223 | /** |
| 232 | 224 | * struct ttm_device - Buffer object driver device-specific data. |
| 233 | − * | |
| 234 | − * @device_list: Our entry in the global device list. | |
| 235 | − * @funcs: Function table for the device. | |
| 236 | − * @sysman: Resource manager for the system domain. | |
| 237 | − * @man_drv: An array of resource_managers. | |
| 238 | − * @vma_manager: Address space manager. | |
| 239 | − * @pool: page pool for the device. | |
| 240 | − * @dev_mapping: A pointer to the struct address_space representing the | |
| 241 | − * device address space. | |
| 242 | − * @wq: Work queue structure for the delayed delete workqueue. | |
| 243 | 225 | */ |
| 244 | 226 | struct ttm_device { |
| 245 | − /* | |
| 227 | + /** | |
| 228 | + * @device_list: Our entry in the global device list. | |
| 246 | 229 | * Constant after bo device init |
| 247 | 230 | */ |
| 248 | 231 | struct list_head device_list; |
| 232 | + | |
| 233 | + /** | |
| 234 | + * @funcs: Function table for the device. | |
| 235 | + * Constant after bo device init | |
| 236 | + */ | |
| 249 | 237 | struct ttm_device_funcs *funcs; |
| 250 | 238 | |
| 251 | − /* | |
| 239 | + /** | |
| 240 | + * @sysman: Resource manager for the system domain. | |
| 252 | 241 | * Access via ttm_manager_type. |
| 253 | 242 | */ |
| 254 | 243 | struct ttm_resource_manager sysman; |
| 244 | + | |
| 245 | + /** | |
| 246 | + * @man_drv: An array of resource_managers, one per resource type. | |
| 247 | + */ | |
| 255 | 248 | struct ttm_resource_manager *man_drv[TTM_NUM_MEM_TYPES]; |
| 256 | 249 | |
| 257 | − /* | |
| 258 | − * Protected by internal locks. | |
| 250 | + /** | |
| 251 | + * @vma_manager: Address space manager for finding BOs to mmap. | |
| 259 | 252 | */ |
| 260 | 253 | struct drm_vma_offset_manager *vma_manager; |
| 254 | + | |
| 255 | + /** | |
| 256 | + * @pool: page pool for the device. | |
| 257 | + */ | |
| 261 | 258 | struct ttm_pool pool; |
| 262 | 259 | |
| 263 | − /* | |
| 264 | − * Protection for the per manager LRU and ddestroy lists. | |
| 260 | + /** | |
| 261 | + * @lru_lock: Protection for the per manager LRU and ddestroy lists. | |
| 265 | 262 | */ |
| 266 | 263 | spinlock_t lru_lock; |
| 264 | + | |
| 265 | + /** | |
| 266 | + * @ddestroy: Destroyed but not yet cleaned up buffer objects. | |
| 267 | + */ | |
| 267 | 268 | struct list_head ddestroy; |
| 268 | 269 | |
| 269 | − /* | |
| 270 | − * Protected by load / firstopen / lastclose /unload sync. | |
| 270 | + /** | |
| 271 | + * @pinned: Buffer objects which are pinned and so not on any LRU list. | |
| 272 | + */ | |
| 273 | + struct list_head pinned; | |
| 274 | + | |
| 275 | + /** | |
| 276 | + * @dev_mapping: A pointer to the struct address_space for invalidating | |
| 277 | + * CPU mappings on buffer move. Protected by load/unload sync. | |
| 271 | 278 | */ |
| 272 | 279 | struct address_space *dev_mapping; |
| 273 | 280 | |
| 274 | − /* | |
| 275 | − * Internal protection. | |
| 281 | + /** | |
| 282 | + * @wq: Work queue structure for the delayed delete workqueue. | |
| 276 | 283 | */ |
| 277 | 284 | struct delayed_work wq; |
| 278 | 285 | }; |
| @@ -284,12 +291,15 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, · 5 unmodified lines | ||
| 284 | 291 | static inline struct ttm_resource_manager * |
| 285 | 292 | ttm_manager_type(struct ttm_device *bdev, int mem_type) |
| 286 | 293 | { |
| 294 | + BUILD_BUG_ON(__builtin_constant_p(mem_type) | |
| 295 | + && mem_type >= TTM_NUM_MEM_TYPES); | |
| 287 | 296 | return bdev->man_drv[mem_type]; |
| 288 | 297 | } |
| 289 | 298 | |
| 290 | 299 | static inline void ttm_set_driver_manager(struct ttm_device *bdev, int type, |
| 291 | 300 | struct ttm_resource_manager *manager) |
| 292 | 301 | { |
| 302 | + BUILD_BUG_ON(__builtin_constant_p(type) && type >= TTM_NUM_MEM_TYPES); | |
| 293 | 303 | bdev->man_drv[type] = manager; |
| 294 | 304 | } |
| 295 | 305 | |
| @@ -298,5 +308,6 @@ int ttm_device_init(struct ttm_device *bdev, struct ttm_device_funcs *funcs, · 2 unmodified lines | ||
| 298 | 308 | struct drm_vma_offset_manager *vma_manager, |
| 299 | 309 | bool use_dma_alloc, bool use_dma32); |
| 300 | 310 | void ttm_device_fini(struct ttm_device *bdev); |
| 311 | +void ttm_device_clear_dma_mappings(struct ttm_device *bdev); | |
| 301 | 312 | |
| 302 | 313 | #endif |
| @@ -58,6 +58,7 @@ · 57 unmodified lines | ||
| 58 | 58 | * |
| 59 | 59 | * @fpfn: first valid page frame number to put the object |
| 60 | 60 | * @lpfn: last valid page frame number to put the object |
| 61 | + * @mem_type: One of TTM_PL_* where the resource should be allocated from. | |
| 61 | 62 | * @flags: memory domain and caching flags for the object |
| 62 | 63 | * |
| 63 | 64 | * Structure indicating a possible place to put an object. |
| @@ -37,7 +37,7 @@ struct ttm_pool; · 36 unmodified lines | ||
| 37 | 37 | struct ttm_operation_ctx; |
| 38 | 38 | |
| 39 | 39 | /** |
| 40 | − * ttm_pool_type - Pool for a certain memory type | |
| 40 | + * struct ttm_pool_type - Pool for a certain memory type | |
| 41 | 41 | * |
| 42 | 42 | * @pool: the pool we belong to, might be NULL for the global ones |
| 43 | 43 | * @order: the allocation order our pages have |
| @@ -58,8 +58,9 @@ struct ttm_pool_type { · 14 unmodified lines | ||
| 58 | 58 | }; |
| 59 | 59 | |
| 60 | 60 | /** |
| 61 | − * ttm_pool - Pool for all caching and orders | |
| 61 | + * struct ttm_pool - Pool for all caching and orders | |
| 62 | 62 | * |
| 63 | + * @dev: the device we allocate pages for | |
| 63 | 64 | * @use_dma_alloc: if coherent DMA allocations should be used |
| 64 | 65 | * @use_dma32: if GFP_DMA32 should be used |
| 65 | 66 | * @caching: pools for each caching/order |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | #define _TTM_RANGE_MANAGER_H_ |
| 5 | 5 | |
| 6 | 6 | #include <drm/ttm/ttm_resource.h> |
| 7 | +#include <drm/ttm/ttm_device.h> | |
| 7 | 8 | #include <drm/drm_mm.h> |
| 8 | 9 | |
| 9 | 10 | /** |
| @@ -33,10 +34,23 @@ to_ttm_range_mgr_node(struct ttm_resource *res) · 23 unmodified lines | ||
| 33 | 34 | return container_of(res, struct ttm_range_mgr_node, base); |
| 34 | 35 | } |
| 35 | 36 | |
| 36 | −int ttm_range_man_init(struct ttm_device *bdev, | |
| 37 | +int ttm_range_man_init_nocheck(struct ttm_device *bdev, | |
| 37 | 38 | unsigned type, bool use_tt, |
| 38 | 39 | unsigned long p_size); |
| 39 | −int ttm_range_man_fini(struct ttm_device *bdev, | |
| 40 | +int ttm_range_man_fini_nocheck(struct ttm_device *bdev, | |
| 40 | 41 | unsigned type); |
| 42 | +static __always_inline int ttm_range_man_init(struct ttm_device *bdev, | |
| 43 | + unsigned int type, bool use_tt, | |
| 44 | + unsigned long p_size) | |
| 45 | +{ | |
| 46 | + BUILD_BUG_ON(__builtin_constant_p(type) && type >= TTM_NUM_MEM_TYPES); | |
| 47 | + return ttm_range_man_init_nocheck(bdev, type, use_tt, p_size); | |
| 48 | +} | |
| 41 | 49 | |
| 50 | +static __always_inline int ttm_range_man_fini(struct ttm_device *bdev, | |
| 51 | + unsigned int type) | |
| 52 | +{ | |
| 53 | + BUILD_BUG_ON(__builtin_constant_p(type) && type >= TTM_NUM_MEM_TYPES); | |
| 54 | + return ttm_range_man_fini_nocheck(bdev, type); | |
| 55 | +} | |
| 42 | 56 | #endif |
| @@ -40,6 +40,7 @@ struct ttm_resource_manager; · 39 unmodified lines | ||
| 40 | 40 | struct ttm_resource; |
| 41 | 41 | struct ttm_place; |
| 42 | 42 | struct ttm_buffer_object; |
| 43 | +struct ttm_placement; | |
| 43 | 44 | struct dma_buf_map; |
| 44 | 45 | struct io_mapping; |
| 45 | 46 | struct sg_table; |
| @@ -102,10 +103,7 @@ struct ttm_resource_manager_func { · 56 unmodified lines | ||
| 102 | 103 | * struct ttm_resource_manager |
| 103 | 104 | * |
| 104 | 105 | * @use_type: The memory type is enabled. |
| 105 | − * @flags: TTM_MEMTYPE_XX flags identifying the traits of the memory | |
| 106 | − * managed by this memory type. | |
| 107 | − * @gpu_offset: If used, the GPU offset of the first managed page of | |
| 108 | − * fixed memory or the first managed location in an aperture. | |
| 106 | + * @use_tt: If a TT object should be used for the backing store. | |
| 109 | 107 | * @size: Size of the managed region. |
| 110 | 108 | * @func: structure pointer implementing the range manager. See above |
| 111 | 109 | * @move_lock: lock for move fence |
| @@ -143,6 +141,7 @@ struct ttm_resource_manager { · 31 unmodified lines | ||
| 143 | 141 | * @addr: mapped virtual address |
| 144 | 142 | * @offset: physical addr |
| 145 | 143 | * @is_iomem: is this io memory ? |
| 144 | + * @caching: See enum ttm_caching | |
| 146 | 145 | * |
| 147 | 146 | * Structure indicating the bus placement of an object. |
| 148 | 147 | */ |
| @@ -266,6 +265,8 @@ int ttm_resource_alloc(struct ttm_buffer_object *bo, · 117 unmodified lines | ||
| 266 | 265 | const struct ttm_place *place, |
| 267 | 266 | struct ttm_resource **res); |
| 268 | 267 | void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res); |
| 268 | +bool ttm_resource_compat(struct ttm_resource *res, | |
| 269 | + struct ttm_placement *placement); | |
| 269 | 270 | |
| 270 | 271 | void ttm_resource_manager_init(struct ttm_resource_manager *man, |
| 271 | 272 | unsigned long p_size); |
| @@ -55,7 +55,7 @@ struct ttm_operation_ctx; · 54 unmodified lines | ||
| 55 | 55 | * @dma_address: The DMA (bus) addresses of the pages |
| 56 | 56 | * @swap_storage: Pointer to shmem struct file for swap storage. |
| 57 | 57 | * @pages_list: used by some page allocation backend |
| 58 | − * @caching: The current caching state of the pages. | |
| 58 | + * @caching: The current caching state of the pages, see enum ttm_caching. | |
| 59 | 59 | * |
| 60 | 60 | * This is a structure holding the pages, caching- and aperture binding |
| 61 | 61 | * status for a buffer object that isn't backed by fixed (VRAM / AGP) |
| @@ -127,21 +127,15 @@ int ttm_sg_tt_init(struct ttm_tt *ttm_dma, struct ttm_buffer_object *bo, · 65 unmodified lines | ||
| 127 | 127 | void ttm_tt_fini(struct ttm_tt *ttm); |
| 128 | 128 | |
| 129 | 129 | /** |
| 130 | − * ttm_ttm_destroy: | |
| 130 | + * ttm_tt_destroy: | |
| 131 | 131 | * |
| 132 | + * @bdev: the ttm_device this object belongs to | |
| 132 | 133 | * @ttm: The struct ttm_tt. |
| 133 | 134 | * |
| 134 | 135 | * Unbind, unpopulate and destroy common struct ttm_tt. |
| 135 | 136 | */ |
| 136 | 137 | void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm); |
| 137 | 138 | |
| 138 | −/** | |
| 139 | − * ttm_tt_destroy_common: | |
| 140 | − * | |
| 141 | − * Called from driver to destroy common path. | |
| 142 | − */ | |
| 143 | −void ttm_tt_destroy_common(struct ttm_device *bdev, struct ttm_tt *ttm); | |
| 144 | − | |
| 145 | 139 | /** |
| 146 | 140 | * ttm_tt_swapin: |
| 147 | 141 | * |
| @@ -156,15 +150,19 @@ int ttm_tt_swapout(struct ttm_device *bdev, struct ttm_tt *ttm, · 8 unmodified lines | ||
| 156 | 150 | /** |
| 157 | 151 | * ttm_tt_populate - allocate pages for a ttm |
| 158 | 152 | * |
| 153 | + * @bdev: the ttm_device this object belongs to | |
| 159 | 154 | * @ttm: Pointer to the ttm_tt structure |
| 155 | + * @ctx: operation context for populating the tt object. | |
| 160 | 156 | * |
| 161 | 157 | * Calls the driver method to allocate pages for a ttm |
| 162 | 158 | */ |
| 163 | −int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, struct ttm_operation_ctx *ctx); | |
| 159 | +int ttm_tt_populate(struct ttm_device *bdev, struct ttm_tt *ttm, | |
| 160 | + struct ttm_operation_ctx *ctx); | |
| 164 | 161 | |
| 165 | 162 | /** |
| 166 | 163 | * ttm_tt_unpopulate - free pages from a ttm |
| 167 | 164 | * |
| 165 | + * @bdev: the ttm_device this object belongs to | |
| 168 | 166 | * @ttm: Pointer to the ttm_tt structure |
| 169 | 167 | * |
| 170 | 168 | * Calls the driver method to free all pages from a ttm |
| @@ -420,6 +420,13 @@ struct dma_buf { · 419 unmodified lines | ||
| 420 | 420 | * - Dynamic importers should set fences for any access that they can't |
| 421 | 421 | * disable immediately from their &dma_buf_attach_ops.move_notify |
| 422 | 422 | * callback. |
| 423 | + * | |
| 424 | + * IMPORTANT: | |
| 425 | + * | |
| 426 | + * All drivers must obey the struct dma_resv rules, specifically the | |
| 427 | + * rules for updating fences, see &dma_resv.fence_excl and | |
| 428 | + * &dma_resv.fence. If these dependency rules are broken access tracking | |
| 429 | + * can be lost resulting in use after free issues. | |
| 423 | 430 | */ |
| 424 | 431 | struct dma_resv *resv; |
| 425 | 432 | |
| @@ -214,19 +214,15 @@ struct dma_fence_ops { · 213 unmodified lines | ||
| 214 | 214 | * Custom wait implementation, defaults to dma_fence_default_wait() if |
| 215 | 215 | * not set. |
| 216 | 216 | * |
| 217 | − * The dma_fence_default_wait implementation should work for any fence, as long | |
| 218 | − * as @enable_signaling works correctly. This hook allows drivers to | |
| 219 | − * have an optimized version for the case where a process context is | |
| 220 | − * already available, e.g. if @enable_signaling for the general case | |
| 221 | − * needs to set up a worker thread. | |
| 217 | + * Deprecated and should not be used by new implementations. Only used | |
| 218 | + * by existing implementations which need special handling for their | |
| 219 | + * hardware reset procedure. | |
| 222 | 220 | * |
| 223 | 221 | * Must return -ERESTARTSYS if the wait is intr = true and the wait was |
| 224 | 222 | * interrupted, and remaining jiffies if fence has signaled, or 0 if wait |
| 225 | 223 | * timed out. Can also return other error values on custom implementations, |
| 226 | 224 | * which should be treated as if the fence is signaled. For example a hardware |
| 227 | 225 | * lockup could be reported like that. |
| 228 | − * | |
| 229 | − * This callback is optional. | |
| 230 | 226 | */ |
| 231 | 227 | signed long (*wait)(struct dma_fence *fence, |
| 232 | 228 | bool intr, signed long timeout); |
| @@ -590,26 +586,4 @@ struct dma_fence *dma_fence_get_stub(void); · 357 unmodified lines | ||
| 590 | 586 | struct dma_fence *dma_fence_allocate_private_stub(void); |
| 591 | 587 | u64 dma_fence_context_alloc(unsigned num); |
| 592 | 588 | |
| 593 | −#define DMA_FENCE_TRACE(f, fmt, args...) \ | |
| 594 | − do { \ | |
| 595 | − struct dma_fence *__ff = (f); \ | |
| 596 | − if (IS_ENABLED(CONFIG_DMA_FENCE_TRACE)) \ | |
| 597 | − pr_info("f %llu#%llu: " fmt, \ | |
| 598 | − __ff->context, __ff->seqno, ##args); \ | |
| 599 | − } while (0) | |
| 600 | − | |
| 601 | −#define DMA_FENCE_WARN(f, fmt, args...) \ | |
| 602 | − do { \ | |
| 603 | − struct dma_fence *__ff = (f); \ | |
| 604 | − pr_warn("f %llu#%llu: " fmt, __ff->context, __ff->seqno,\ | |
| 605 | − ##args); \ | |
| 606 | − } while (0) | |
| 607 | − | |
| 608 | −#define DMA_FENCE_ERR(f, fmt, args...) \ | |
| 609 | − do { \ | |
| 610 | − struct dma_fence *__ff = (f); \ | |
| 611 | − pr_err("f %llu#%llu: " fmt, __ff->context, __ff->seqno, \ | |
| 612 | − ##args); \ | |
| 613 | − } while (0) | |
| 614 | − | |
| 615 | 589 | #endif /* __LINUX_DMA_FENCE_H */ |
| @@ -62,16 +62,90 @@ struct dma_resv_list { · 61 unmodified lines | ||
| 62 | 62 | |
| 63 | 63 | /** |
| 64 | 64 | * struct dma_resv - a reservation object manages fences for a buffer |
| 65 | − * @lock: update side lock | |
| 66 | − * @seq: sequence count for managing RCU read-side synchronization | |
| 67 | − * @fence_excl: the exclusive fence, if there is one currently | |
| 68 | − * @fence: list of current shared fences | |
| 65 | + * | |
| 66 | + * There are multiple uses for this, with sometimes slightly different rules in | |
| 67 | + * how the fence slots are used. | |
| 68 | + * | |
| 69 | + * One use is to synchronize cross-driver access to a struct dma_buf, either for | |
| 70 | + * dynamic buffer management or just to handle implicit synchronization between | |
| 71 | + * different users of the buffer in userspace. See &dma_buf.resv for a more | |
| 72 | + * in-depth discussion. | |
| 73 | + * | |
| 74 | + * The other major use is to manage access and locking within a driver in a | |
| 75 | + * buffer based memory manager. struct ttm_buffer_object is the canonical | |
| 76 | + * example here, since this is where reservation objects originated from. But | |
| 77 | + * use in drivers is spreading and some drivers also manage struct | |
| 78 | + * drm_gem_object with the same scheme. | |
| 69 | 79 | */ |
| 70 | 80 | struct dma_resv { |
| 81 | + /** | |
| 82 | + * @lock: | |
| 83 | + * | |
| 84 | + * Update side lock. Don't use directly, instead use the wrapper | |
| 85 | + * functions like dma_resv_lock() and dma_resv_unlock(). | |
| 86 | + * | |
| 87 | + * Drivers which use the reservation object to manage memory dynamically | |
| 88 | + * also use this lock to protect buffer object state like placement, | |
| 89 | + * allocation policies or throughout command submission. | |
| 90 | + */ | |
| 71 | 91 | struct ww_mutex lock; |
| 92 | + | |
| 93 | + /** | |
| 94 | + * @seq: | |
| 95 | + * | |
| 96 | + * Sequence count for managing RCU read-side synchronization, allows | |
| 97 | + * read-only access to @fence_excl and @fence while ensuring we take a | |
| 98 | + * consistent snapshot. | |
| 99 | + */ | |
| 72 | 100 | seqcount_ww_mutex_t seq; |
| 73 | 101 | |
| 102 | + /** | |
| 103 | + * @fence_excl: | |
| 104 | + * | |
| 105 | + * The exclusive fence, if there is one currently. | |
| 106 | + * | |
| 107 | + * There are two ways to update this fence: | |
| 108 | + * | |
| 109 | + * - First by calling dma_resv_add_excl_fence(), which replaces all | |
| 110 | + * fences attached to the reservation object. To guarantee that no | |
| 111 | + * fences are lost, this new fence must signal only after all previous | |
| 112 | + * fences, both shared and exclusive, have signalled. In some cases it | |
| 113 | + * is convenient to achieve that by attaching a struct dma_fence_array | |
| 114 | + * with all the new and old fences. | |
| 115 | + * | |
| 116 | + * - Alternatively the fence can be set directly, which leaves the | |
| 117 | + * shared fences unchanged. To guarantee that no fences are lost, this | |
| 118 | + * new fence must signal only after the previous exclusive fence has | |
| 119 | + * signalled. Since the shared fences are staying intact, it is not | |
| 120 | + * necessary to maintain any ordering against those. If semantically | |
| 121 | + * only a new access is added without actually treating the previous | |
| 122 | + * one as a dependency the exclusive fences can be strung together | |
| 123 | + * using struct dma_fence_chain. | |
| 124 | + * | |
| 125 | + * Note that actual semantics of what an exclusive or shared fence mean | |
| 126 | + * is defined by the user, for reservation objects shared across drivers | |
| 127 | + * see &dma_buf.resv. | |
| 128 | + */ | |
| 74 | 129 | struct dma_fence __rcu *fence_excl; |
| 130 | + | |
| 131 | + /** | |
| 132 | + * @fence: | |
| 133 | + * | |
| 134 | + * List of current shared fences. | |
| 135 | + * | |
| 136 | + * There are no ordering constraints of shared fences against the | |
| 137 | + * exclusive fence slot. If a waiter needs to wait for all access, it | |
| 138 | + * has to wait for both sets of fences to signal. | |
| 139 | + * | |
| 140 | + * A new fence is added by calling dma_resv_add_shared_fence(). Since | |
| 141 | + * this often needs to be done past the point of no return in command | |
| 142 | + * submission it cannot fail, and therefore sufficient slots need to be | |
| 143 | + * reserved by calling dma_resv_reserve_shared(). | |
| 144 | + * | |
| 145 | + * Note that actual semantics of what an exclusive or shared fence mean | |
| 146 | + * is defined by the user, for reservation objects shared across drivers | |
| 147 | + * see &dma_buf.resv. | |
| 148 | + */ | |
| 75 | 149 | struct dma_resv_list __rcu *fence; |
| 76 | 150 | }; |
| 77 | 151 | |
| @@ -98,6 +172,13 @@ static inline void dma_resv_reset_shared_max(struct dma_resv *obj) {} · 20 unmodified lines | ||
| 98 | 172 | * undefined order, a #ww_acquire_ctx is passed to unwind if a cycle |
| 99 | 173 | * is detected. See ww_mutex_lock() and ww_acquire_init(). A reservation |
| 100 | 174 | * object may be locked by itself by passing NULL as @ctx. |
| 175 | + * | |
| 176 | + * When a die situation is indicated by returning -EDEADLK all locks held by | |
| 177 | + * @ctx must be unlocked and then dma_resv_lock_slow() called on @obj. | |
| 178 | + * | |
| 179 | + * Unlocked by calling dma_resv_unlock(). | |
| 180 | + * | |
| 181 | + * See also dma_resv_lock_interruptible() for the interruptible variant. | |
| 101 | 182 | */ |
| 102 | 183 | static inline int dma_resv_lock(struct dma_resv *obj, |
| 103 | 184 | struct ww_acquire_ctx *ctx) |
| @@ -119,6 +200,12 @@ static inline int dma_resv_lock(struct dma_resv *obj, · 15 unmodified lines | ||
| 119 | 200 | * undefined order, a #ww_acquire_ctx is passed to unwind if a cycle |
| 120 | 201 | * is detected. See ww_mutex_lock() and ww_acquire_init(). A reservation |
| 121 | 202 | * object may be locked by itself by passing NULL as @ctx. |
| 203 | + * | |
| 204 | + * When a die situation is indicated by returning -EDEADLK all locks held by | |
| 205 | + * @ctx must be unlocked and then dma_resv_lock_slow_interruptible() called on | |
| 206 | + * @obj. | |
| 207 | + * | |
| 208 | + * Unlocked by calling dma_resv_unlock(). | |
| 122 | 209 | */ |
| 123 | 210 | static inline int dma_resv_lock_interruptible(struct dma_resv *obj, |
| 124 | 211 | struct ww_acquire_ctx *ctx) |
| @@ -134,6 +221,8 @@ static inline int dma_resv_lock_interruptible(struct dma_resv *obj, · 9 unmodified lines | ||
| 134 | 221 | * Acquires the reservation object after a die case. This function |
| 135 | 222 | * will sleep until the lock becomes available. See dma_resv_lock() as |
| 136 | 223 | * well. |
| 224 | + * | |
| 225 | + * See also dma_resv_lock_slow_interruptible() for the interruptible variant. | |
| 137 | 226 | */ |
| 138 | 227 | static inline void dma_resv_lock_slow(struct dma_resv *obj, |
| 139 | 228 | struct ww_acquire_ctx *ctx) |
| @@ -167,7 +256,7 @@ static inline int dma_resv_lock_slow_interruptible(struct dma_resv *obj, · 27 unmodified lines | ||
| 167 | 256 | * if they overlap with a writer. |
| 168 | 257 | * |
| 169 | 258 | * Also note that since no context is provided, no deadlock protection is |
| 170 | − * possible. | |
| 259 | + * possible, which is also not needed for a trylock. | |
| 171 | 260 | * |
| 172 | 261 | * Returns true if the lock was acquired, false otherwise. |
| 173 | 262 | */ |
| @@ -193,6 +282,11 @@ static inline bool dma_resv_is_locked(struct dma_resv *obj) · 19 unmodified lines | ||
| 193 | 282 | * |
| 194 | 283 | * Returns the context used to lock a reservation object or NULL if no context |
| 195 | 284 | * was used or the object is not locked at all. |
| 285 | + * | |
| 286 | + * WARNING: This interface is pretty horrible, but TTM needs it because it | |
| 287 | + * doesn't pass the struct ww_acquire_ctx around in some very long callchains. | |
| 288 | + * Everyone else just uses it to check whether they're holding a reservation or | |
| 289 | + * not. | |
| 196 | 290 | */ |
| 197 | 291 | static inline struct ww_acquire_ctx *dma_resv_locking_ctx(struct dma_resv *obj) |
| 198 | 292 | { |
| @@ -1,109 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0-only */ | |
| 2 | −/* | |
| 3 | − * seqno-fence, using a dma-buf to synchronize fencing | |
| 4 | − * | |
| 5 | − * Copyright (C) 2012 Texas Instruments | |
| 6 | − * Copyright (C) 2012 Canonical Ltd | |
| 7 | − * Authors: | |
| 8 | − * Rob Clark <robdclark@gmail.com> | |
| 9 | − * Maarten Lankhorst <maarten.lankhorst@canonical.com> | |
| 10 | − */ | |
| 11 | − | |
| 12 | −#ifndef __LINUX_SEQNO_FENCE_H | |
| 13 | −#define __LINUX_SEQNO_FENCE_H | |
| 14 | − | |
| 15 | −#include <linux/dma-fence.h> | |
| 16 | −#include <linux/dma-buf.h> | |
| 17 | − | |
| 18 | −enum seqno_fence_condition { | |
| 19 | − SEQNO_FENCE_WAIT_GEQUAL, | |
| 20 | − SEQNO_FENCE_WAIT_NONZERO | |
| 21 | −}; | |
| 22 | − | |
| 23 | −struct seqno_fence { | |
| 24 | − struct dma_fence base; | |
| 25 | − | |
| 26 | − const struct dma_fence_ops *ops; | |
| 27 | − struct dma_buf *sync_buf; | |
| 28 | − uint32_t seqno_ofs; | |
| 29 | − enum seqno_fence_condition condition; | |
| 30 | −}; | |
| 31 | − | |
| 32 | −extern const struct dma_fence_ops seqno_fence_ops; | |
| 33 | − | |
| 34 | −/** | |
| 35 | − * to_seqno_fence - cast a fence to a seqno_fence | |
| 36 | − * @fence: fence to cast to a seqno_fence | |
| 37 | − * | |
| 38 | − * Returns NULL if the fence is not a seqno_fence, | |
| 39 | − * or the seqno_fence otherwise. | |
| 40 | − */ | |
| 41 | −static inline struct seqno_fence * | |
| 42 | −to_seqno_fence(struct dma_fence *fence) | |
| 43 | −{ | |
| 44 | − if (fence->ops != &seqno_fence_ops) | |
| 45 | − return NULL; | |
| 46 | − return container_of(fence, struct seqno_fence, base); | |
| 47 | −} | |
| 48 | − | |
| 49 | −/** | |
| 50 | − * seqno_fence_init - initialize a seqno fence | |
| 51 | − * @fence: seqno_fence to initialize | |
| 52 | − * @lock: pointer to spinlock to use for fence | |
| 53 | − * @sync_buf: buffer containing the memory location to signal on | |
| 54 | − * @context: the execution context this fence is a part of | |
| 55 | − * @seqno_ofs: the offset within @sync_buf | |
| 56 | − * @seqno: the sequence # to signal on | |
| 57 | − * @cond: fence wait condition | |
| 58 | − * @ops: the fence_ops for operations on this seqno fence | |
| 59 | − * | |
| 60 | − * This function initializes a struct seqno_fence with passed parameters, | |
| 61 | − * and takes a reference on sync_buf which is released on fence destruction. | |
| 62 | − * | |
| 63 | − * A seqno_fence is a dma_fence which can complete in software when | |
| 64 | − * enable_signaling is called, but it also completes when | |
| 65 | − * (s32)((sync_buf)[seqno_ofs] - seqno) >= 0 is true | |
| 66 | − * | |
| 67 | − * The seqno_fence will take a refcount on the sync_buf until it's | |
| 68 | − * destroyed, but actual lifetime of sync_buf may be longer if one of the | |
| 69 | − * callers take a reference to it. | |
| 70 | − * | |
| 71 | − * Certain hardware have instructions to insert this type of wait condition | |
| 72 | − * in the command stream, so no intervention from software would be needed. | |
| 73 | − * This type of fence can be destroyed before completed, however a reference | |
| 74 | − * on the sync_buf dma-buf can be taken. It is encouraged to re-use the same | |
| 75 | − * dma-buf for sync_buf, since mapping or unmapping the sync_buf to the | |
| 76 | − * device's vm can be expensive. | |
| 77 | − * | |
| 78 | − * It is recommended for creators of seqno_fence to call dma_fence_signal() | |
| 79 | − * before destruction. This will prevent possible issues from wraparound at | |
| 80 | − * time of issue vs time of check, since users can check dma_fence_is_signaled() | |
| 81 | − * before submitting instructions for the hardware to wait on the fence. | |
| 82 | − * However, when ops.enable_signaling is not called, it doesn't have to be | |
| 83 | − * done as soon as possible, just before there's any real danger of seqno | |
| 84 | − * wraparound. | |
| 85 | − */ | |
| 86 | −static inline void | |
| 87 | −seqno_fence_init(struct seqno_fence *fence, spinlock_t *lock, | |
| 88 | − struct dma_buf *sync_buf, uint32_t context, | |
| 89 | − uint32_t seqno_ofs, uint32_t seqno, | |
| 90 | − enum seqno_fence_condition cond, | |
| 91 | − const struct dma_fence_ops *ops) | |
| 92 | −{ | |
| 93 | − BUG_ON(!fence || !sync_buf || !ops); | |
| 94 | − BUG_ON(!ops->wait || !ops->enable_signaling || | |
| 95 | − !ops->get_driver_name || !ops->get_timeline_name); | |
| 96 | − | |
| 97 | − /* | |
| 98 | − * ops is used in dma_fence_init for get_driver_name, so needs to be | |
| 99 | − * initialized first | |
| 100 | − */ | |
| 101 | − fence->ops = ops; | |
| 102 | − dma_fence_init(&fence->base, &seqno_fence_ops, lock, context, seqno); | |
| 103 | − get_dma_buf(sync_buf); | |
| 104 | − fence->sync_buf = sync_buf; | |
| 105 | − fence->seqno_ofs = seqno_ofs; | |
| 106 | − fence->condition = cond; | |
| 107 | −} | |
| 108 | − | |
| 109 | −#endif /* __LINUX_SEQNO_FENCE_H */ | |
| @@ -93,4 +93,5 @@ extern void register_shrinker_prepared(struct shrinker *shrinker); · 92 unmodified lines | ||
| 93 | 93 | extern int register_shrinker(struct shrinker *shrinker); |
| 94 | 94 | extern void unregister_shrinker(struct shrinker *shrinker); |
| 95 | 95 | extern void free_prealloced_shrinker(struct shrinker *shrinker); |
| 96 | +extern void synchronize_shrinkers(void); | |
| 96 | 97 | #endif |
| @@ -373,6 +373,12 @@ extern "C" { · 372 unmodified lines | ||
| 373 | 373 | |
| 374 | 374 | #define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) |
| 375 | 375 | |
| 376 | +#define fourcc_mod_get_vendor(modifier) \ | |
| 377 | + (((modifier) >> 56) & 0xff) | |
| 378 | + | |
| 379 | +#define fourcc_mod_is_vendor(modifier, vendor) \ | |
| 380 | + (fourcc_mod_get_vendor(modifier) == DRM_FORMAT_MOD_VENDOR_## vendor) | |
| 381 | + | |
| 376 | 382 | #define fourcc_mod_code(vendor, val) \ |
| 377 | 383 | ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) |
| 378 | 384 | |
| @@ -1110,6 +1110,9 @@ struct drm_mode_destroy_blob { · 1109 unmodified lines | ||
| 1110 | 1110 | * struct drm_mode_create_lease - Create lease |
| 1111 | 1111 | * |
| 1112 | 1112 | * Lease mode resources, creating another drm_master. |
| 1113 | + * | |
| 1114 | + * The @object_ids array must reference at least one CRTC, one connector and | |
| 1115 | + * one plane if &DRM_CLIENT_CAP_UNIVERSAL_PLANES is enabled. | |
| 1113 | 1116 | */ |
| 1114 | 1117 | struct drm_mode_create_lease { |
| 1115 | 1118 | /** @object_ids: Pointer to array of object ids (__u32) */ |
| @@ -687,6 +687,21 @@ void unregister_shrinker(struct shrinker *shrinker) · 686 unmodified lines | ||
| 687 | 687 | } |
| 688 | 688 | EXPORT_SYMBOL(unregister_shrinker); |
| 689 | 689 | |
| 690 | +/** | |
| 691 | + * synchronize_shrinkers - Wait for all running shrinkers to complete. | |
| 692 | + * | |
| 693 | + * This is equivalent to calling unregister_shrink() and register_shrinker(), | |
| 694 | + * but atomically and with less overhead. This is useful to guarantee that all | |
| 695 | + * shrinker invocations have seen an update, before freeing memory, similar to | |
| 696 | + * rcu. | |
| 697 | + */ | |
| 698 | +void synchronize_shrinkers(void) | |
| 699 | +{ | |
| 700 | + down_write(&shrinker_rwsem); | |
| 701 | + up_write(&shrinker_rwsem); | |
| 702 | +} | |
| 703 | +EXPORT_SYMBOL(synchronize_shrinkers); | |
| 704 | + | |
| 690 | 705 | #define SHRINK_BATCH 128 |
| 691 | 706 | |
| 692 | 707 | static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, |