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 | items: | 22 | items: |
| 23 | - enum: | 23 | - enum: |
| 24 | # ili9341 240*320 Color on stm32f429-disco board | 24 | # ili9341 240*320 Color on stm32f429-disco board |
| 25 | - st,sf-tc240t-9370-t | 25 | - st,sf-tc240t-9370-t |
| 26 | - const: ilitek,ili9341 | 26 | - const: ilitek,ili9341 |
| 27 | 27 | ||
| 28 | reg: true | 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 | .. kernel-doc:: include/linux/dma-fence.h | 176 | .. kernel-doc:: include/linux/dma-fence.h |
| 177 | :internal: | 177 | :internal: |
| 178 | 178 | ||
| 179 | Seqno Hardware Fences | ||
| 180 | ~~~~~~~~~~~~~~~~~~~~~ | ||
| 181 | |||
| 182 | .. kernel-doc:: include/linux/seqno-fence.h | ||
| 183 | :internal: | ||
| 184 | |||
| 185 | DMA Fence Array | 179 | DMA Fence Array |
| 186 | ~~~~~~~~~~~~~~~ | 180 | ~~~~~~~~~~~~~~~ |
| 187 | 181 | ||
| @@ -28,56 +28,53 @@ UMA devices. · 27 unmodified lines | |||
| 28 | The Translation Table Manager (TTM) | 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 | .. kernel-doc:: include/drm/ttm/ttm_caching.h |
| 34 | ------------------ | 35 | :internal: |
| 35 | 36 | ||
| 36 | **Warning** | 37 | TTM device object reference |
| 37 | This section is outdated. | 38 | --------------------------- |
| 38 | 39 | ||
| 39 | Drivers wishing to support TTM must pass a filled :c:type:`ttm_bo_driver | 40 | .. kernel-doc:: include/drm/ttm/ttm_device.h |
| 40 | <ttm_bo_driver>` structure to ttm_device_init, together with an | 41 | :internal: |
| 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. | ||
| 45 | 42 | ||
| 46 | The :c:type:`struct drm_global_reference <drm_global_reference>` is made | 43 | .. kernel-doc:: drivers/gpu/drm/ttm/ttm_device.c |
| 47 | up of several fields: | 44 | :export: |
| 48 | 45 | ||
| 49 | .. code-block:: c | 46 | TTM resource placement reference |
| 47 | -------------------------------- | ||
| 50 | 48 | ||
| 51 | struct drm_global_reference { | 49 | .. kernel-doc:: include/drm/ttm/ttm_placement.h |
| 52 | enum ttm_global_types global_type; | 50 | :internal: |
| 53 | size_t size; | 51 | |
| 54 | void *object; | 52 | TTM resource object reference |
| 55 | int (*init) (struct drm_global_reference *); | 53 | ----------------------------- |
| 56 | void (*release) (struct drm_global_reference *); | 54 | |
| 57 | }; | 55 | .. kernel-doc:: include/drm/ttm/ttm_resource.h |
| 58 | 56 | :internal: | |
| 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. | ||
| 68 | 57 | ||
| 69 | Once your global TTM accounting structure is set up and initialized by | 58 | .. kernel-doc:: drivers/gpu/drm/ttm/ttm_resource.c |
| 70 | calling ttm_global_item_ref() on it, you need to create a buffer | 59 | :export: |
| 71 | object TTM to provide a pool for buffer object allocation by clients and | 60 | |
| 72 | the kernel itself. The type of this object should be | 61 | TTM TT object reference |
| 73 | TTM_GLOBAL_TTM_BO, and its size should be sizeof(struct | 62 | ----------------------- |
| 74 | ttm_bo_global). Again, driver-specific init and release functions may | 63 | |
| 75 | be provided, likely eventually calling ttm_bo_global_ref_init() and | 64 | .. kernel-doc:: include/drm/ttm/ttm_tt.h |
| 76 | ttm_bo_global_ref_release(), respectively. Also, like the previous | 65 | :internal: |
| 77 | object, ttm_global_item_ref() is used to create an initial reference | 66 | |
| 78 | count for the TTM, which will call your initialization function. | 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 | The Graphics Execution Manager (GEM) | 79 | The Graphics Execution Manager (GEM) |
| 83 | ==================================== | 80 | ==================================== |
| @@ -504,3 +501,6 @@ Scheduler Function References · 420 unmodified lines | |||
| 504 | 501 | ||
| 505 | .. kernel-doc:: drivers/gpu/drm/scheduler/sched_main.c | 502 | .. kernel-doc:: drivers/gpu/drm/scheduler/sched_main.c |
| 506 | :export: | 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 | F: Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml | 6018 | F: Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml |
| 6019 | F: drivers/gpu/drm/panel/panel-samsung-db7430.c | 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 | DRM DRIVER FOR SITRONIX ST7703 PANELS | 6027 | DRM DRIVER FOR SITRONIX ST7703 PANELS |
| 6022 | M: Guido Günther <agx@sigxcpu.org> | 6028 | M: Guido Günther <agx@sigxcpu.org> |
| 6023 | R: Purism Kernel Team <kernel@puri.sm> | 6029 | R: Purism Kernel Team <kernel@puri.sm> |
| @@ -1,6 +1,6 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \ | 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 | obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o | 4 | obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o |
| 5 | obj-$(CONFIG_DMABUF_HEAPS) += heaps/ | 5 | obj-$(CONFIG_DMABUF_HEAPS) += heaps/ |
| 6 | obj-$(CONFIG_SYNC_FILE) += sync_file.o | 6 | obj-$(CONFIG_SYNC_FILE) += sync_file.o |
| @@ -82,6 +82,7 @@ static void dma_buf_release(struct dentry *dentry) · 81 unmodified lines | |||
| 82 | if (dmabuf->resv == (struct dma_resv *)&dmabuf[1]) | 82 | if (dmabuf->resv == (struct dma_resv *)&dmabuf[1]) |
| 83 | dma_resv_fini(dmabuf->resv); | 83 | dma_resv_fini(dmabuf->resv); |
| 84 | 84 | ||
| 85 | WARN_ON(!list_empty(&dmabuf->attachments)); | ||
| 85 | module_put(dmabuf->owner); | 86 | module_put(dmabuf->owner); |
| 86 | kfree(dmabuf->name); | 87 | kfree(dmabuf->name); |
| 87 | kfree(dmabuf); | 88 | kfree(dmabuf); |
| @@ -616,20 +616,17 @@ EXPORT_SYMBOL(dma_fence_enable_sw_signaling); · 615 unmodified lines | |||
| 616 | * @cb: the callback to register | 616 | * @cb: the callback to register |
| 617 | * @func: the function to call | 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 | * @cb will be initialized by dma_fence_add_callback(), no initialization | 622 | * @cb will be initialized by dma_fence_add_callback(), no initialization |
| 620 | * by the caller is required. Any number of callbacks can be registered | 623 | * by the caller is required. Any number of callbacks can be registered |
| 621 | * to a fence, but a callback can only be registered to one fence at a time. | 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 | 626 | * If fence is already signaled, this function will return -ENOENT (and |
| 624 | * fence is already signaled, this function will return -ENOENT (and | ||
| 625 | * *not* call the callback). | 627 | * *not* call the callback). |
| 626 | * | 628 | * |
| 627 | * Add a software callback to the fence. Same restrictions apply to | 629 | * Note that the callback can be called from an atomic context or irq context. |
| 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. | ||
| 633 | * | 630 | * |
| 634 | * Returns 0 in case of success, -ENOENT if the fence is already signaled | 631 | * Returns 0 in case of success, -ENOENT if the fence is already signaled |
| 635 | * and -EINVAL in case of error. | 632 | * and -EINVAL in case of error. |
| @@ -48,6 +48,8 @@ · 47 unmodified lines | |||
| 48 | * write operations) or N shared fences (read operations). The RCU | 48 | * write operations) or N shared fences (read operations). The RCU |
| 49 | * mechanism is used to protect read access to fences from locked | 49 | * mechanism is used to protect read access to fences from locked |
| 50 | * write-side updates. | 50 | * write-side updates. |
| 51 | * | ||
| 52 | * See struct dma_resv for more details. | ||
| 51 | */ | 53 | */ |
| 52 | 54 | ||
| 53 | DEFINE_WD_CLASS(reservation_ww_class); | 55 | DEFINE_WD_CLASS(reservation_ww_class); |
| @@ -137,7 +139,11 @@ EXPORT_SYMBOL(dma_resv_fini); · 83 unmodified lines | |||
| 137 | * @num_fences: number of fences we want to add | 139 | * @num_fences: number of fences we want to add |
| 138 | * | 140 | * |
| 139 | * Should be called before dma_resv_add_shared_fence(). Must | 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 | * RETURNS | 148 | * RETURNS |
| 143 | * Zero for success, or -errno | 149 | * Zero for success, or -errno |
| @@ -234,8 +240,10 @@ EXPORT_SYMBOL(dma_resv_reset_shared_max); · 90 unmodified lines | |||
| 234 | * @obj: the reservation object | 240 | * @obj: the reservation object |
| 235 | * @fence: the shared fence to add | 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 | * dma_resv_reserve_shared() has been called. | 244 | * dma_resv_reserve_shared() has been called. |
| 245 | * | ||
| 246 | * See also &dma_resv.fence for a discussion of the semantics. | ||
| 239 | */ | 247 | */ |
| 240 | void dma_resv_add_shared_fence(struct dma_resv *obj, struct dma_fence *fence) | 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 | * dma_resv_add_excl_fence - Add an exclusive fence. | 287 | * dma_resv_add_excl_fence - Add an exclusive fence. |
| 280 | * @obj: the reservation object | 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 | void dma_resv_add_excl_fence(struct dma_resv *obj, struct dma_fence *fence) | 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 | * fence | 619 | * fence |
| 610 | * | 620 | * |
| 611 | * Callers are not required to hold specific locks, but maybe hold | 621 | * Callers are not required to hold specific locks, but maybe hold |
| 612 | * dma_resv_lock() already | 622 | * dma_resv_lock() already. |
| 623 | * | ||
| 613 | * RETURNS | 624 | * RETURNS |
| 614 | * true if all fences signaled, else false | 625 | * |
| 626 | * True if all fences signaled, else false. | ||
| 615 | */ | 627 | */ |
| 616 | bool dma_resv_test_signaled(struct dma_resv *obj, bool test_all) | 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 | bool mapped; | 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 | #define HIGH_ORDER_GFP (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \ | 45 | #define HIGH_ORDER_GFP (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \ |
| 44 | | __GFP_NORETRY) & ~__GFP_RECLAIM) \ | 46 | | __GFP_NORETRY) & ~__GFP_RECLAIM) \ |
| 45 | | __GFP_COMP) | 47 | | __GFP_COMP) |
| 46 | #define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO | __GFP_COMP) | 48 | static gfp_t order_flags[] = {HIGH_ORDER_GFP, MID_ORDER_GFP, LOW_ORDER_GFP}; |
| 47 | static gfp_t order_flags[] = {HIGH_ORDER_GFP, LOW_ORDER_GFP, LOW_ORDER_GFP}; | ||
| 48 | /* | 49 | /* |
| 49 | * The selection of the orders used for allocation (1MB, 64K, 4K) is designed | 50 | * The selection of the orders used for allocation (1MB, 64K, 4K) is designed |
| 50 | * to match with the sizes often found in IOMMUs. Using order 4 pages instead | 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 | config DRM_GEM_SHMEM_HELPER | 212 | config DRM_GEM_SHMEM_HELPER |
| 213 | bool | 213 | bool |
| 214 | depends on DRM | 214 | depends on DRM && MMU |
| 215 | help | 215 | help |
| 216 | Choose this if you need the GEM shmem helper functions | 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 | config DRM_VGEM | 272 | config DRM_VGEM |
| 273 | tristate "Virtual GEM provider" | 273 | tristate "Virtual GEM provider" |
| 274 | depends on DRM | 274 | depends on DRM && MMU |
| 275 | select DRM_GEM_SHMEM_HELPER | ||
| 275 | help | 276 | help |
| 276 | Choose this option to get a virtual graphics memory manager, | 277 | Choose this option to get a virtual graphics memory manager, |
| 277 | as used by Mesa's software renderer for enhanced performance. | 278 | as used by Mesa's software renderer for enhanced performance. |
| @@ -279,7 +280,7 @@ config DRM_VGEM · 1 unmodified line | |||
| 279 | 280 | ||
| 280 | config DRM_VKMS | 281 | config DRM_VKMS |
| 281 | tristate "Virtual KMS (EXPERIMENTAL)" | 282 | tristate "Virtual KMS (EXPERIMENTAL)" |
| 282 | depends on DRM | 283 | depends on DRM && MMU |
| 283 | select DRM_KMS_HELPER | 284 | select DRM_KMS_HELPER |
| 284 | select DRM_GEM_SHMEM_HELPER | 285 | select DRM_GEM_SHMEM_HELPER |
| 285 | select CRC32 | 286 | select CRC32 |
| @@ -351,8 +352,6 @@ source "drivers/gpu/drm/hisilicon/Kconfig" · 65 unmodified lines | |||
| 351 | 352 | ||
| 352 | source "drivers/gpu/drm/mediatek/Kconfig" | 353 | source "drivers/gpu/drm/mediatek/Kconfig" |
| 353 | 354 | ||
| 354 | source "drivers/gpu/drm/zte/Kconfig" | ||
| 355 | |||
| 356 | source "drivers/gpu/drm/mxsfb/Kconfig" | 355 | source "drivers/gpu/drm/mxsfb/Kconfig" |
| 357 | 356 | ||
| 358 | source "drivers/gpu/drm/meson/Kconfig" | 357 | source "drivers/gpu/drm/meson/Kconfig" |
| @@ -113,7 +113,6 @@ obj-y += bridge/ · 112 unmodified lines | |||
| 113 | obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ | 113 | obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ |
| 114 | obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ | 114 | obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ |
| 115 | obj-y += hisilicon/ | 115 | obj-y += hisilicon/ |
| 116 | obj-$(CONFIG_DRM_ZTE) += zte/ | ||
| 117 | obj-$(CONFIG_DRM_MXSFB) += mxsfb/ | 116 | obj-$(CONFIG_DRM_MXSFB) += mxsfb/ |
| 118 | obj-y += tiny/ | 117 | obj-y += tiny/ |
| 119 | obj-$(CONFIG_DRM_PL111) += pl111/ | 118 | obj-$(CONFIG_DRM_PL111) += pl111/ |
| @@ -1222,6 +1222,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, · 1221 unmodified lines | |||
| 1222 | if (r) | 1222 | if (r) |
| 1223 | goto error_unlock; | 1223 | goto error_unlock; |
| 1224 | 1224 | ||
| 1225 | drm_sched_job_arm(&job->base); | ||
| 1226 | |||
| 1225 | /* No memory allocation is allowed while holding the notifier lock. | 1227 | /* No memory allocation is allowed while holding the notifier lock. |
| 1226 | * The lock is held until amdgpu_cs_submit is finished and fence is | 1228 | * The lock is held until amdgpu_cs_submit is finished and fence is |
| 1227 | * added to BOs. | 1229 | * added to BOs. |
| @@ -1259,7 +1261,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, · 31 unmodified lines | |||
| 1259 | 1261 | ||
| 1260 | trace_amdgpu_cs_ioctl(job); | 1262 | trace_amdgpu_cs_ioctl(job); |
| 1261 | amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->ticket); | 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 | amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm); | 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 | struct amdgpu_fence_driver *drv = &ring->fence_drv; | 266 | struct amdgpu_fence_driver *drv = &ring->fence_drv; |
| 267 | struct amdgpu_device *adev = ring->adev; | 267 | struct amdgpu_device *adev = ring->adev; |
| 268 | uint32_t seq, last_seq; | 268 | uint32_t seq, last_seq; |
| 269 | int r; | ||
| 270 | 269 | ||
| 271 | do { | 270 | do { |
| 272 | last_seq = atomic_read(&ring->fence_drv.last_seq); | 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 | if (!fence) | 297 | if (!fence) |
| 299 | continue; | 298 | continue; |
| 300 | 299 | ||
| 301 | r = dma_fence_signal(fence); | 300 | dma_fence_signal(fence); |
| 302 | if (!r) | ||
| 303 | DMA_FENCE_TRACE(fence, "signaled from irq context\n"); | ||
| 304 | else | ||
| 305 | BUG(); | ||
| 306 | |||
| 307 | dma_fence_put(fence); | 301 | dma_fence_put(fence); |
| 308 | pm_runtime_mark_last_busy(adev_to_drm(adev)->dev); | 302 | pm_runtime_mark_last_busy(adev_to_drm(adev)->dev); |
| 309 | pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); | 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 | if (!timer_pending(&ring->fence_drv.fallback_timer)) | 678 | if (!timer_pending(&ring->fence_drv.fallback_timer)) |
| 685 | amdgpu_fence_schedule_fallback(ring); | 679 | amdgpu_fence_schedule_fallback(ring); |
| 686 | 680 | ||
| 687 | DMA_FENCE_TRACE(f, "armed on ring %i!\n", ring->idx); | ||
| 688 | |||
| 689 | return true; | 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 | if (r) | 182 | if (r) |
| 183 | return r; | 183 | return r; |
| 184 | 184 | ||
| 185 | drm_sched_job_arm(&job->base); | ||
| 186 | |||
| 185 | *f = dma_fence_get(&job->base.s_fence->finished); | 187 | *f = dma_fence_get(&job->base.s_fence->finished); |
| 186 | amdgpu_job_free_resources(job); | 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 | return 0; | 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 | struct amdgpu_ttm_tt *gtt = (void *)ttm; | 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 | if (gtt->usertask) | 1069 | if (gtt->usertask) |
| 1072 | put_task_struct(gtt->usertask); | 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 | struct amdgpu_ttm_tt *gtt = (void *)ttm; | 1146 | struct amdgpu_ttm_tt *gtt = (void *)ttm; |
| 1149 | struct amdgpu_device *adev; | 1147 | struct amdgpu_device *adev; |
| 1150 | 1148 | ||
| 1149 | amdgpu_ttm_backend_unbind(bdev, ttm); | ||
| 1150 | |||
| 1151 | if (gtt->userptr) { | 1151 | if (gtt->userptr) { |
| 1152 | amdgpu_ttm_tt_set_user_pages(ttm, NULL); | 1152 | amdgpu_ttm_tt_set_user_pages(ttm, NULL); |
| 1153 | kfree(ttm->sg); | 1153 | kfree(ttm->sg); |
| @@ -165,7 +165,7 @@ bool malidp_format_mod_supported(struct drm_device *drm, · 164 unmodified lines | |||
| 165 | return !malidp_hw_format_is_afbc_only(format); | 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 | DRM_ERROR("Unknown modifier (not Arm)\n"); | 169 | DRM_ERROR("Unknown modifier (not Arm)\n"); |
| 170 | return false; | 170 | return false; |
| 171 | } | 171 | } |
| @@ -1,21 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 1 | /* | 2 | /* |
| 2 | * adv7511_cec.c - Analog Devices ADV7511/33 cec driver | 3 | * adv7511_cec.c - Analog Devices ADV7511/33 cec driver |
| 3 | * | 4 | * |
| 4 | * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | 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 | #include <linux/device.h> | 8 | #include <linux/device.h> |
| @@ -720,7 +720,7 @@ static int edid_read(struct anx7625_data *ctx, · 719 unmodified lines | |||
| 720 | ret = sp_tx_aux_rd(ctx, 0xf1); | 720 | ret = sp_tx_aux_rd(ctx, 0xf1); |
| 721 | 721 | ||
| 722 | if (ret) { | 722 | if (ret) { |
| 723 | sp_tx_rst_aux(ctx); | 723 | ret = sp_tx_rst_aux(ctx); |
| 724 | DRM_DEV_DEBUG_DRIVER(dev, "edid read fail, reset!\n"); | 724 | DRM_DEV_DEBUG_DRIVER(dev, "edid read fail, reset!\n"); |
| 725 | } else { | 725 | } else { |
| 726 | ret = anx7625_reg_block_read(ctx, ctx->i2c.rx_p0_client, | 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 | if (cnt > EDID_TRY_CNT) | 735 | if (cnt > EDID_TRY_CNT) |
| 736 | return -EIO; | 736 | return -EIO; |
| 737 | 737 | ||
| 738 | return 0; | 738 | return ret; |
| 739 | } | 739 | } |
| 740 | 740 | ||
| 741 | static int segments_edid_read(struct anx7625_data *ctx, | 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 | if (cnt > EDID_TRY_CNT) | 785 | if (cnt > EDID_TRY_CNT) |
| 786 | return -EIO; | 786 | return -EIO; |
| 787 | 787 | ||
| 788 | return 0; | 788 | return ret; |
| 789 | } | 789 | } |
| 790 | 790 | ||
| 791 | static int sp_tx_edid_read(struct anx7625_data *ctx, | 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 | if (g_edid_break == 1) | 845 | if (g_edid_break == 1) |
| 846 | break; | 846 | break; |
| 847 | 847 | ||
| 848 | segments_edid_read(ctx, count / 2, | 848 | ret = segments_edid_read(ctx, count / 2, |
| 849 | pblock_buf, offset); | 849 | pblock_buf, offset); |
| 850 | if (ret < 0) | ||
| 851 | return ret; | ||
| 852 | |||
| 850 | memcpy(&pedid_blocks_buf[edid_pos], | 853 | memcpy(&pedid_blocks_buf[edid_pos], |
| 851 | pblock_buf, | 854 | pblock_buf, |
| 852 | MAX_DPCD_BUFFER_SIZE); | 855 | MAX_DPCD_BUFFER_SIZE); |
| @@ -863,8 +866,11 @@ static int sp_tx_edid_read(struct anx7625_data *ctx, · 10 unmodified lines | |||
| 863 | if (g_edid_break == 1) | 866 | if (g_edid_break == 1) |
| 864 | break; | 867 | break; |
| 865 | 868 | ||
| 866 | segments_edid_read(ctx, count / 2, | 869 | ret = segments_edid_read(ctx, count / 2, |
| 867 | pblock_buf, offset); | 870 | pblock_buf, offset); |
| 871 | if (ret < 0) | ||
| 872 | return ret; | ||
| 873 | |||
| 868 | memcpy(&pedid_blocks_buf[edid_pos], | 874 | memcpy(&pedid_blocks_buf[edid_pos], |
| 869 | pblock_buf, | 875 | pblock_buf, |
| 870 | MAX_DPCD_BUFFER_SIZE); | 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 | /* Reset aux channel */ | 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 | return (blocks_num + 1); | 902 | return (blocks_num + 1); |
| 893 | } | 903 | } |
| @@ -1325,7 +1335,6 @@ static int anx7625_attach_dsi(struct anx7625_data *ctx) · 431 unmodified lines | |||
| 1325 | dsi->format = MIPI_DSI_FMT_RGB888; | 1335 | dsi->format = MIPI_DSI_FMT_RGB888; |
| 1326 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | | 1336 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | |
| 1327 | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | | 1337 | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | |
| 1328 | MIPI_DSI_MODE_NO_EOT_PACKET | | ||
| 1329 | MIPI_DSI_MODE_VIDEO_HSE; | 1338 | MIPI_DSI_MODE_VIDEO_HSE; |
| 1330 | 1339 | ||
| 1331 | if (mipi_dsi_attach(dsi) < 0) { | 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 | struct cdns_dsi *dsi; | 1172 | struct cdns_dsi *dsi; |
| 1173 | struct cdns_dsi_input *input; | 1173 | struct cdns_dsi_input *input; |
| 1174 | struct resource *res; | ||
| 1175 | int ret, irq; | 1174 | int ret, irq; |
| 1176 | u32 val; | 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 | input = &dsi->input; | 1183 | input = &dsi->input; |
| 1185 | 1184 | ||
| 1186 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1185 | dsi->regs = devm_platform_ioremap_resource(pdev, 0); |
| 1187 | dsi->regs = devm_ioremap_resource(&pdev->dev, res); | ||
| 1188 | if (IS_ERR(dsi->regs)) | 1186 | if (IS_ERR(dsi->regs)) |
| 1189 | return PTR_ERR(dsi->regs); | 1187 | return PTR_ERR(dsi->regs); |
| 1190 | 1188 | ||
| @@ -889,7 +889,7 @@ unlock: · 888 unmodified lines | |||
| 889 | static int it66121_probe(struct i2c_client *client, | 889 | static int it66121_probe(struct i2c_client *client, |
| 890 | const struct i2c_device_id *id) | 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 | struct device_node *ep; | 893 | struct device_node *ep; |
| 894 | int ret; | 894 | int ret; |
| 895 | struct it66121_ctx *ctx; | 895 | struct it66121_ctx *ctx; |
| @@ -924,6 +924,9 @@ static int it66121_probe(struct i2c_client *client, · 28 unmodified lines | |||
| 924 | ctx->next_bridge = of_drm_find_bridge(ep); | 924 | ctx->next_bridge = of_drm_find_bridge(ep); |
| 925 | of_node_put(ep); | 925 | of_node_put(ep); |
| 926 | 926 | ||
| 927 | if (!ctx->next_bridge) | ||
| 928 | return -EPROBE_DEFER; | ||
| 929 | |||
| 927 | i2c_set_clientdata(client, ctx); | 930 | i2c_set_clientdata(client, ctx); |
| 928 | mutex_init(&ctx->lock); | 931 | mutex_init(&ctx->lock); |
| 929 | 932 | ||
| @@ -18,16 +18,18 @@ · 17 unmodified lines | |||
| 18 | #include <drm/drm_print.h> | 18 | #include <drm/drm_print.h> |
| 19 | 19 | ||
| 20 | #define PAGE2_GPIO_H 0xa7 | 20 | #define PAGE2_GPIO_H 0xa7 |
| 21 | #define PS_GPIO9 BIT(1) | 21 | #define PS_GPIO9 BIT(1) |
| 22 | #define PAGE2_I2C_BYPASS 0xea | 22 | #define PAGE2_I2C_BYPASS 0xea |
| 23 | #define I2C_BYPASS_EN 0xd0 | 23 | #define I2C_BYPASS_EN 0xd0 |
| 24 | #define PAGE2_MCS_EN 0xf3 | 24 | #define PAGE2_MCS_EN 0xf3 |
| 25 | #define MCS_EN BIT(0) | 25 | #define MCS_EN BIT(0) |
| 26 | |||
| 26 | #define PAGE3_SET_ADD 0xfe | 27 | #define PAGE3_SET_ADD 0xfe |
| 27 | #define VDO_CTL_ADD 0x13 | 28 | #define VDO_CTL_ADD 0x13 |
| 28 | #define VDO_DIS 0x18 | 29 | #define VDO_DIS 0x18 |
| 29 | #define VDO_EN 0x1c | 30 | #define VDO_EN 0x1c |
| 30 | #define DP_NUM_LANES 4 | 31 | |
| 32 | #define NUM_MIPI_LANES 4 | ||
| 31 | 33 | ||
| 32 | /* | 34 | /* |
| 33 | * PS8640 uses multiple addresses: | 35 | * PS8640 uses multiple addresses: |
| @@ -254,7 +256,7 @@ static int ps8640_bridge_attach(struct drm_bridge *bridge, · 220 unmodified lines | |||
| 254 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | | 256 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | |
| 255 | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; | 257 | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; |
| 256 | dsi->format = MIPI_DSI_FMT_RGB888; | 258 | dsi->format = MIPI_DSI_FMT_RGB888; |
| 257 | dsi->lanes = DP_NUM_LANES; | 259 | dsi->lanes = NUM_MIPI_LANES; |
| 258 | ret = mipi_dsi_attach(dsi); | 260 | ret = mipi_dsi_attach(dsi); |
| 259 | if (ret) | 261 | if (ret) |
| 260 | goto err_dsi_attach; | 262 | goto err_dsi_attach; |
| @@ -28,6 +28,7 @@ · 27 unmodified lines | |||
| 28 | #include <drm/drm_atomic_state_helper.h> | 28 | #include <drm/drm_atomic_state_helper.h> |
| 29 | #include <drm/drm_bridge.h> | 29 | #include <drm/drm_bridge.h> |
| 30 | #include <drm/drm_encoder.h> | 30 | #include <drm/drm_encoder.h> |
| 31 | #include <drm/drm_of.h> | ||
| 31 | #include <drm/drm_print.h> | 32 | #include <drm/drm_print.h> |
| 32 | 33 | ||
| 33 | #include "drm_crtc_internal.h" | 34 | #include "drm_crtc_internal.h" |
| @@ -51,10 +52,8 @@ · 17 unmodified lines | |||
| 51 | * | 52 | * |
| 52 | * Display drivers are responsible for linking encoders with the first bridge | 53 | * Display drivers are responsible for linking encoders with the first bridge |
| 53 | * in the chains. This is done by acquiring the appropriate bridge with | 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 | * devm_drm_of_get_bridge(). Once acquired, the bridge shall be attached to the |
| 55 | * panel with drm_panel_bridge_add_typed() (or the managed version | 56 | * encoder with a call to drm_bridge_attach(). |
| 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(). | ||
| 58 | * | 57 | * |
| 59 | * Bridges are responsible for linking themselves with the next bridge in the | 58 | * Bridges are responsible for linking themselves with the next bridge in the |
| 60 | * chain, if any. This is done the same way as for encoders, with the call to | 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 | return NULL; | 1232 | return NULL; |
| 1234 | } | 1233 | } |
| 1235 | EXPORT_SYMBOL(of_drm_find_bridge); | 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 | #endif | 1269 | #endif |
| 1237 | 1270 | ||
| 1238 | MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>"); | 1271 | MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>"); |
| @@ -65,6 +65,14 @@ · 64 unmodified lines | |||
| 65 | * support can instead use e.g. drm_helper_hpd_irq_event(). | 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 | struct drm_conn_prop_enum_list { | 76 | struct drm_conn_prop_enum_list { |
| 69 | int type; | 77 | int type; |
| 70 | const char *name; | 78 | const char *name; |
| @@ -267,6 +275,7 @@ int drm_connector_init(struct drm_device *dev, · 196 unmodified lines | |||
| 267 | goto out_put_type_id; | 275 | goto out_put_type_id; |
| 268 | } | 276 | } |
| 269 | 277 | ||
| 278 | INIT_LIST_HEAD(&connector->global_connector_list_entry); | ||
| 270 | INIT_LIST_HEAD(&connector->probed_modes); | 279 | INIT_LIST_HEAD(&connector->probed_modes); |
| 271 | INIT_LIST_HEAD(&connector->modes); | 280 | INIT_LIST_HEAD(&connector->modes); |
| 272 | mutex_init(&connector->mutex); | 281 | mutex_init(&connector->mutex); |
| @@ -474,6 +483,8 @@ void drm_connector_cleanup(struct drm_connector *connector) · 201 unmodified lines | |||
| 474 | drm_mode_object_unregister(dev, &connector->base); | 483 | drm_mode_object_unregister(dev, &connector->base); |
| 475 | kfree(connector->name); | 484 | kfree(connector->name); |
| 476 | connector->name = NULL; | 485 | connector->name = NULL; |
| 486 | fwnode_handle_put(connector->fwnode); | ||
| 487 | connector->fwnode = NULL; | ||
| 477 | spin_lock_irq(&dev->mode_config.connector_list_lock); | 488 | spin_lock_irq(&dev->mode_config.connector_list_lock); |
| 478 | list_del(&connector->head); | 489 | list_del(&connector->head); |
| 479 | dev->mode_config.num_connector--; | 490 | dev->mode_config.num_connector--; |
| @@ -532,6 +543,9 @@ int drm_connector_register(struct drm_connector *connector) · 52 unmodified lines | |||
| 532 | /* Let userspace know we have a new connector */ | 543 | /* Let userspace know we have a new connector */ |
| 533 | drm_sysfs_hotplug_event(connector->dev); | 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 | goto unlock; | 549 | goto unlock; |
| 536 | 550 | ||
| 537 | err_debugfs: | 551 | err_debugfs: |
| @@ -560,6 +574,10 @@ void drm_connector_unregister(struct drm_connector *connector) · 22 unmodified lines | |||
| 560 | return; | 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 | if (connector->funcs->early_unregister) | 581 | if (connector->funcs->early_unregister) |
| 564 | connector->funcs->early_unregister(connector); | 582 | connector->funcs->early_unregister(connector); |
| 565 | 583 | ||
| @@ -2543,6 +2561,67 @@ out: · 1977 unmodified lines | |||
| 2543 | return ret; | 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 | * DOC: Tile group | 2627 | * DOC: Tile group |
| @@ -58,6 +58,7 @@ struct drm_property; · 57 unmodified lines | |||
| 58 | struct edid; | 58 | struct edid; |
| 59 | struct kref; | 59 | struct kref; |
| 60 | struct work_struct; | 60 | struct work_struct; |
| 61 | struct fwnode_handle; | ||
| 61 | 62 | ||
| 62 | /* drm_crtc.c */ | 63 | /* drm_crtc.c */ |
| 63 | int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, | 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 | int drm_connector_create_standard_properties(struct drm_device *dev); | 187 | int drm_connector_create_standard_properties(struct drm_device *dev); |
| 187 | const char *drm_get_connector_force_name(enum drm_connector_force force); | 188 | const char *drm_get_connector_force_name(enum drm_connector_force force); |
| 188 | void drm_connector_free_work_fn(struct work_struct *work); | 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 | /* IOCTL */ | 192 | /* IOCTL */ |
| 191 | int drm_connector_property_set_ioctl(struct drm_device *dev, | 193 | int drm_connector_property_set_ioctl(struct drm_device *dev, |
| @@ -10,6 +10,10 @@ · 9 unmodified lines | |||
| 10 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
| 11 | #include <linux/vmalloc.h> | 11 | #include <linux/vmalloc.h> |
| 12 | 12 | ||
| 13 | #ifdef CONFIG_X86 | ||
| 14 | #include <asm/set_memory.h> | ||
| 15 | #endif | ||
| 16 | |||
| 13 | #include <drm/drm.h> | 17 | #include <drm/drm.h> |
| 14 | #include <drm/drm_device.h> | 18 | #include <drm/drm_device.h> |
| 15 | #include <drm/drm_drv.h> | 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 | return PTR_ERR(pages); | 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 | shmem->pages = pages; | 179 | shmem->pages = pages; |
| 166 | 180 | ||
| 167 | return 0; | 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 | if (--shmem->pages_use_count > 0) | 217 | if (--shmem->pages_use_count > 0) |
| 204 | return; | 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 | drm_gem_put_pages(obj, shmem->pages, | 225 | drm_gem_put_pages(obj, shmem->pages, |
| 207 | shmem->pages_mark_dirty_on_put, | 226 | shmem->pages_mark_dirty_on_put, |
| 208 | shmem->pages_mark_accessed_on_put); | 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 | } else { | 561 | } else { |
| 543 | page = shmem->pages[page_offset]; | 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 | mutex_unlock(&shmem->pages_lock); | 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 | return ret; | 631 | return ret; |
| 613 | } | 632 | } |
| 614 | 633 | ||
| 615 | vma->vm_flags |= VM_MIXEDMAP | VM_DONTEXPAND; | 634 | vma->vm_flags |= VM_PFNMAP | VM_DONTEXPAND; |
| 616 | vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); | 635 | vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); |
| 617 | if (shmem->map_wc) | 636 | if (shmem->map_wc) |
| 618 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | 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 | static void bo_driver_ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *tt) | 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 | ttm_tt_fini(tt); | 849 | ttm_tt_fini(tt); |
| 851 | kfree(tt); | 850 | kfree(tt); |
| 852 | } | 851 | } |
| @@ -522,19 +522,7 @@ int drm_version(struct drm_device *dev, void *data, · 521 unmodified lines | |||
| 522 | return err; | 522 | return err; |
| 523 | } | 523 | } |
| 524 | 524 | ||
| 525 | /** | 525 | static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) |
| 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) | ||
| 538 | { | 526 | { |
| 539 | /* ROOT_ONLY is only for CAP_SYS_ADMIN */ | 527 | /* ROOT_ONLY is only for CAP_SYS_ADMIN */ |
| 540 | if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN))) | 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 | return 0; | 546 | return 0; |
| 559 | } | 547 | } |
| 560 | EXPORT_SYMBOL(drm_ioctl_permit); | ||
| 561 | 548 | ||
| 562 | #define DRM_IOCTL_DEF(ioctl, _func, _flags) \ | 549 | #define DRM_IOCTL_DEF(ioctl, _func, _flags) \ |
| 563 | [DRM_IOCTL_NR(ioctl)] = { \ | 550 | [DRM_IOCTL_NR(ioctl)] = { \ |
| @@ -725,7 +712,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { · 161 unmodified lines | |||
| 725 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER), | 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 | * DOC: driver specific ioctls | 718 | * DOC: driver specific ioctls |
| @@ -834,8 +821,8 @@ long drm_ioctl(struct file *filp, · 102 unmodified lines | |||
| 834 | if (drm_dev_is_unplugged(dev)) | 821 | if (drm_dev_is_unplugged(dev)) |
| 835 | return -ENODEV; | 822 | return -ENODEV; |
| 836 | 823 | ||
| 837 | if (DRM_IOCTL_TYPE(cmd) != DRM_IOCTL_BASE) | 824 | if (DRM_IOCTL_TYPE(cmd) != DRM_IOCTL_BASE) |
| 838 | return -ENOTTY; | 825 | return -ENOTTY; |
| 839 | 826 | ||
| 840 | is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END; | 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 | static int __init drm_kms_helper_init(void) | 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 | return drm_dp_aux_dev_init(); | 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 | * return either the associated struct drm_panel or drm_bridge device. Either | 231 | * return either the associated struct drm_panel or drm_bridge device. Either |
| 232 | * @panel or @bridge must not be NULL. | 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 | * Returns zero if successful, or one of the standard error codes if it fails. | 237 | * Returns zero if successful, or one of the standard error codes if it fails. |
| 235 | */ | 238 | */ |
| 236 | int drm_of_find_panel_or_bridge(const struct device_node *np, | 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 | .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, | 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 | static const struct dmi_system_id orientation_data[] = { | 118 | static const struct dmi_system_id orientation_data[] = { |
| 113 | { /* Acer One 10 (S1003) */ | 119 | { /* Acer One 10 (S1003) */ |
| 114 | .matches = { | 120 | .matches = { |
| @@ -134,6 +140,20 @@ static const struct dmi_system_id orientation_data[] = { · 19 unmodified lines | |||
| 134 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"), | 140 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T103HAF"), |
| 135 | }, | 141 | }, |
| 136 | .driver_data = (void *)&lcd800x1280_rightside_up, | 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 | }, { /* GPD MicroPC (generic strings, also match on bios date) */ | 157 | }, { /* GPD MicroPC (generic strings, also match on bios date) */ |
| 138 | .matches = { | 158 | .matches = { |
| 139 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), | 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 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"), | 213 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "TW891"), |
| 194 | }, | 214 | }, |
| 195 | .driver_data = (void *)&itworks_tw891, | 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 | * Lenovo Ideapad Miix 310 laptop, only some production batches | 224 | * Lenovo Ideapad Miix 310 laptop, only some production batches |
| 198 | * have a portrait screen, the resolution checks makes the quirk | 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 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), | 238 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"), |
| 212 | }, | 239 | }, |
| 213 | .driver_data = (void *)&lcd800x1280_rightside_up, | 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 | .matches = { | 248 | .matches = { |
| 216 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), | 249 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
| 217 | DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81H3"), | ||
| 218 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"), | 250 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"), |
| 219 | }, | 251 | }, |
| 220 | .driver_data = (void *)&lcd1200x1920_rightside_up, | 252 | .driver_data = (void *)&lcd1200x1920_rightside_up, |
| @@ -225,6 +257,19 @@ static const struct dmi_system_id orientation_data[] = { · 4 unmodified lines | |||
| 225 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Default string"), | 257 | DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Default string"), |
| 226 | }, | 258 | }, |
| 227 | .driver_data = (void *)&onegx1_pro, | 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 | }, { /* VIOS LTH17 */ | 273 | }, { /* VIOS LTH17 */ |
| 229 | .matches = { | 274 | .matches = { |
| 230 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"), | 275 | DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"), |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | |||
| 10 | * Copyright (c) 2003-2004 IBM Corp. | 10 | * Copyright (c) 2003-2004 IBM Corp. |
| 11 | */ | 11 | */ |
| 12 | 12 | ||
| 13 | #include <linux/acpi.h> | ||
| 13 | #include <linux/device.h> | 14 | #include <linux/device.h> |
| 14 | #include <linux/err.h> | 15 | #include <linux/err.h> |
| 15 | #include <linux/export.h> | 16 | #include <linux/export.h> |
| @@ -50,8 +51,45 @@ static struct device_type drm_sysfs_device_minor = { · 34 unmodified lines | |||
| 50 | .name = "drm_minor" | 51 | .name = "drm_minor" |
| 51 | }; | 52 | }; |
| 52 | 53 | ||
| 54 | static struct device_type drm_sysfs_device_connector = { | ||
| 55 | .name = "drm_connector", | ||
| 56 | }; | ||
| 57 | |||
| 53 | struct class *drm_class; | 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 | static char *drm_devnode(struct device *dev, umode_t *mode) | 93 | static char *drm_devnode(struct device *dev, umode_t *mode) |
| 56 | { | 94 | { |
| 57 | return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); | 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 | drm_class->devnode = drm_devnode; | 125 | drm_class->devnode = drm_devnode; |
| 126 | |||
| 127 | drm_sysfs_acpi_register(); | ||
| 88 | return 0; | 128 | return 0; |
| 89 | } | 129 | } |
| 90 | 130 | ||
| @@ -97,11 +137,17 @@ void drm_sysfs_destroy(void) · 6 unmodified lines | |||
| 97 | { | 137 | { |
| 98 | if (IS_ERR_OR_NULL(drm_class)) | 138 | if (IS_ERR_OR_NULL(drm_class)) |
| 99 | return; | 139 | return; |
| 140 | drm_sysfs_acpi_unregister(); | ||
| 100 | class_remove_file(drm_class, &class_attr_version.attr); | 141 | class_remove_file(drm_class, &class_attr_version.attr); |
| 101 | class_destroy(drm_class); | 142 | class_destroy(drm_class); |
| 102 | drm_class = NULL; | 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 | * Connector properties | 152 | * Connector properties |
| 107 | */ | 153 | */ |
| @@ -273,27 +319,47 @@ static const struct attribute_group *connector_dev_groups[] = { · 165 unmodified lines | |||
| 273 | int drm_sysfs_connector_add(struct drm_connector *connector) | 319 | int drm_sysfs_connector_add(struct drm_connector *connector) |
| 274 | { | 320 | { |
| 275 | struct drm_device *dev = connector->dev; | 321 | struct drm_device *dev = connector->dev; |
| 322 | struct device *kdev; | ||
| 323 | int r; | ||
| 276 | 324 | ||
| 277 | if (connector->kdev) | 325 | if (connector->kdev) |
| 278 | return 0; | 326 | return 0; |
| 279 | 327 | ||
| 280 | connector->kdev = | 328 | kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); |
| 281 | device_create_with_groups(drm_class, dev->primary->kdev, 0, | 329 | if (!kdev) |
| 282 | connector, connector_dev_groups, | 330 | return -ENOMEM; |
| 283 | "card%d-%s", dev->primary->index, | 331 | |
| 284 | connector->name); | 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 | DRM_DEBUG("adding \"%s\" to sysfs\n", | 344 | DRM_DEBUG("adding \"%s\" to sysfs\n", |
| 286 | connector->name); | 345 | connector->name); |
| 287 | 346 | ||
| 288 | if (IS_ERR(connector->kdev)) { | 347 | r = device_add(kdev); |
| 289 | DRM_ERROR("failed to register connector device: %ld\n", PTR_ERR(connector->kdev)); | 348 | if (r) { |
| 290 | return PTR_ERR(connector->kdev); | 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 | if (connector->ddc) | 355 | if (connector->ddc) |
| 294 | return sysfs_create_link(&connector->kdev->kobj, | 356 | return sysfs_create_link(&connector->kdev->kobj, |
| 295 | &connector->ddc->dev.kobj, "ddc"); | 357 | &connector->ddc->dev.kobj, "ddc"); |
| 296 | return 0; | 358 | return 0; |
| 359 | |||
| 360 | err_free: | ||
| 361 | put_device(kdev); | ||
| 362 | return r; | ||
| 297 | } | 363 | } |
| 298 | 364 | ||
| 299 | void drm_sysfs_connector_remove(struct drm_connector *connector) | 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 | EXPORT_SYMBOL(drm_sysfs_connector_status_event); | 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 | struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) | 443 | struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) |
| 383 | { | 444 | { |
| 384 | const char *minor_str; | 445 | const char *minor_str; |
| @@ -163,6 +163,8 @@ int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity, · 162 unmodified lines | |||
| 163 | if (ret) | 163 | if (ret) |
| 164 | goto out_unlock; | 164 | goto out_unlock; |
| 165 | 165 | ||
| 166 | drm_sched_job_arm(&submit->sched_job); | ||
| 167 | |||
| 166 | submit->out_fence = dma_fence_get(&submit->sched_job.s_fence->finished); | 168 | submit->out_fence = dma_fence_get(&submit->sched_job.s_fence->finished); |
| 167 | submit->out_fence_id = idr_alloc_cyclic(&submit->gpu->fence_idr, | 169 | submit->out_fence_id = idr_alloc_cyclic(&submit->gpu->fence_idr, |
| 168 | submit->out_fence, 0, | 170 | submit->out_fence, 0, |
| @@ -176,7 +178,7 @@ int etnaviv_sched_push_job(struct drm_sched_entity *sched_entity, · 7 unmodified lines | |||
| 176 | /* the scheduler holds on to the job now */ | 178 | /* the scheduler holds on to the job now */ |
| 177 | kref_get(&submit->refcount); | 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 | out_unlock: | 183 | out_unlock: |
| 182 | mutex_unlock(&submit->gpu->fence_lock); | 184 | mutex_unlock(&submit->gpu->fence_lock); |
| @@ -2,7 +2,7 @@ · 1 unmodified line | |||
| 2 | 2 | ||
| 3 | config DRM_GUD | 3 | config DRM_GUD |
| 4 | tristate "GUD USB Display" | 4 | tristate "GUD USB Display" |
| 5 | depends on DRM && USB | 5 | depends on DRM && USB && MMU |
| 6 | select LZ4_COMPRESS | 6 | select LZ4_COMPRESS |
| 7 | select DRM_KMS_HELPER | 7 | select DRM_KMS_HELPER |
| 8 | select DRM_GEM_SHMEM_HELPER | 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 | struct i915_ttm_tt *i915_tt = container_of(ttm, typeof(*i915_tt), ttm); | 215 | struct i915_ttm_tt *i915_tt = container_of(ttm, typeof(*i915_tt), ttm); |
| 216 | 216 | ||
| 217 | ttm_tt_destroy_common(bdev, ttm); | ||
| 218 | ttm_tt_fini(ttm); | 217 | ttm_tt_fini(ttm); |
| 219 | kfree(i915_tt); | 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 | if (explicit) | 267 | if (explicit) |
| 268 | return 0; | 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 | static int lima_gem_add_deps(struct drm_file *file, struct lima_submit *submit) | 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 | if (err) | 287 | if (err) |
| 286 | return err; | 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 | if (err) { | 291 | if (err) { |
| 290 | dma_fence_put(fence); | 292 | dma_fence_put(fence); |
| 291 | return err; | 293 | return err; |
| @@ -359,8 +361,7 @@ int lima_gem_submit(struct drm_file *file, struct lima_submit *submit) · 67 unmodified lines | |||
| 359 | goto err_out2; | 361 | goto err_out2; |
| 360 | } | 362 | } |
| 361 | 363 | ||
| 362 | fence = lima_sched_context_queue_task( | 364 | fence = lima_sched_context_queue_task(submit->task); |
| 363 | submit->ctx->context + submit->pipe, submit->task); | ||
| 364 | 365 | ||
| 365 | for (i = 0; i < submit->nr_bos; i++) { | 366 | for (i = 0; i < submit->nr_bos; i++) { |
| 366 | if (submit->bos[i].flags & LIMA_SUBMIT_BO_WRITE) | 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 | return err; | 129 | return err; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | drm_sched_job_arm(&task->base); | ||
| 133 | |||
| 132 | task->num_bos = num_bos; | 134 | task->num_bos = num_bos; |
| 133 | task->vm = lima_vm_get(vm); | 135 | task->vm = lima_vm_get(vm); |
| 134 | 136 | ||
| 135 | xa_init_flags(&task->deps, XA_FLAGS_ALLOC); | ||
| 136 | |||
| 137 | return 0; | 137 | return 0; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | void lima_sched_task_fini(struct lima_sched_task *task) | 140 | void lima_sched_task_fini(struct lima_sched_task *task) |
| 141 | { | 141 | { |
| 142 | struct dma_fence *fence; | ||
| 143 | unsigned long index; | ||
| 144 | int i; | 142 | int i; |
| 145 | 143 | ||
| 146 | drm_sched_job_cleanup(&task->base); | 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 | if (task->bos) { | 146 | if (task->bos) { |
| 154 | for (i = 0; i < task->num_bos; i++) | 147 | for (i = 0; i < task->num_bos; i++) |
| 155 | drm_gem_object_put(&task->bos[i]->base.base); | 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 | drm_sched_entity_fini(&context->base); | 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, | 171 | struct dma_fence *lima_sched_context_queue_task(struct lima_sched_task *task) |
| 179 | struct lima_sched_task *task) | ||
| 180 | { | 172 | { |
| 181 | struct dma_fence *fence = dma_fence_get(&task->base.s_fence->finished); | 173 | struct dma_fence *fence = dma_fence_get(&task->base.s_fence->finished); |
| 182 | 174 | ||
| 183 | trace_lima_task_submit(task); | 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 | return fence; | 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 | static int lima_pm_busy(struct lima_device *ldev) | 180 | static int lima_pm_busy(struct lima_device *ldev) |
| 200 | { | 181 | { |
| 201 | int ret; | 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 | static const struct drm_sched_backend_ops lima_sched_ops = { | 454 | static const struct drm_sched_backend_ops lima_sched_ops = { |
| 474 | .dependency = lima_sched_dependency, | ||
| 475 | .run_job = lima_sched_run_job, | 455 | .run_job = lima_sched_run_job, |
| 476 | .timedout_job = lima_sched_timedout_job, | 456 | .timedout_job = lima_sched_timedout_job, |
| 477 | .free_job = lima_sched_free_job, | 457 | .free_job = lima_sched_free_job, |
| @@ -23,9 +23,6 @@ struct lima_sched_task { · 22 unmodified lines | |||
| 23 | struct lima_vm *vm; | 23 | struct lima_vm *vm; |
| 24 | void *frame; | 24 | void *frame; |
| 25 | 25 | ||
| 26 | struct xarray deps; | ||
| 27 | unsigned long last_dep; | ||
| 28 | |||
| 29 | struct lima_bo **bos; | 26 | struct lima_bo **bos; |
| 30 | int num_bos; | 27 | int num_bos; |
| 31 | 28 | ||
| @@ -98,8 +95,7 @@ int lima_sched_context_init(struct lima_sched_pipe *pipe, · 66 unmodified lines | |||
| 98 | atomic_t *guilty); | 95 | atomic_t *guilty); |
| 99 | void lima_sched_context_fini(struct lima_sched_pipe *pipe, | 96 | void lima_sched_context_fini(struct lima_sched_pipe *pipe, |
| 100 | struct lima_sched_context *context); | 97 | struct lima_sched_context *context); |
| 101 | struct dma_fence *lima_sched_context_queue_task(struct lima_sched_context *context, | 98 | struct dma_fence *lima_sched_context_queue_task(struct lima_sched_task *task); |
| 102 | struct lima_sched_task *task); | ||
| 103 | 99 | ||
| 104 | int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name); | 100 | int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name); |
| 105 | void lima_sched_pipe_fini(struct lima_sched_pipe *pipe); | 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 | struct drm_device *drm; | 276 | struct drm_device *drm; |
| 277 | struct mcde *mcde; | 277 | struct mcde *mcde; |
| 278 | struct component_match *match = NULL; | 278 | struct component_match *match = NULL; |
| 279 | struct resource *res; | ||
| 280 | u32 pid; | 279 | u32 pid; |
| 281 | int irq; | 280 | int irq; |
| 282 | int ret; | 281 | int ret; |
| @@ -344,8 +343,7 @@ static int mcde_probe(struct platform_device *pdev) · 61 unmodified lines | |||
| 344 | goto clk_disable; | 343 | goto clk_disable; |
| 345 | } | 344 | } |
| 346 | 345 | ||
| 347 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 346 | mcde->regs = devm_platform_ioremap_resource(pdev, 0); |
| 348 | mcde->regs = devm_ioremap_resource(dev, res); | ||
| 349 | if (IS_ERR(mcde->regs)) { | 347 | if (IS_ERR(mcde->regs)) { |
| 350 | dev_err(dev, "no MCDE regs\n"); | 348 | dev_err(dev, "no MCDE regs\n"); |
| 351 | ret = -EINVAL; | 349 | ret = -EINVAL; |
| @@ -1169,7 +1169,6 @@ static int mcde_dsi_probe(struct platform_device *pdev) · 1168 unmodified lines | |||
| 1169 | struct device *dev = &pdev->dev; | 1169 | struct device *dev = &pdev->dev; |
| 1170 | struct mcde_dsi *d; | 1170 | struct mcde_dsi *d; |
| 1171 | struct mipi_dsi_host *host; | 1171 | struct mipi_dsi_host *host; |
| 1172 | struct resource *res; | ||
| 1173 | u32 dsi_id; | 1172 | u32 dsi_id; |
| 1174 | int ret; | 1173 | int ret; |
| 1175 | 1174 | ||
| @@ -1187,8 +1186,7 @@ static int mcde_dsi_probe(struct platform_device *pdev) · 11 unmodified lines | |||
| 1187 | return PTR_ERR(d->prcmu); | 1186 | return PTR_ERR(d->prcmu); |
| 1188 | } | 1187 | } |
| 1189 | 1188 | ||
| 1190 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1189 | d->regs = devm_platform_ioremap_resource(pdev, 0); |
| 1191 | d->regs = devm_ioremap_resource(dev, res); | ||
| 1192 | if (IS_ERR(d->regs)) | 1190 | if (IS_ERR(d->regs)) |
| 1193 | return PTR_ERR(d->regs); | 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 | priv->compat = match->compat; | 206 | priv->compat = match->compat; |
| 207 | priv->afbcd.ops = match->afbcd_ops; | 207 | priv->afbcd.ops = match->afbcd_ops; |
| 208 | 208 | ||
| 209 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); | 209 | regs = devm_platform_ioremap_resource_byname(pdev, "vpu"); |
| 210 | regs = devm_ioremap_resource(dev, res); | ||
| 211 | if (IS_ERR(regs)) { | 210 | if (IS_ERR(regs)) { |
| 212 | ret = PTR_ERR(regs); | 211 | ret = PTR_ERR(regs); |
| 213 | goto free_drm; | 212 | goto free_drm; |
| @@ -978,7 +978,6 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master, · 977 unmodified lines | |||
| 978 | struct dw_hdmi_plat_data *dw_plat_data; | 978 | struct dw_hdmi_plat_data *dw_plat_data; |
| 979 | struct drm_bridge *next_bridge; | 979 | struct drm_bridge *next_bridge; |
| 980 | struct drm_encoder *encoder; | 980 | struct drm_encoder *encoder; |
| 981 | struct resource *res; | ||
| 982 | int irq; | 981 | int irq; |
| 983 | int ret; | 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 | return PTR_ERR(meson_dw_hdmi->hdmitx_phy); | 1041 | return PTR_ERR(meson_dw_hdmi->hdmitx_phy); |
| 1043 | } | 1042 | } |
| 1044 | 1043 | ||
| 1045 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1044 | meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0); |
| 1046 | meson_dw_hdmi->hdmitx = devm_ioremap_resource(dev, res); | ||
| 1047 | if (IS_ERR(meson_dw_hdmi->hdmitx)) | 1045 | if (IS_ERR(meson_dw_hdmi->hdmitx)) |
| 1048 | return PTR_ERR(meson_dw_hdmi->hdmitx); | 1046 | return PTR_ERR(meson_dw_hdmi->hdmitx); |
| 1049 | 1047 | ||
| @@ -309,11 +309,6 @@ struct msm_gem_submit { · 308 unmodified lines | |||
| 309 | struct ww_acquire_ctx ticket; | 309 | struct ww_acquire_ctx ticket; |
| 310 | uint32_t seqno; /* Sequence number of the submit on the ring */ | 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 | /* Hw fence, which is created when the scheduler executes the job, and | 312 | /* Hw fence, which is created when the scheduler executes the job, and |
| 318 | * is signaled when the hw finishes (via seqno write from cmdstream) | 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 | return ERR_PTR(ret); | 52 | return ERR_PTR(ret); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | xa_init_flags(&submit->deps, XA_FLAGS_ALLOC); | ||
| 56 | |||
| 57 | kref_init(&submit->ref); | 55 | kref_init(&submit->ref); |
| 58 | submit->dev = dev; | 56 | submit->dev = dev; |
| 59 | submit->aspace = queue->ctx->aspace; | 57 | submit->aspace = queue->ctx->aspace; |
| @@ -72,8 +70,6 @@ void __msm_gem_submit_destroy(struct kref *kref) · 12 unmodified lines | |||
| 72 | { | 70 | { |
| 73 | struct msm_gem_submit *submit = | 71 | struct msm_gem_submit *submit = |
| 74 | container_of(kref, struct msm_gem_submit, ref); | 72 | container_of(kref, struct msm_gem_submit, ref); |
| 75 | unsigned long index; | ||
| 76 | struct dma_fence *fence; | ||
| 77 | unsigned i; | 73 | unsigned i; |
| 78 | 74 | ||
| 79 | if (submit->fence_id) { | 75 | if (submit->fence_id) { |
| @@ -82,12 +78,6 @@ void __msm_gem_submit_destroy(struct kref *kref) · 2 unmodified lines | |||
| 82 | mutex_unlock(&submit->queue->lock); | 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 | dma_fence_put(submit->user_fence); | 81 | dma_fence_put(submit->user_fence); |
| 92 | dma_fence_put(submit->hw_fence); | 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 | return ret; | 330 | return ret; |
| 341 | } | 331 | } |
| 342 | 332 | ||
| 343 | if (no_implicit) | 333 | /* exclusive fences must be ordered */ |
| 334 | if (no_implicit && !write) | ||
| 344 | continue; | 335 | continue; |
| 345 | 336 | ||
| 346 | ret = drm_gem_fence_array_add_implicit(&submit->deps, obj, | 337 | ret = drm_sched_job_add_implicit_dependencies(&submit->base, |
| 347 | write); | 338 | obj, |
| 339 | write); | ||
| 348 | if (ret) | 340 | if (ret) |
| 349 | break; | 341 | break; |
| 350 | } | 342 | } |
| @@ -588,7 +580,7 @@ static struct drm_syncobj **msm_parse_deps(struct msm_gem_submit *submit, · 237 unmodified lines | |||
| 588 | if (ret) | 580 | if (ret) |
| 589 | break; | 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 | if (ret) | 584 | if (ret) |
| 593 | break; | 585 | break; |
| 594 | 586 | ||
| @@ -798,7 +790,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, · 203 unmodified lines | |||
| 798 | goto out_unlock; | 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 | if (ret) | 794 | if (ret) |
| 803 | goto out_unlock; | 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 | submit->nr_cmds = i; | 871 | submit->nr_cmds = i; |
| 880 | 872 | ||
| 873 | drm_sched_job_arm(&submit->base); | ||
| 874 | |||
| 881 | submit->user_fence = dma_fence_get(&submit->base.s_fence->finished); | 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 | if (submit->fence_id < 0) { | 883 | if (submit->fence_id < 0) { |
| 890 | ret = submit->fence_id = 0; | 884 | ret = submit->fence_id = 0; |
| 891 | submit->fence_id = 0; | 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 | struct sync_file *sync_file = sync_file_create(submit->user_fence); | 889 | struct sync_file *sync_file = sync_file_create(submit->user_fence); |
| 897 | if (!sync_file) { | 890 | if (!sync_file) { |
| 898 | ret = -ENOMEM; | 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 | submit_attach_object_fences(submit); | 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 | /* The scheduler owns a ref now: */ | 900 | /* The scheduler owns a ref now: */ |
| 908 | msm_gem_submit_get(submit); | 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 | args->fence = submit->fence_id; | 905 | args->fence = submit->fence_id; |
| 913 | 906 | ||
| @@ -11,17 +11,6 @@ static uint num_hw_submissions = 8; · 10 unmodified lines | |||
| 11 | MODULE_PARM_DESC(num_hw_submissions, "The max # of jobs to write into ringbuffer (default 8)"); | 11 | MODULE_PARM_DESC(num_hw_submissions, "The max # of jobs to write into ringbuffer (default 8)"); |
| 12 | module_param(num_hw_submissions, uint, 0600); | 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 | static struct dma_fence *msm_job_run(struct drm_sched_job *job) | 14 | static struct dma_fence *msm_job_run(struct drm_sched_job *job) |
| 26 | { | 15 | { |
| 27 | struct msm_gem_submit *submit = to_msm_submit(job); | 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 | const struct drm_sched_backend_ops msm_sched_ops = { | 43 | const struct drm_sched_backend_ops msm_sched_ops = { |
| 55 | .dependency = msm_job_dependency, | ||
| 56 | .run_job = msm_job_run, | 44 | .run_job = msm_job_run, |
| 57 | .free_job = msm_job_free | 45 | .free_job = msm_job_free |
| 58 | }; | 46 | }; |
| @@ -1277,6 +1277,8 @@ nouveau_ttm_tt_unpopulate(struct ttm_device *bdev, · 1276 unmodified lines | |||
| 1277 | if (slave) | 1277 | if (slave) |
| 1278 | return; | 1278 | return; |
| 1279 | 1279 | ||
| 1280 | nouveau_ttm_tt_unbind(bdev, ttm); | ||
| 1281 | |||
| 1280 | drm = nouveau_bdev(bdev); | 1282 | drm = nouveau_bdev(bdev); |
| 1281 | dev = drm->dev->dev; | 1283 | dev = drm->dev->dev; |
| 1282 | 1284 | ||
| @@ -1290,8 +1292,6 @@ nouveau_ttm_tt_destroy(struct ttm_device *bdev, · 7 unmodified lines | |||
| 1290 | #if IS_ENABLED(CONFIG_AGP) | 1292 | #if IS_ENABLED(CONFIG_AGP) |
| 1291 | struct nouveau_drm *drm = nouveau_bdev(bdev); | 1293 | struct nouveau_drm *drm = nouveau_bdev(bdev); |
| 1292 | if (drm->agp.bridge) { | 1294 | if (drm->agp.bridge) { |
| 1293 | ttm_agp_unbind(ttm); | ||
| 1294 | ttm_tt_destroy_common(bdev, ttm); | ||
| 1295 | ttm_agp_destroy(ttm); | 1295 | ttm_agp_destroy(ttm); |
| 1296 | return; | 1296 | return; |
| 1297 | } | 1297 | } |
| @@ -21,8 +21,6 @@ nouveau_sgdma_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) · 20 unmodified lines | |||
| 21 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; | 21 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; |
| 22 | 22 | ||
| 23 | if (ttm) { | 23 | if (ttm) { |
| 24 | nouveau_sgdma_unbind(bdev, ttm); | ||
| 25 | ttm_tt_destroy_common(bdev, ttm); | ||
| 26 | ttm_tt_fini(&nvbe->ttm); | 24 | ttm_tt_fini(&nvbe->ttm); |
| 27 | kfree(nvbe); | 25 | kfree(nvbe); |
| 28 | } | 26 | } |
| @@ -3,7 +3,6 @@ config DRM_OMAP · 2 unmodified lines | |||
| 3 | tristate "OMAP DRM" | 3 | tristate "OMAP DRM" |
| 4 | depends on DRM | 4 | depends on DRM |
| 5 | depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM | 5 | depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM |
| 6 | select OMAP2_DSS | ||
| 7 | select DRM_KMS_HELPER | 6 | select DRM_KMS_HELPER |
| 8 | select VIDEOMODE_HELPERS | 7 | select VIDEOMODE_HELPERS |
| 9 | select HDMI | 8 | select HDMI |
| @@ -392,6 +392,17 @@ config DRM_PANEL_SAMSUNG_S6D16D0 · 391 unmodified lines | |||
| 392 | depends on DRM_MIPI_DSI | 392 | depends on DRM_MIPI_DSI |
| 393 | select VIDEOMODE_HELPERS | 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 | config DRM_PANEL_SAMSUNG_S6E3HA2 | 406 | config DRM_PANEL_SAMSUNG_S6E3HA2 |
| 396 | tristate "Samsung S6E3HA2 DSI video mode panel" | 407 | tristate "Samsung S6E3HA2 DSI video mode panel" |
| 397 | depends on OF | 408 | depends on OF |
| @@ -39,6 +39,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o · 38 unmodified lines | |||
| 39 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o | 39 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o |
| 40 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o | 40 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o |
| 41 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o | 41 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o |
| 42 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D27A1) += panel-samsung-s6d27a1.o | ||
| 42 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o | 43 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o |
| 43 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o | 44 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o |
| 44 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o | 45 | obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o |
| @@ -60,6 +60,9 @@ · 59 unmodified lines | |||
| 60 | #define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */ | 60 | #define MCS_CMD2_ENA1 0xFF00 /* Enable Access Command2 "CMD2" */ |
| 61 | #define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */ | 61 | #define MCS_CMD2_ENA2 0xFF80 /* Enable Access Orise Command2 */ |
| 62 | 62 | ||
| 63 | #define OTM8009A_HDISPLAY 480 | ||
| 64 | #define OTM8009A_VDISPLAY 800 | ||
| 65 | |||
| 63 | struct otm8009a { | 66 | struct otm8009a { |
| 64 | struct device *dev; | 67 | struct device *dev; |
| 65 | struct drm_panel panel; | 68 | struct drm_panel panel; |
| @@ -70,19 +73,35 @@ struct otm8009a { · 4 unmodified lines | |||
| 70 | bool enabled; | 73 | bool enabled; |
| 71 | }; | 74 | }; |
| 72 | 75 | ||
| 73 | static const struct drm_display_mode default_mode = { | 76 | static const struct drm_display_mode modes[] = { |
| 74 | .clock = 29700, | 77 | { /* 50 Hz, preferred */ |
| 75 | .hdisplay = 480, | 78 | .clock = 29700, |
| 76 | .hsync_start = 480 + 98, | 79 | .hdisplay = 480, |
| 77 | .hsync_end = 480 + 98 + 32, | 80 | .hsync_start = 480 + 98, |
| 78 | .htotal = 480 + 98 + 32 + 98, | 81 | .hsync_end = 480 + 98 + 32, |
| 79 | .vdisplay = 800, | 82 | .htotal = 480 + 98 + 32 + 98, |
| 80 | .vsync_start = 800 + 15, | 83 | .vdisplay = 800, |
| 81 | .vsync_end = 800 + 15 + 10, | 84 | .vsync_start = 800 + 15, |
| 82 | .vtotal = 800 + 15 + 10 + 14, | 85 | .vsync_end = 800 + 15 + 10, |
| 83 | .flags = 0, | 86 | .vtotal = 800 + 15 + 10 + 14, |
| 84 | .width_mm = 52, | 87 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
| 85 | .height_mm = 86, | 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 | static inline struct otm8009a *panel_to_otm8009a(struct drm_panel *panel) | 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 | /* Default portrait 480x800 rgb24 */ | 227 | /* Default portrait 480x800 rgb24 */ |
| 209 | dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); | 228 | dcs_write_seq(ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x00); |
| 210 | 229 | ||
| 211 | ret = mipi_dsi_dcs_set_column_address(dsi, 0, | 230 | ret = mipi_dsi_dcs_set_column_address(dsi, 0, OTM8009A_HDISPLAY - 1); |
| 212 | default_mode.hdisplay - 1); | ||
| 213 | if (ret) | 231 | if (ret) |
| 214 | return ret; | 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 | if (ret) | 235 | if (ret) |
| 218 | return ret; | 236 | return ret; |
| 219 | 237 | ||
| @@ -337,24 +355,33 @@ static int otm8009a_get_modes(struct drm_panel *panel, · 117 unmodified lines | |||
| 337 | struct drm_connector *connector) | 355 | struct drm_connector *connector) |
| 338 | { | 356 | { |
| 339 | struct drm_display_mode *mode; | 357 | struct drm_display_mode *mode; |
| 340 | 358 | unsigned int num_modes = ARRAY_SIZE(modes); | |
| 341 | mode = drm_mode_duplicate(connector->dev, &default_mode); | 359 | unsigned int i; |
| 342 | if (!mode) { | 360 | |
| 343 | dev_err(panel->dev, "failed to add mode %ux%u@%u\n", | 361 | for (i = 0; i < num_modes; i++) { |
| 344 | default_mode.hdisplay, default_mode.vdisplay, | 362 | mode = drm_mode_duplicate(connector->dev, &modes[i]); |
| 345 | drm_mode_vrefresh(&default_mode)); | 363 | if (!mode) { |
| 346 | return -ENOMEM; | 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 | connector->display_info.width_mm = mode->width_mm; | 381 | connector->display_info.width_mm = mode->width_mm; |
| 355 | connector->display_info.height_mm = mode->height_mm; | 382 | connector->display_info.height_mm = mode->height_mm; |
| 356 | 383 | ||
| 357 | return 1; | 384 | return num_modes; |
| 358 | } | 385 | } |
| 359 | 386 | ||
| 360 | static const struct drm_panel_funcs otm8009a_drm_funcs = { | 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 | .connector_type = DRM_MODE_CONNECTOR_DPI, | 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 | static const struct drm_display_mode logicpd_type_28_mode = { | 3161 | static const struct drm_display_mode logicpd_type_28_mode = { |
| 3175 | .clock = 9107, | 3162 | .clock = 9107, |
| 3176 | .hdisplay = 480, | 3163 | .hdisplay = 480, |
| @@ -3205,6 +3192,19 @@ static const struct panel_desc logicpd_type_28 = { · 28 unmodified lines | |||
| 3205 | .connector_type = DRM_MODE_CONNECTOR_DPI, | 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 | static const struct panel_desc mitsubishi_aa070mc01 = { | 3208 | static const struct panel_desc mitsubishi_aa070mc01 = { |
| 3209 | .modes = &mitsubishi_aa070mc01_mode, | 3209 | .modes = &mitsubishi_aa070mc01_mode, |
| 3210 | .num_modes = 1, | 3210 | .num_modes = 1, |
| @@ -198,7 +198,6 @@ err: · 197 unmodified lines | |||
| 198 | int panfrost_device_init(struct panfrost_device *pfdev) | 198 | int panfrost_device_init(struct panfrost_device *pfdev) |
| 199 | { | 199 | { |
| 200 | int err; | 200 | int err; |
| 201 | struct resource *res; | ||
| 202 | 201 | ||
| 203 | mutex_init(&pfdev->sched_lock); | 202 | mutex_init(&pfdev->sched_lock); |
| 204 | INIT_LIST_HEAD(&pfdev->scheduled_jobs); | 203 | INIT_LIST_HEAD(&pfdev->scheduled_jobs); |
| @@ -236,8 +235,7 @@ int panfrost_device_init(struct panfrost_device *pfdev) · 31 unmodified lines | |||
| 236 | if (err) | 235 | if (err) |
| 237 | goto out_reset; | 236 | goto out_reset; |
| 238 | 237 | ||
| 239 | res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0); | 238 | pfdev->iomem = devm_platform_ioremap_resource(pfdev->pdev, 0); |
| 240 | pfdev->iomem = devm_ioremap_resource(pfdev->dev, res); | ||
| 241 | if (IS_ERR(pfdev->iomem)) { | 239 | if (IS_ERR(pfdev->iomem)) { |
| 242 | err = PTR_ERR(pfdev->iomem); | 240 | err = PTR_ERR(pfdev->iomem); |
| 243 | goto out_pm_domain; | 241 | goto out_pm_domain; |
| @@ -218,7 +218,7 @@ panfrost_copy_in_sync(struct drm_device *dev, · 217 unmodified lines | |||
| 218 | if (ret) | 218 | if (ret) |
| 219 | goto fail; | 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 | if (ret) | 223 | if (ret) |
| 224 | goto fail; | 224 | goto fail; |
| @@ -236,7 +236,7 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, · 11 unmodified lines | |||
| 236 | struct drm_panfrost_submit *args = data; | 236 | struct drm_panfrost_submit *args = data; |
| 237 | struct drm_syncobj *sync_out = NULL; | 237 | struct drm_syncobj *sync_out = NULL; |
| 238 | struct panfrost_job *job; | 238 | struct panfrost_job *job; |
| 239 | int ret = 0; | 239 | int ret = 0, slot; |
| 240 | 240 | ||
| 241 | if (!args->jc) | 241 | if (!args->jc) |
| 242 | return -EINVAL; | 242 | return -EINVAL; |
| @@ -253,38 +253,47 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data, · 10 unmodified lines | |||
| 253 | job = kzalloc(sizeof(*job), GFP_KERNEL); | 253 | job = kzalloc(sizeof(*job), GFP_KERNEL); |
| 254 | if (!job) { | 254 | if (!job) { |
| 255 | ret = -ENOMEM; | 255 | ret = -ENOMEM; |
| 256 | goto fail_out_sync; | 256 | goto out_put_syncout; |
| 257 | } | 257 | } |
| 258 | 258 | ||
| 259 | kref_init(&job->refcount); | 259 | kref_init(&job->refcount); |
| 260 | 260 | ||
| 261 | xa_init_flags(&job->deps, XA_FLAGS_ALLOC); | ||
| 262 | |||
| 263 | job->pfdev = pfdev; | 261 | job->pfdev = pfdev; |
| 264 | job->jc = args->jc; | 262 | job->jc = args->jc; |
| 265 | job->requirements = args->requirements; | 263 | job->requirements = args->requirements; |
| 266 | job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev); | 264 | job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev); |
| 267 | job->file_priv = file->driver_priv; | 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 | ret = panfrost_copy_in_sync(dev, file, args, job); | 275 | ret = panfrost_copy_in_sync(dev, file, args, job); |
| 270 | if (ret) | 276 | if (ret) |
| 271 | goto fail_job; | 277 | goto out_cleanup_job; |
| 272 | 278 | ||
| 273 | ret = panfrost_lookup_bos(dev, file, args, job); | 279 | ret = panfrost_lookup_bos(dev, file, args, job); |
| 274 | if (ret) | 280 | if (ret) |
| 275 | goto fail_job; | 281 | goto out_cleanup_job; |
| 276 | 282 | ||
| 277 | ret = panfrost_job_push(job); | 283 | ret = panfrost_job_push(job); |
| 278 | if (ret) | 284 | if (ret) |
| 279 | goto fail_job; | 285 | goto out_cleanup_job; |
| 280 | 286 | ||
| 281 | /* Update the return sync object for the job */ | 287 | /* Update the return sync object for the job */ |
| 282 | if (sync_out) | 288 | if (sync_out) |
| 283 | drm_syncobj_replace_fence(sync_out, job->render_done_fence); | 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 | panfrost_job_put(job); | 295 | panfrost_job_put(job); |
| 287 | fail_out_sync: | 296 | out_put_syncout: |
| 288 | if (sync_out) | 297 | if (sync_out) |
| 289 | drm_syncobj_put(sync_out); | 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 | return &fence->base; | 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 | /* JS0: fragment jobs. | 107 | /* JS0: fragment jobs. |
| 108 | * JS1: vertex/tiler jobs | 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 | affinity = pfdev->features.shader_present; | 138 | affinity = pfdev->features.shader_present; |
| 139 | 139 | ||
| 140 | job_write(pfdev, JS_AFFINITY_NEXT_LO(js), affinity & 0xFFFFFFFF); | 140 | job_write(pfdev, JS_AFFINITY_NEXT_LO(js), lower_32_bits(affinity)); |
| 141 | job_write(pfdev, JS_AFFINITY_NEXT_HI(js), affinity >> 32); | 141 | job_write(pfdev, JS_AFFINITY_NEXT_HI(js), upper_32_bits(affinity)); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | static u32 | 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 | cfg = panfrost_mmu_as_get(pfdev, job->file_priv->mmu); | 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); | 206 | job_write(pfdev, JS_HEAD_NEXT_LO(js), lower_32_bits(jc_head)); |
| 207 | job_write(pfdev, JS_HEAD_NEXT_HI(js), jc_head >> 32); | 207 | job_write(pfdev, JS_HEAD_NEXT_HI(js), upper_32_bits(jc_head)); |
| 208 | 208 | ||
| 209 | panfrost_job_write_affinity(pfdev, job->requirements, js); | 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 | static int panfrost_acquire_object_fences(struct drm_gem_object **bos, | 243 | static int panfrost_acquire_object_fences(struct drm_gem_object **bos, |
| 244 | int bo_count, | 244 | int bo_count, |
| 245 | struct xarray *deps) | 245 | struct drm_sched_job *job) |
| 246 | { | 246 | { |
| 247 | int i, ret; | 247 | int i, ret; |
| 248 | 248 | ||
| 249 | for (i = 0; i < bo_count; i++) { | 249 | for (i = 0; i < bo_count; i++) { |
| 250 | /* panfrost always uses write mode in its current uapi */ | 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 | if (ret) | 253 | if (ret) |
| 253 | return ret; | 254 | return ret; |
| 254 | } | 255 | } |
| @@ -269,29 +270,21 @@ static void panfrost_attach_object_fences(struct drm_gem_object **bos, · 14 unmodified lines | |||
| 269 | int panfrost_job_push(struct panfrost_job *job) | 270 | int panfrost_job_push(struct panfrost_job *job) |
| 270 | { | 271 | { |
| 271 | struct panfrost_device *pfdev = job->pfdev; | 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 | struct ww_acquire_ctx acquire_ctx; | 273 | struct ww_acquire_ctx acquire_ctx; |
| 275 | int ret = 0; | 274 | int ret = 0; |
| 276 | 275 | ||
| 277 | |||
| 278 | ret = drm_gem_lock_reservations(job->bos, job->bo_count, | 276 | ret = drm_gem_lock_reservations(job->bos, job->bo_count, |
| 279 | &acquire_ctx); | 277 | &acquire_ctx); |
| 280 | if (ret) | 278 | if (ret) |
| 281 | return ret; | 279 | return ret; |
| 282 | 280 | ||
| 283 | mutex_lock(&pfdev->sched_lock); | 281 | mutex_lock(&pfdev->sched_lock); |
| 284 | 282 | drm_sched_job_arm(&job->base); | |
| 285 | ret = drm_sched_job_init(&job->base, entity, NULL); | ||
| 286 | if (ret) { | ||
| 287 | mutex_unlock(&pfdev->sched_lock); | ||
| 288 | goto unlock; | ||
| 289 | } | ||
| 290 | 283 | ||
| 291 | job->render_done_fence = dma_fence_get(&job->base.s_fence->finished); | 284 | job->render_done_fence = dma_fence_get(&job->base.s_fence->finished); |
| 292 | 285 | ||
| 293 | ret = panfrost_acquire_object_fences(job->bos, job->bo_count, | 286 | ret = panfrost_acquire_object_fences(job->bos, job->bo_count, |
| 294 | &job->deps); | 287 | &job->base); |
| 295 | if (ret) { | 288 | if (ret) { |
| 296 | mutex_unlock(&pfdev->sched_lock); | 289 | mutex_unlock(&pfdev->sched_lock); |
| 297 | goto unlock; | 290 | goto unlock; |
| @@ -299,7 +292,7 @@ int panfrost_job_push(struct panfrost_job *job) · 1 unmodified line | |||
| 299 | 292 | ||
| 300 | kref_get(&job->refcount); /* put by scheduler job completion */ | 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 | mutex_unlock(&pfdev->sched_lock); | 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 | struct panfrost_job *job = container_of(ref, struct panfrost_job, | 310 | struct panfrost_job *job = container_of(ref, struct panfrost_job, |
| 318 | refcount); | 311 | refcount); |
| 319 | struct dma_fence *fence; | ||
| 320 | unsigned long index; | ||
| 321 | unsigned int i; | 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 | dma_fence_put(job->done_fence); | 314 | dma_fence_put(job->done_fence); |
| 329 | dma_fence_put(job->render_done_fence); | 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 | panfrost_job_put(job); | 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 | static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job) | 352 | static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job) |
| 378 | { | 353 | { |
| 379 | struct panfrost_job *job = to_panfrost_job(sched_job); | 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 | static const struct drm_sched_backend_ops panfrost_sched_ops = { | 740 | static const struct drm_sched_backend_ops panfrost_sched_ops = { |
| 766 | .dependency = panfrost_job_dependency, | ||
| 767 | .run_job = panfrost_job_run, | 741 | .run_job = panfrost_job_run, |
| 768 | .timedout_job = panfrost_job_timedout, | 742 | .timedout_job = panfrost_job_timedout, |
| 769 | .free_job = panfrost_job_free | 743 | .free_job = panfrost_job_free |
| @@ -19,10 +19,6 @@ struct panfrost_job { · 18 unmodified lines | |||
| 19 | struct panfrost_device *pfdev; | 19 | struct panfrost_device *pfdev; |
| 20 | struct panfrost_file_priv *file_priv; | 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 | /* Fence to be signaled by IRQ handler when the job is complete. */ | 22 | /* Fence to be signaled by IRQ handler when the job is complete. */ |
| 27 | struct dma_fence *done_fence; | 23 | struct dma_fence *done_fence; |
| 28 | 24 | ||
| @@ -42,6 +38,7 @@ int panfrost_job_init(struct panfrost_device *pfdev); · 13 unmodified lines | |||
| 42 | void panfrost_job_fini(struct panfrost_device *pfdev); | 38 | void panfrost_job_fini(struct panfrost_device *pfdev); |
| 43 | int panfrost_job_open(struct panfrost_file_priv *panfrost_priv); | 39 | int panfrost_job_open(struct panfrost_file_priv *panfrost_priv); |
| 44 | void panfrost_job_close(struct panfrost_file_priv *panfrost_priv); | 40 | void panfrost_job_close(struct panfrost_file_priv *panfrost_priv); |
| 41 | int panfrost_job_get_slot(struct panfrost_job *job); | ||
| 45 | int panfrost_job_push(struct panfrost_job *job); | 42 | int panfrost_job_push(struct panfrost_job *job); |
| 46 | void panfrost_job_put(struct panfrost_job *job); | 43 | void panfrost_job_put(struct panfrost_job *job); |
| 47 | void panfrost_job_enable_interrupts(struct panfrost_device *pfdev); | 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 | region |= region_width; | 71 | region |= region_width; |
| 72 | 72 | ||
| 73 | /* Lock the region that needs to be updated */ | 73 | /* Lock the region that needs to be updated */ |
| 74 | mmu_write(pfdev, AS_LOCKADDR_LO(as_nr), region & 0xFFFFFFFFUL); | 74 | mmu_write(pfdev, AS_LOCKADDR_LO(as_nr), lower_32_bits(region)); |
| 75 | mmu_write(pfdev, AS_LOCKADDR_HI(as_nr), (region >> 32) & 0xFFFFFFFFUL); | 75 | mmu_write(pfdev, AS_LOCKADDR_HI(as_nr), upper_32_bits(region)); |
| 76 | write_cmd(pfdev, as_nr, AS_COMMAND_LOCK); | 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 | mmu_hw_do_operation_locked(pfdev, as_nr, 0, ~0ULL, AS_COMMAND_FLUSH_MEM); | 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); | 117 | mmu_write(pfdev, AS_TRANSTAB_LO(as_nr), lower_32_bits(transtab)); |
| 118 | mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), transtab >> 32); | 118 | mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), upper_32_bits(transtab)); |
| 119 | 119 | ||
| 120 | /* Need to revisit mem attrs. | 120 | /* Need to revisit mem attrs. |
| 121 | * NC is the default, Mali driver is inner WT. | 121 | * NC is the default, Mali driver is inner WT. |
| 122 | */ | 122 | */ |
| 123 | mmu_write(pfdev, AS_MEMATTR_LO(as_nr), memattr & 0xffffffffUL); | 123 | mmu_write(pfdev, AS_MEMATTR_LO(as_nr), lower_32_bits(memattr)); |
| 124 | mmu_write(pfdev, AS_MEMATTR_HI(as_nr), memattr >> 32); | 124 | mmu_write(pfdev, AS_MEMATTR_HI(as_nr), upper_32_bits(memattr)); |
| 125 | 125 | ||
| 126 | write_cmd(pfdev, as_nr, AS_COMMAND_UPDATE); | 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 | reinit_completion(&pfdev->perfcnt->dump_comp); | 52 | reinit_completion(&pfdev->perfcnt->dump_comp); |
| 53 | gpuva = pfdev->perfcnt->mapping->mmnode.start << PAGE_SHIFT; | 53 | gpuva = pfdev->perfcnt->mapping->mmnode.start << PAGE_SHIFT; |
| 54 | gpu_write(pfdev, GPU_PERFCNT_BASE_LO, gpuva); | 54 | gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva)); |
| 55 | gpu_write(pfdev, GPU_PERFCNT_BASE_HI, gpuva >> 32); | 55 | gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva)); |
| 56 | gpu_write(pfdev, GPU_INT_CLEAR, | 56 | gpu_write(pfdev, GPU_INT_CLEAR, |
| 57 | GPU_IRQ_CLEAN_CACHES_COMPLETED | | 57 | GPU_IRQ_CLEAN_CACHES_COMPLETED | |
| 58 | GPU_IRQ_PERFCNT_SAMPLE_COMPLETED); | 58 | GPU_IRQ_PERFCNT_SAMPLE_COMPLETED); |
| @@ -36,10 +36,10 @@ · 35 unmodified lines | |||
| 36 | /* manage releaseables */ | 36 | /* manage releaseables */ |
| 37 | /* stack them 16 high for now -drawable object is 191 */ | 37 | /* stack them 16 high for now -drawable object is 191 */ |
| 38 | #define RELEASE_SIZE 256 | 38 | #define RELEASE_SIZE 256 |
| 39 | #define RELEASES_PER_BO (4096 / RELEASE_SIZE) | 39 | #define RELEASES_PER_BO (PAGE_SIZE / RELEASE_SIZE) |
| 40 | /* put an alloc/dealloc surface cmd into one bo and round up to 128 */ | 40 | /* put an alloc/dealloc surface cmd into one bo and round up to 128 */ |
| 41 | #define SURFACE_RELEASE_SIZE 128 | 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 | static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE }; | 44 | static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE }; |
| 45 | static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO }; | 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 | static void qxl_ttm_backend_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) | 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 | ttm_tt_fini(ttm); | 104 | ttm_tt_fini(ttm); |
| 106 | kfree(ttm); | 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 | for (i = 0; i < pages; i++) { | 99 | for (i = 0; i < pages; i++) { |
| 100 | if (!entry->busaddr[i]) | 100 | if (!entry->busaddr[i]) |
| 101 | break; | 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 | if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) | 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 | if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { | 135 | if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { |
| 135 | DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); | 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 | DRM_ERROR("fail to set dma mask to 0x%Lx\n", | 139 | DRM_ERROR("fail to set dma mask to 0x%Lx\n", |
| 139 | (unsigned long long)gart_info->table_mask); | 140 | (unsigned long long)gart_info->table_mask); |
| 140 | ret = -EFAULT; | 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 | gart_idx = 0; | 174 | gart_idx = 0; |
| 174 | for (i = 0; i < pages; i++) { | 175 | for (i = 0; i < pages; i++) { |
| 175 | /* we need to support large memory configurations */ | 176 | /* we need to support large memory configurations */ |
| 176 | entry->busaddr[i] = pci_map_page(pdev, entry->pagelist[i], | 177 | entry->busaddr[i] = dma_map_page(&pdev->dev, entry->pagelist[i], |
| 177 | 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | 178 | 0, PAGE_SIZE, DMA_BIDIRECTIONAL); |
| 178 | if (pci_dma_mapping_error(pdev, entry->busaddr[i])) { | 179 | if (dma_mapping_error(&pdev->dev, entry->busaddr[i])) { |
| 179 | DRM_ERROR("unable to map PCIGART pages!\n"); | 180 | DRM_ERROR("unable to map PCIGART pages!\n"); |
| 180 | drm_ati_pcigart_cleanup(dev, gart_info); | 181 | drm_ati_pcigart_cleanup(dev, gart_info); |
| 181 | address = NULL; | 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 | seq = atomic64_read(&fence->rdev->fence_drv[fence->ring].last_seq); | 177 | seq = atomic64_read(&fence->rdev->fence_drv[fence->ring].last_seq); |
| 178 | if (seq >= fence->seq) { | 178 | if (seq >= fence->seq) { |
| 179 | int ret = dma_fence_signal_locked(&fence->base); | 179 | 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 | |||
| 186 | radeon_irq_kms_sw_irq_put(fence->rdev, fence->ring); | 180 | radeon_irq_kms_sw_irq_put(fence->rdev, fence->ring); |
| 187 | __remove_wait_queue(&fence->rdev->fence_queue, &fence->fence_wake); | 181 | __remove_wait_queue(&fence->rdev->fence_queue, &fence->fence_wake); |
| 188 | dma_fence_put(&fence->base); | 182 | dma_fence_put(&fence->base); |
| 189 | } else | 183 | } |
| 190 | DMA_FENCE_TRACE(&fence->base, "pending\n"); | ||
| 191 | return 0; | 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 | fence->fence_wake.func = radeon_fence_check_signaled; | 415 | fence->fence_wake.func = radeon_fence_check_signaled; |
| 423 | __add_wait_queue(&rdev->fence_queue, &fence->fence_wake); | 416 | __add_wait_queue(&rdev->fence_queue, &fence->fence_wake); |
| 424 | dma_fence_get(f); | 417 | dma_fence_get(f); |
| 425 | |||
| 426 | DMA_FENCE_TRACE(&fence->base, "armed on ring %i!\n", fence->ring); | ||
| 427 | return true; | 418 | return true; |
| 428 | } | 419 | } |
| 429 | 420 | ||
| @@ -441,11 +432,7 @@ bool radeon_fence_signaled(struct radeon_fence *fence) · 11 unmodified lines | |||
| 441 | return true; | 432 | return true; |
| 442 | 433 | ||
| 443 | if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) { | 434 | if (radeon_fence_seq_signaled(fence->rdev, fence->seq, fence->ring)) { |
| 444 | int ret; | 435 | dma_fence_signal(&fence->base); |
| 445 | |||
| 446 | ret = dma_fence_signal(&fence->base); | ||
| 447 | if (!ret) | ||
| 448 | DMA_FENCE_TRACE(&fence->base, "signaled from radeon_fence_signaled\n"); | ||
| 449 | return true; | 436 | return true; |
| 450 | } | 437 | } |
| 451 | return false; | 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 | uint64_t seq[RADEON_NUM_RINGS] = {}; | 538 | uint64_t seq[RADEON_NUM_RINGS] = {}; |
| 552 | long r; | 539 | long r; |
| 553 | int r_sig; | ||
| 554 | 540 | ||
| 555 | /* | 541 | /* |
| 556 | * This function should not be called on !radeon fences. | 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 | return r; | 553 | return r; |
| 568 | } | 554 | } |
| 569 | 555 | ||
| 570 | r_sig = dma_fence_signal(&fence->base); | 556 | dma_fence_signal(&fence->base); |
| 571 | if (!r_sig) | ||
| 572 | DMA_FENCE_TRACE(&fence->base, "signaled from fence_wait\n"); | ||
| 573 | return r; | 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 | struct radeon_ttm_tt *gtt = (void *)ttm; | 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 | ttm_tt_fini(>t->ttm); | 491 | ttm_tt_fini(>t->ttm); |
| 495 | kfree(gtt); | 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 | struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm); | 571 | struct radeon_ttm_tt *gtt = radeon_ttm_tt_to_gtt(rdev, ttm); |
| 575 | bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); | 572 | bool slave = !!(ttm->page_flags & TTM_PAGE_FLAG_SG); |
| 576 | 573 | ||
| 574 | radeon_ttm_tt_unbind(bdev, ttm); | ||
| 575 | |||
| 577 | if (gtt && gtt->userptr) { | 576 | if (gtt && gtt->userptr) { |
| 578 | kfree(ttm->sg); | 577 | kfree(ttm->sg); |
| 579 | ttm->page_flags &= ~TTM_PAGE_FLAG_SG; | 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 | struct radeon_device *rdev = radeon_get_rdev(bdev); | 650 | struct radeon_device *rdev = radeon_get_rdev(bdev); |
| 652 | 651 | ||
| 653 | if (rdev->flags & RADEON_IS_AGP) { | 652 | if (rdev->flags & RADEON_IS_AGP) { |
| 654 | ttm_agp_unbind(ttm); | ||
| 655 | ttm_tt_destroy_common(bdev, ttm); | ||
| 656 | ttm_agp_destroy(ttm); | 653 | ttm_agp_destroy(ttm); |
| 657 | return; | 654 | return; |
| 658 | } | 655 | } |
| @@ -9,7 +9,6 @@ config DRM_ROCKCHIP · 8 unmodified lines | |||
| 9 | select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP | 9 | select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP |
| 10 | select DRM_DW_HDMI if ROCKCHIP_DW_HDMI | 10 | select DRM_DW_HDMI if ROCKCHIP_DW_HDMI |
| 11 | select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI | 11 | select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI |
| 12 | select DRM_RGB if ROCKCHIP_RGB | ||
| 13 | select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI | 12 | select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI |
| 14 | select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI | 13 | select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI |
| 15 | select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC | 14 | select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC |
| @@ -45,8 +45,14 @@ · 44 unmodified lines | |||
| 45 | * @guilty: atomic_t set to 1 when a job on this queue | 45 | * @guilty: atomic_t set to 1 when a job on this queue |
| 46 | * is found to be guilty causing a timeout | 46 | * is found to be guilty causing a timeout |
| 47 | * | 47 | * |
| 48 | * Note: the sched_list should have at least one element to schedule | 48 | * Note that the &sched_list must have at least one element to schedule the entity. |
| 49 | * 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 | * Returns 0 on success or a negative error code on failure. | 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 | * @sched_list: the list of new drm scheds which will replace | 98 | * @sched_list: the list of new drm scheds which will replace |
| 93 | * existing entity->sched_list | 99 | * existing entity->sched_list |
| 94 | * @num_sched_list: number of drm sched in sched_list | 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 | void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, | 107 | void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, |
| 97 | struct drm_gpu_scheduler **sched_list, | 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 | EXPORT_SYMBOL(drm_sched_entity_modify_sched); | 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 | static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity) | 118 | static bool drm_sched_entity_is_idle(struct drm_sched_entity *entity) |
| 115 | { | 119 | { |
| 116 | rmb(); /* for list_empty to work without lock */ | 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 | return false; | 127 | return false; |
| 124 | } | 128 | } |
| 125 | 129 | ||
| 126 | /** | 130 | /* Return true if entity could provide a job. */ |
| 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 | */ | ||
| 133 | bool drm_sched_entity_is_ready(struct drm_sched_entity *entity) | 131 | bool drm_sched_entity_is_ready(struct drm_sched_entity *entity) |
| 134 | { | 132 | { |
| 135 | if (spsc_queue_peek(&entity->job_queue) == NULL) | 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 | EXPORT_SYMBOL(drm_sched_entity_flush); | 191 | EXPORT_SYMBOL(drm_sched_entity_flush); |
| 194 | 192 | ||
| 195 | /** | 193 | /* Signal the scheduler finished fence when the entity in question is killed. */ |
| 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 | */ | ||
| 203 | static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f, | 194 | static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f, |
| 204 | struct dma_fence_cb *cb) | 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 | job->sched->ops->free_job(job); | 202 | job->sched->ops->free_job(job); |
| 212 | } | 203 | } |
| 213 | 204 | ||
| 214 | /** | 205 | static struct dma_fence * |
| 215 | * drm_sched_entity_kill_jobs - Make sure all remaining jobs are killed | 206 | drm_sched_job_dependency(struct drm_sched_job *job, |
| 216 | * | 207 | struct drm_sched_entity *entity) |
| 217 | * @entity: entity which is cleaned up | 208 | { |
| 218 | * | 209 | if (!xa_empty(&job->dependencies)) |
| 219 | * Makes sure that all remaining jobs in an entity are killed before it is | 210 | return xa_erase(&job->dependencies, job->last_dependency++); |
| 220 | * destroyed. | 211 | |
| 221 | */ | 212 | if (job->sched->ops->dependency) |
| 213 | return job->sched->ops->dependency(job, entity); | ||
| 214 | |||
| 215 | return NULL; | ||
| 216 | } | ||
| 217 | |||
| 222 | static void drm_sched_entity_kill_jobs(struct drm_sched_entity *entity) | 218 | static void drm_sched_entity_kill_jobs(struct drm_sched_entity *entity) |
| 223 | { | 219 | { |
| 224 | struct drm_sched_job *job; | 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 | struct drm_sched_fence *s_fence = job->s_fence; | 225 | struct drm_sched_fence *s_fence = job->s_fence; |
| 230 | 226 | ||
| 231 | /* Wait for all dependencies to avoid data corruptions */ | 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 | dma_fence_wait(f, false); | 229 | dma_fence_wait(f, false); |
| 234 | 230 | ||
| 235 | drm_sched_fence_scheduled(s_fence); | 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 | * @entity: scheduler entity | 257 | * @entity: scheduler entity |
| 262 | * | 258 | * |
| 263 | * This should be called after @drm_sched_entity_do_release. It goes over the | 259 | * Cleanups up @entity which has been initialized by drm_sched_entity_init(). |
| 264 | * entity and signals all jobs with an error code if the process was killed. | ||
| 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 | void drm_sched_entity_fini(struct drm_sched_entity *entity) | 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 | * drm_sched_entity_destroy - Destroy a context entity | 302 | * drm_sched_entity_destroy - Destroy a context entity |
| 305 | * | ||
| 306 | * @entity: scheduler entity | 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 | void drm_sched_entity_destroy(struct drm_sched_entity *entity) | 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 | EXPORT_SYMBOL(drm_sched_entity_destroy); | 313 | EXPORT_SYMBOL(drm_sched_entity_destroy); |
| 316 | 314 | ||
| 317 | /* | 315 | /* drm_sched_entity_clear_dep - callback to clear the entities dependency */ |
| 318 | * drm_sched_entity_clear_dep - callback to clear the entities dependency | ||
| 319 | */ | ||
| 320 | static void drm_sched_entity_clear_dep(struct dma_fence *f, | 316 | static void drm_sched_entity_clear_dep(struct dma_fence *f, |
| 321 | struct dma_fence_cb *cb) | 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 | EXPORT_SYMBOL(drm_sched_entity_set_priority); | 355 | EXPORT_SYMBOL(drm_sched_entity_set_priority); |
| 360 | 356 | ||
| 361 | /** | 357 | /* |
| 362 | * drm_sched_entity_add_dependency_cb - add callback for the entities dependency | ||
| 363 | * | ||
| 364 | * @entity: entity with dependency | ||
| 365 | * | ||
| 366 | * Add a callback to the current dependency of the entity to wake up the | 358 | * Add a callback to the current dependency of the entity to wake up the |
| 367 | * scheduler when the entity becomes available. | 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 | return false; | 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 | struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) | 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 | struct drm_sched_job *sched_job; | 407 | struct drm_sched_job *sched_job; |
| 424 | 408 | ||
| 425 | sched_job = to_drm_sched_job(spsc_queue_peek(&entity->job_queue)); | 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 | return NULL; | 411 | return NULL; |
| 428 | 412 | ||
| 429 | while ((entity->dependency = | 413 | while ((entity->dependency = |
| 430 | sched->ops->dependency(sched_job, entity))) { | 414 | drm_sched_job_dependency(sched_job, entity))) { |
| 431 | trace_drm_sched_job_wait_dep(sched_job, entity->dependency); | 415 | trace_drm_sched_job_wait_dep(sched_job, entity->dependency); |
| 432 | 416 | ||
| 433 | if (drm_sched_entity_add_dependency_cb(entity)) | 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 | dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED); | 423 | dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED); |
| 440 | 424 | ||
| 441 | dma_fence_put(entity->last_scheduled); | 425 | dma_fence_put(entity->last_scheduled); |
| 426 | |||
| 442 | entity->last_scheduled = dma_fence_get(&sched_job->s_fence->finished); | 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 | spsc_queue_pop(&entity->job_queue); | 436 | spsc_queue_pop(&entity->job_queue); |
| 445 | return sched_job; | 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 | void drm_sched_entity_select_rq(struct drm_sched_entity *entity) | 440 | void drm_sched_entity_select_rq(struct drm_sched_entity *entity) |
| 457 | { | 441 | { |
| 458 | struct dma_fence *fence; | 442 | struct dma_fence *fence; |
| 459 | struct drm_gpu_scheduler *sched; | 443 | struct drm_gpu_scheduler *sched; |
| 460 | struct drm_sched_rq *rq; | 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 | return; | 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 | if (fence && !dma_fence_is_signaled(fence)) | 465 | if (fence && !dma_fence_is_signaled(fence)) |
| 467 | return; | 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 | * drm_sched_entity_push_job - Submit a job to the entity's job queue | 482 | * drm_sched_entity_push_job - Submit a job to the entity's job queue |
| 484 | * | ||
| 485 | * @sched_job: job to submit | 483 | * @sched_job: job to submit |
| 486 | * @entity: scheduler entity | ||
| 487 | * | 484 | * |
| 488 | * Note: To guarantee that the order of insertion to queue matches | 485 | * Note: To guarantee that the order of insertion to queue matches the job's |
| 489 | * the job's fence sequence number this function should be | 486 | * fence sequence number this function should be called with drm_sched_job_arm() |
| 490 | * called with drm_sched_job_init under common lock. | 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 | * Returns 0 for success, negative error code otherwise. | 490 | * Returns 0 for success, negative error code otherwise. |
| 493 | */ | 491 | */ |
| 494 | void drm_sched_entity_push_job(struct drm_sched_job *sched_job, | 492 | void drm_sched_entity_push_job(struct drm_sched_job *sched_job) |
| 495 | struct drm_sched_entity *entity) | ||
| 496 | { | 493 | { |
| 494 | struct drm_sched_entity *entity = sched_job->entity; | ||
| 497 | bool first; | 495 | bool first; |
| 498 | 496 | ||
| 499 | trace_drm_sched_job(sched_job, entity); | 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 | void drm_sched_fence_scheduled(struct drm_sched_fence *fence) | 51 | void drm_sched_fence_scheduled(struct drm_sched_fence *fence) |
| 52 | { | 52 | { |
| 53 | int ret = dma_fence_signal(&fence->scheduled); | 53 | 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"); | ||
| 61 | } | 54 | } |
| 62 | 55 | ||
| 63 | void drm_sched_fence_finished(struct drm_sched_fence *fence) | 56 | void drm_sched_fence_finished(struct drm_sched_fence *fence) |
| 64 | { | 57 | { |
| 65 | int ret = dma_fence_signal(&fence->finished); | 58 | 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"); | ||
| 73 | } | 59 | } |
| 74 | 60 | ||
| 75 | static const char *drm_sched_fence_get_driver_name(struct dma_fence *fence) | 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 | return (const char *)fence->sched->name; | 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); | 91 | /* This function should not be called if the fence has been initialized. */ |
| 96 | struct drm_sched_fence *fence = to_drm_sched_fence(f); | 92 | if (!WARN_ON_ONCE(fence->sched)) |
| 97 | 93 | kmem_cache_free(sched_fence_slab, fence); | |
| 98 | 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 | struct drm_sched_fence *fence = to_drm_sched_fence(f); | 106 | struct drm_sched_fence *fence = to_drm_sched_fence(f); |
| 112 | 107 | ||
| 113 | dma_fence_put(fence->parent); | 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 | EXPORT_SYMBOL(to_drm_sched_fence); | 148 | EXPORT_SYMBOL(to_drm_sched_fence); |
| 154 | 149 | ||
| 155 | struct drm_sched_fence *drm_sched_fence_create(struct drm_sched_entity *entity, | 150 | struct drm_sched_fence *drm_sched_fence_alloc(struct drm_sched_entity *entity, |
| 156 | void *owner) | 151 | void *owner) |
| 157 | { | 152 | { |
| 158 | struct drm_sched_fence *fence = NULL; | 153 | struct drm_sched_fence *fence = NULL; |
| 159 | unsigned seq; | ||
| 160 | 154 | ||
| 161 | fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL); | 155 | fence = kmem_cache_zalloc(sched_fence_slab, GFP_KERNEL); |
| 162 | if (fence == NULL) | 156 | if (fence == NULL) |
| 163 | return NULL; | 157 | return NULL; |
| 164 | 158 | ||
| 165 | fence->owner = owner; | 159 | fence->owner = owner; |
| 166 | fence->sched = entity->rq->sched; | ||
| 167 | spin_lock_init(&fence->lock); | 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 | seq = atomic_inc_return(&entity->fence_seq); | 171 | seq = atomic_inc_return(&entity->fence_seq); |
| 170 | dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled, | 172 | dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled, |
| 171 | &fence->lock, entity->fence_context, seq); | 173 | &fence->lock, entity->fence_context, seq); |
| 172 | dma_fence_init(&fence->finished, &drm_sched_fence_ops_finished, | 174 | dma_fence_init(&fence->finished, &drm_sched_fence_ops_finished, |
| 173 | &fence->lock, entity->fence_context + 1, seq); | 175 | &fence->lock, entity->fence_context + 1, seq); |
| 174 | |||
| 175 | return fence; | ||
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | module_init(drm_sched_fence_slab_init); | 178 | module_init(drm_sched_fence_slab_init); |
| @@ -48,9 +48,11 @@ · 47 unmodified lines | |||
| 48 | #include <linux/wait.h> | 48 | #include <linux/wait.h> |
| 49 | #include <linux/sched.h> | 49 | #include <linux/sched.h> |
| 50 | #include <linux/completion.h> | 50 | #include <linux/completion.h> |
| 51 | #include <linux/dma-resv.h> | ||
| 51 | #include <uapi/linux/sched/types.h> | 52 | #include <uapi/linux/sched/types.h> |
| 52 | 53 | ||
| 53 | #include <drm/drm_print.h> | 54 | #include <drm/drm_print.h> |
| 55 | #include <drm/drm_gem.h> | ||
| 54 | #include <drm/gpu_scheduler.h> | 56 | #include <drm/gpu_scheduler.h> |
| 55 | #include <drm/spsc_queue.h> | 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 | * drm_sched_job_init - init a scheduler job | 568 | * drm_sched_job_init - init a scheduler job |
| 567 | * | ||
| 568 | * @job: scheduler job to init | 569 | * @job: scheduler job to init |
| 569 | * @entity: scheduler entity to use | 570 | * @entity: scheduler entity to use |
| 570 | * @owner: job owner for debugging | 571 | * @owner: job owner for debugging |
| @@ -572,43 +573,193 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs_ext); · 1 unmodified line | |||
| 572 | * Refer to drm_sched_entity_push_job() documentation | 573 | * Refer to drm_sched_entity_push_job() documentation |
| 573 | * for locking considerations. | 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 | * Returns 0 for success, negative error code otherwise. | 584 | * Returns 0 for success, negative error code otherwise. |
| 576 | */ | 585 | */ |
| 577 | int drm_sched_job_init(struct drm_sched_job *job, | 586 | int drm_sched_job_init(struct drm_sched_job *job, |
| 578 | struct drm_sched_entity *entity, | 587 | struct drm_sched_entity *entity, |
| 579 | void *owner) | 588 | void *owner) |
| 580 | { | 589 | { |
| 581 | struct drm_gpu_scheduler *sched; | ||
| 582 | |||
| 583 | drm_sched_entity_select_rq(entity); | 590 | drm_sched_entity_select_rq(entity); |
| 584 | if (!entity->rq) | 591 | if (!entity->rq) |
| 585 | return -ENOENT; | 592 | return -ENOENT; |
| 586 | 593 | ||
| 587 | sched = entity->rq->sched; | ||
| 588 | |||
| 589 | job->sched = sched; | ||
| 590 | job->entity = entity; | 594 | job->entity = entity; |
| 591 | job->s_priority = entity->rq - sched->sched_rq; | 595 | job->s_fence = drm_sched_fence_alloc(entity, owner); |
| 592 | job->s_fence = drm_sched_fence_create(entity, owner); | ||
| 593 | if (!job->s_fence) | 596 | if (!job->s_fence) |
| 594 | return -ENOMEM; | 597 | return -ENOMEM; |
| 595 | job->id = atomic64_inc_return(&sched->job_id_count); | ||
| 596 | 598 | ||
| 597 | INIT_LIST_HEAD(&job->list); | 599 | INIT_LIST_HEAD(&job->list); |
| 598 | 600 | ||
| 601 | xa_init_flags(&job->dependencies, XA_FLAGS_ALLOC); | ||
| 602 | |||
| 599 | return 0; | 603 | return 0; |
| 600 | } | 604 | } |
| 601 | EXPORT_SYMBOL(drm_sched_job_init); | 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 | * @job: scheduler job to clean up | 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 | void drm_sched_job_cleanup(struct drm_sched_job *job) | 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 | job->s_fence = NULL; | 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 | EXPORT_SYMBOL(drm_sched_job_cleanup); | 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 | struct drm_sched_job *job, *next; | 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 | spin_lock(&sched->job_list_lock); | 830 | spin_lock(&sched->job_list_lock); |
| 689 | 831 | ||
| 690 | job = list_first_entry_or_null(&sched->pending_list, | 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 | if (job && dma_fence_is_signaled(&job->s_fence->finished)) { | 835 | if (job && dma_fence_is_signaled(&job->s_fence->finished)) { |
| 694 | /* remove job from pending_list */ | 836 | /* remove job from pending_list */ |
| 695 | list_del_init(&job->list); | 837 | list_del_init(&job->list); |
| 838 | |||
| 839 | /* cancel this job's TO timer */ | ||
| 840 | cancel_delayed_work(&sched->work_tdr); | ||
| 696 | /* make the scheduled timestamp more accurate */ | 841 | /* make the scheduled timestamp more accurate */ |
| 697 | next = list_first_entry_or_null(&sched->pending_list, | 842 | next = list_first_entry_or_null(&sched->pending_list, |
| 698 | typeof(*next), list); | 843 | typeof(*next), list); |
| 699 | if (next) | 844 | |
| 845 | if (next) { | ||
| 700 | next->s_fence->scheduled.timestamp = | 846 | next->s_fence->scheduled.timestamp = |
| 701 | job->s_fence->finished.timestamp; | 847 | job->s_fence->finished.timestamp; |
| 702 | 848 | /* start TO timer for next job */ | |
| 849 | drm_sched_start_timeout(sched); | ||
| 850 | } | ||
| 703 | } else { | 851 | } else { |
| 704 | job = NULL; | 852 | job = NULL; |
| 705 | /* queue timeout for next job */ | ||
| 706 | drm_sched_start_timeout(sched); | ||
| 707 | } | 853 | } |
| 708 | 854 | ||
| 709 | spin_unlock(&sched->job_list_lock); | 855 | spin_unlock(&sched->job_list_lock); |
| @@ -791,11 +937,8 @@ static int drm_sched_main(void *param) · 81 unmodified lines | |||
| 791 | (entity = drm_sched_select_entity(sched))) || | 937 | (entity = drm_sched_select_entity(sched))) || |
| 792 | kthread_should_stop()); | 938 | kthread_should_stop()); |
| 793 | 939 | ||
| 794 | if (cleanup_job) { | 940 | if (cleanup_job) |
| 795 | sched->ops->free_job(cleanup_job); | 941 | sched->ops->free_job(cleanup_job); |
| 796 | /* queue timeout for next job */ | ||
| 797 | drm_sched_start_timeout(sched); | ||
| 798 | } | ||
| 799 | 942 | ||
| 800 | if (!entity) | 943 | if (!entity) |
| 801 | continue; | 944 | continue; |
| @@ -845,7 +845,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, · 844 unmodified lines | |||
| 845 | LXCFBLR_CFBLL | LXCFBLR_CFBP, val); | 845 | LXCFBLR_CFBLL | LXCFBLR_CFBP, val); |
| 846 | 846 | ||
| 847 | /* Specifies the constant alpha value */ | 847 | /* Specifies the constant alpha value */ |
| 848 | val = CONSTA_MAX; | 848 | val = newstate->alpha >> 8; |
| 849 | reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val); | 849 | reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val); |
| 850 | 850 | ||
| 851 | /* Specifies the blending factors */ | 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 | drm_plane_helper_add(plane, <dc_plane_helper_funcs); | 998 | drm_plane_helper_add(plane, <dc_plane_helper_funcs); |
| 999 | 999 | ||
| 1000 | drm_plane_create_alpha_property(plane); | ||
| 1001 | |||
| 1000 | DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id); | 1002 | DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id); |
| 1001 | 1003 | ||
| 1002 | return plane; | 1004 | return plane; |
| @@ -1024,6 +1026,8 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) · 21 unmodified lines | |||
| 1024 | return -EINVAL; | 1026 | return -EINVAL; |
| 1025 | } | 1027 | } |
| 1026 | 1028 | ||
| 1029 | drm_plane_create_zpos_immutable_property(primary, 0); | ||
| 1030 | |||
| 1027 | ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, | 1031 | ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, |
| 1028 | <dc_crtc_funcs, NULL); | 1032 | <dc_crtc_funcs, NULL); |
| 1029 | if (ret) { | 1033 | if (ret) { |
| @@ -1046,6 +1050,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) · 16 unmodified lines | |||
| 1046 | DRM_ERROR("Can not create overlay plane %d\n", i); | 1050 | DRM_ERROR("Can not create overlay plane %d\n", i); |
| 1047 | goto cleanup; | 1051 | goto cleanup; |
| 1048 | } | 1052 | } |
| 1053 | drm_plane_create_zpos_immutable_property(overlay, i); | ||
| 1049 | } | 1054 | } |
| 1050 | 1055 | ||
| 1051 | return 0; | 1056 | return 0; |
| @@ -782,7 +782,6 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, · 781 unmodified lines | |||
| 782 | struct sun4i_drv *drv = drm->dev_private; | 782 | struct sun4i_drv *drv = drm->dev_private; |
| 783 | struct sun4i_backend *backend; | 783 | struct sun4i_backend *backend; |
| 784 | const struct sun4i_backend_quirks *quirks; | 784 | const struct sun4i_backend_quirks *quirks; |
| 785 | struct resource *res; | ||
| 786 | void __iomem *regs; | 785 | void __iomem *regs; |
| 787 | int i, ret; | 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 | if (IS_ERR(backend->frontend)) | 814 | if (IS_ERR(backend->frontend)) |
| 816 | dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n"); | 815 | dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n"); |
| 817 | 816 | ||
| 818 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 817 | regs = devm_platform_ioremap_resource(pdev, 0); |
| 819 | regs = devm_ioremap_resource(dev, res); | ||
| 820 | if (IS_ERR(regs)) | 818 | if (IS_ERR(regs)) |
| 821 | return PTR_ERR(regs); | 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 | struct sun4i_frontend *frontend; | 561 | struct sun4i_frontend *frontend; |
| 562 | struct drm_device *drm = data; | 562 | struct drm_device *drm = data; |
| 563 | struct sun4i_drv *drv = drm->dev_private; | 563 | struct sun4i_drv *drv = drm->dev_private; |
| 564 | struct resource *res; | ||
| 565 | void __iomem *regs; | 564 | void __iomem *regs; |
| 566 | 565 | ||
| 567 | frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL); | 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 | if (!frontend->data) | 575 | if (!frontend->data) |
| 577 | return -ENODEV; | 576 | return -ENODEV; |
| 578 | 577 | ||
| 579 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 578 | regs = devm_platform_ioremap_resource(pdev, 0); |
| 580 | regs = devm_ioremap_resource(dev, res); | ||
| 581 | if (IS_ERR(regs)) | 579 | if (IS_ERR(regs)) |
| 582 | return PTR_ERR(regs); | 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 | struct cec_connector_info conn_info; | 489 | struct cec_connector_info conn_info; |
| 490 | struct sun4i_drv *drv = drm->dev_private; | 490 | struct sun4i_drv *drv = drm->dev_private; |
| 491 | struct sun4i_hdmi *hdmi; | 491 | struct sun4i_hdmi *hdmi; |
| 492 | struct resource *res; | ||
| 493 | u32 reg; | 492 | u32 reg; |
| 494 | int ret; | 493 | int ret; |
| 495 | 494 | ||
| @@ -504,8 +503,7 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, · 8 unmodified lines | |||
| 504 | if (!hdmi->variant) | 503 | if (!hdmi->variant) |
| 505 | return -EINVAL; | 504 | return -EINVAL; |
| 506 | 505 | ||
| 507 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 506 | hdmi->base = devm_platform_ioremap_resource(pdev, 0); |
| 508 | hdmi->base = devm_ioremap_resource(dev, res); | ||
| 509 | if (IS_ERR(hdmi->base)) { | 507 | if (IS_ERR(hdmi->base)) { |
| 510 | dev_err(dev, "Couldn't map the HDMI encoder registers\n"); | 508 | dev_err(dev, "Couldn't map the HDMI encoder registers\n"); |
| 511 | return PTR_ERR(hdmi->base); | 509 | return PTR_ERR(hdmi->base); |
| @@ -841,11 +841,9 @@ static int sun4i_tcon_init_regmap(struct device *dev, · 840 unmodified lines | |||
| 841 | struct sun4i_tcon *tcon) | 841 | struct sun4i_tcon *tcon) |
| 842 | { | 842 | { |
| 843 | struct platform_device *pdev = to_platform_device(dev); | 843 | struct platform_device *pdev = to_platform_device(dev); |
| 844 | struct resource *res; | ||
| 845 | void __iomem *regs; | 844 | void __iomem *regs; |
| 846 | 845 | ||
| 847 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 846 | regs = devm_platform_ioremap_resource(pdev, 0); |
| 848 | regs = devm_ioremap_resource(dev, res); | ||
| 849 | if (IS_ERR(regs)) | 847 | if (IS_ERR(regs)) |
| 850 | return PTR_ERR(regs); | 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 | struct drm_device *drm = data; | 538 | struct drm_device *drm = data; |
| 539 | struct sun4i_drv *drv = drm->dev_private; | 539 | struct sun4i_drv *drv = drm->dev_private; |
| 540 | struct sun4i_tv *tv; | 540 | struct sun4i_tv *tv; |
| 541 | struct resource *res; | ||
| 542 | void __iomem *regs; | 541 | void __iomem *regs; |
| 543 | int ret; | 542 | int ret; |
| 544 | 543 | ||
| @@ -548,8 +547,7 @@ static int sun4i_tv_bind(struct device *dev, struct device *master, · 3 unmodified lines | |||
| 548 | tv->drv = drv; | 547 | tv->drv = drv; |
| 549 | dev_set_drvdata(dev, tv); | 548 | dev_set_drvdata(dev, tv); |
| 550 | 549 | ||
| 551 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 550 | regs = devm_platform_ioremap_resource(pdev, 0); |
| 552 | regs = devm_ioremap_resource(dev, res); | ||
| 553 | if (IS_ERR(regs)) { | 551 | if (IS_ERR(regs)) { |
| 554 | dev_err(dev, "Couldn't map the TV encoder registers\n"); | 552 | dev_err(dev, "Couldn't map the TV encoder registers\n"); |
| 555 | return PTR_ERR(regs); | 553 | return PTR_ERR(regs); |
| @@ -1104,7 +1104,6 @@ static int sun6i_dsi_probe(struct platform_device *pdev) · 1103 unmodified lines | |||
| 1104 | struct device *dev = &pdev->dev; | 1104 | struct device *dev = &pdev->dev; |
| 1105 | const char *bus_clk_name = NULL; | 1105 | const char *bus_clk_name = NULL; |
| 1106 | struct sun6i_dsi *dsi; | 1106 | struct sun6i_dsi *dsi; |
| 1107 | struct resource *res; | ||
| 1108 | void __iomem *base; | 1107 | void __iomem *base; |
| 1109 | int ret; | 1108 | int ret; |
| 1110 | 1109 | ||
| @@ -1120,8 +1119,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev) · 9 unmodified lines | |||
| 1120 | "allwinner,sun6i-a31-mipi-dsi")) | 1119 | "allwinner,sun6i-a31-mipi-dsi")) |
| 1121 | bus_clk_name = "bus"; | 1120 | bus_clk_name = "bus"; |
| 1122 | 1121 | ||
| 1123 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1122 | base = devm_platform_ioremap_resource(pdev, 0); |
| 1124 | base = devm_ioremap_resource(dev, res); | ||
| 1125 | if (IS_ERR(base)) { | 1123 | if (IS_ERR(base)) { |
| 1126 | dev_err(dev, "Couldn't map the DSI encoder registers\n"); | 1124 | dev_err(dev, "Couldn't map the DSI encoder registers\n"); |
| 1127 | return PTR_ERR(base); | 1125 | return PTR_ERR(base); |
| @@ -16,8 +16,8 @@ struct sun8i_mixer; · 15 unmodified lines | |||
| 16 | #define CCSC10_OFFSET 0xA0000 | 16 | #define CCSC10_OFFSET 0xA0000 |
| 17 | #define CCSC11_OFFSET 0xF0000 | 17 | #define CCSC11_OFFSET 0xF0000 |
| 18 | 18 | ||
| 19 | #define SUN8I_CSC_CTRL(base) (base + 0x0) | 19 | #define SUN8I_CSC_CTRL(base) ((base) + 0x0) |
| 20 | #define SUN8I_CSC_COEFF(base, i) (base + 0x10 + 4 * i) | 20 | #define SUN8I_CSC_COEFF(base, i) ((base) + 0x10 + 4 * (i)) |
| 21 | 21 | ||
| 22 | #define SUN8I_CSC_CTRL_EN BIT(0) | 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 | struct drm_device *drm = data; | 337 | struct drm_device *drm = data; |
| 338 | struct sun4i_drv *drv = drm->dev_private; | 338 | struct sun4i_drv *drv = drm->dev_private; |
| 339 | struct sun8i_mixer *mixer; | 339 | struct sun8i_mixer *mixer; |
| 340 | struct resource *res; | ||
| 341 | void __iomem *regs; | 340 | void __iomem *regs; |
| 342 | unsigned int base; | 341 | unsigned int base; |
| 343 | int plane_cnt; | 342 | int plane_cnt; |
| @@ -390,8 +389,7 @@ static int sun8i_mixer_bind(struct device *dev, struct device *master, · 46 unmodified lines | |||
| 390 | if (!mixer->cfg) | 389 | if (!mixer->cfg) |
| 391 | return -EINVAL; | 390 | return -EINVAL; |
| 392 | 391 | ||
| 393 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 392 | regs = devm_platform_ioremap_resource(pdev, 0); |
| 394 | regs = devm_ioremap_resource(dev, res); | ||
| 395 | if (IS_ERR(regs)) | 393 | if (IS_ERR(regs)) |
| 396 | return PTR_ERR(regs); | 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 | struct clk_hw_onecell_data *clk_data; | 128 | struct clk_hw_onecell_data *clk_data; |
| 129 | struct sun8i_tcon_top *tcon_top; | 129 | struct sun8i_tcon_top *tcon_top; |
| 130 | const struct sun8i_tcon_top_quirks *quirks; | 130 | const struct sun8i_tcon_top_quirks *quirks; |
| 131 | struct resource *res; | ||
| 132 | void __iomem *regs; | 131 | void __iomem *regs; |
| 133 | int ret, i; | 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 | return PTR_ERR(tcon_top->bus); | 157 | return PTR_ERR(tcon_top->bus); |
| 159 | } | 158 | } |
| 160 | 159 | ||
| 161 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 160 | regs = devm_platform_ioremap_resource(pdev, 0); |
| 162 | regs = devm_ioremap_resource(dev, res); | ||
| 163 | tcon_top->regs = regs; | 161 | tcon_top->regs = regs; |
| 164 | if (IS_ERR(regs)) | 162 | if (IS_ERR(regs)) |
| 165 | return PTR_ERR(regs); | 163 | return PTR_ERR(regs); |
| @@ -44,7 +44,7 @@ int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, · 43 unmodified lines | |||
| 44 | { | 44 | { |
| 45 | uint64_t modifier = framebuffer->modifier; | 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 | if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0) | 48 | if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0) |
| 49 | tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA; | 49 | tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA; |
| 50 | else | 50 | else |
| @@ -113,7 +113,7 @@ static bool tegra_plane_format_mod_supported(struct drm_plane *plane, · 112 unmodified lines | |||
| 113 | return true; | 113 | return true; |
| 114 | 114 | ||
| 115 | /* check for the sector layout bit */ | 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 | if (modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) { | 117 | if (modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) { |
| 118 | if (!tegra_plane_supports_sector_layout(plane)) | 118 | if (!tegra_plane_supports_sector_layout(plane)) |
| 119 | return false; | 119 | return false; |
| @@ -44,7 +44,7 @@ config DRM_CIRRUS_QEMU · 43 unmodified lines | |||
| 44 | 44 | ||
| 45 | config DRM_GM12U320 | 45 | config DRM_GM12U320 |
| 46 | tristate "GM12U320 driver for USB projectors" | 46 | tristate "GM12U320 driver for USB projectors" |
| 47 | depends on DRM && USB | 47 | depends on DRM && USB && MMU |
| 48 | select DRM_KMS_HELPER | 48 | select DRM_KMS_HELPER |
| 49 | select DRM_GEM_SHMEM_HELPER | 49 | select DRM_GEM_SHMEM_HELPER |
| 50 | help | 50 | help |
| @@ -53,7 +53,7 @@ config DRM_GM12U320 · 2 unmodified lines | |||
| 53 | 53 | ||
| 54 | config DRM_SIMPLEDRM | 54 | config DRM_SIMPLEDRM |
| 55 | tristate "Simple framebuffer driver" | 55 | tristate "Simple framebuffer driver" |
| 56 | depends on DRM | 56 | depends on DRM && MMU |
| 57 | select DRM_GEM_SHMEM_HELPER | 57 | select DRM_GEM_SHMEM_HELPER |
| 58 | select DRM_KMS_HELPER | 58 | select DRM_KMS_HELPER |
| 59 | help | 59 | help |
| @@ -63,6 +63,7 @@ MODULE_PARM_DESC(defy, "default y resolution"); · 62 unmodified lines | |||
| 63 | 63 | ||
| 64 | enum bochs_types { | 64 | enum bochs_types { |
| 65 | BOCHS_QEMU_STDVGA, | 65 | BOCHS_QEMU_STDVGA, |
| 66 | BOCHS_SIMICS, | ||
| 66 | BOCHS_UNKNOWN, | 67 | BOCHS_UNKNOWN, |
| 67 | }; | 68 | }; |
| 68 | 69 | ||
| @@ -695,6 +696,13 @@ static const struct pci_device_id bochs_pci_tbl[] = { · 626 unmodified lines | |||
| 695 | .subdevice = PCI_ANY_ID, | 696 | .subdevice = PCI_ANY_ID, |
| 696 | .driver_data = BOCHS_UNKNOWN, | 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 | { /* end of list */ } | 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 | struct ttm_device *bdev = bo->bdev; | 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 | dma_resv_assert_held(bo->base.resv); | 108 | dma_resv_assert_held(bo->base.resv); |
| 99 | 109 | ||
| 100 | if (bo->pin_count) { | 110 | if (bo->pin_count) { |
| 101 | ttm_bo_del_from_lru(bo); | 111 | ttm_bo_move_to_pinned(bo); |
| 102 | return; | 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 | return ret; | 352 | return ret; |
| 343 | } | 353 | } |
| 344 | 354 | ||
| 345 | ttm_bo_del_from_lru(bo); | 355 | ttm_bo_move_to_pinned(bo); |
| 346 | list_del_init(&bo->ddestroy); | 356 | list_del_init(&bo->ddestroy); |
| 347 | spin_unlock(&bo->bdev->lru_lock); | 357 | spin_unlock(&bo->bdev->lru_lock); |
| 348 | ttm_bo_cleanup_memtype_use(bo); | 358 | ttm_bo_cleanup_memtype_use(bo); |
| @@ -914,57 +924,11 @@ out: · 565 unmodified lines | |||
| 914 | return ret; | 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 | int ttm_bo_validate(struct ttm_buffer_object *bo, | 927 | int ttm_bo_validate(struct ttm_buffer_object *bo, |
| 963 | struct ttm_placement *placement, | 928 | struct ttm_placement *placement, |
| 964 | struct ttm_operation_ctx *ctx) | 929 | struct ttm_operation_ctx *ctx) |
| 965 | { | 930 | { |
| 966 | int ret; | 931 | int ret; |
| 967 | uint32_t new_flags; | ||
| 968 | 932 | ||
| 969 | dma_resv_assert_held(bo->base.resv); | 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 | * Check whether we need to move buffer. | 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 | ret = ttm_bo_move_buffer(bo, placement, ctx); | 945 | ret = ttm_bo_move_buffer(bo, placement, ctx); |
| 982 | if (ret) | 946 | if (ret) |
| 983 | return ret; | 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 | return ret == -EBUSY ? -ENOSPC : ret; | 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 | /* TODO: Cleanup the locking */ | 1133 | /* TODO: Cleanup the locking */ |
| 1170 | spin_unlock(&bo->bdev->lru_lock); | 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 | if (bo->ttm == NULL) | 1188 | if (bo->ttm == NULL) |
| 1225 | return; | 1189 | return; |
| 1226 | 1190 | ||
| 1191 | ttm_tt_unpopulate(bo->bdev, bo->ttm); | ||
| 1227 | ttm_tt_destroy(bo->bdev, bo->ttm); | 1192 | ttm_tt_destroy(bo->bdev, bo->ttm); |
| 1228 | bo->ttm = NULL; | 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 | INIT_DELAYED_WORK(&bdev->wq, ttm_device_delayed_workqueue); | 220 | INIT_DELAYED_WORK(&bdev->wq, ttm_device_delayed_workqueue); |
| 221 | spin_lock_init(&bdev->lru_lock); | 221 | spin_lock_init(&bdev->lru_lock); |
| 222 | INIT_LIST_HEAD(&bdev->ddestroy); | 222 | INIT_LIST_HEAD(&bdev->ddestroy); |
| 223 | INIT_LIST_HEAD(&bdev->pinned); | ||
| 223 | bdev->dev_mapping = mapping; | 224 | bdev->dev_mapping = mapping; |
| 224 | mutex_lock(&ttm_global_mutex); | 225 | mutex_lock(&ttm_global_mutex); |
| 225 | list_add_tail(&bdev->device_list, &glob->device_list); | 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 | ttm_global_release(); | 258 | ttm_global_release(); |
| 258 | } | 259 | } |
| 259 | EXPORT_SYMBOL(ttm_device_fini); | 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 | #include "ttm_module.h" | 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 | * ttm_prot_from_caching - Modify the page protection according to the | 55 | * ttm_prot_from_caching - Modify the page protection according to the |
| 44 | * ttm cacing mode | 56 | * ttm cacing mode |
| @@ -70,7 +70,7 @@ static struct ttm_pool_type global_uncached[MAX_ORDER]; · 69 unmodified lines | |||
| 70 | static struct ttm_pool_type global_dma32_write_combined[MAX_ORDER]; | 70 | static struct ttm_pool_type global_dma32_write_combined[MAX_ORDER]; |
| 71 | static struct ttm_pool_type global_dma32_uncached[MAX_ORDER]; | 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 | static struct list_head shrinker_list; | 74 | static struct list_head shrinker_list; |
| 75 | static struct shrinker mm_shrinker; | 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 | spin_lock_init(&pt->lock); | 263 | spin_lock_init(&pt->lock); |
| 264 | INIT_LIST_HEAD(&pt->pages); | 264 | INIT_LIST_HEAD(&pt->pages); |
| 265 | 265 | ||
| 266 | mutex_lock(&shrinker_lock); | 266 | spin_lock(&shrinker_lock); |
| 267 | list_add_tail(&pt->shrinker_list, &shrinker_list); | 267 | list_add_tail(&pt->shrinker_list, &shrinker_list); |
| 268 | mutex_unlock(&shrinker_lock); | 268 | spin_unlock(&shrinker_lock); |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | /* Remove a pool_type from the global shrinker list and free all pages */ | 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 | struct page *p; | 274 | struct page *p; |
| 275 | 275 | ||
| 276 | mutex_lock(&shrinker_lock); | 276 | spin_lock(&shrinker_lock); |
| 277 | list_del(&pt->shrinker_list); | 277 | list_del(&pt->shrinker_list); |
| 278 | mutex_unlock(&shrinker_lock); | 278 | spin_unlock(&shrinker_lock); |
| 279 | 279 | ||
| 280 | while ((p = ttm_pool_type_take(pt))) | 280 | while ((p = ttm_pool_type_take(pt))) |
| 281 | ttm_pool_free_page(pt->pool, pt->caching, pt->order, p); | 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 | static unsigned int ttm_pool_shrink(void) | 313 | static unsigned int ttm_pool_shrink(void) |
| 314 | { | 314 | { |
| 315 | struct ttm_pool_type *pt; | 315 | struct ttm_pool_type *pt; |
| 316 | unsigned int num_freed; | 316 | unsigned int num_pages; |
| 317 | struct page *p; | 317 | struct page *p; |
| 318 | 318 | ||
| 319 | mutex_lock(&shrinker_lock); | 319 | spin_lock(&shrinker_lock); |
| 320 | pt = list_first_entry(&shrinker_list, typeof(*pt), shrinker_list); | 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 | p = ttm_pool_type_take(pt); | 324 | p = ttm_pool_type_take(pt); |
| 323 | if (p) { | 325 | if (p) { |
| 324 | ttm_pool_free_page(pt->pool, pt->caching, pt->order, p); | 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 | } else { | 328 | } else { |
| 327 | num_freed = 0; | 329 | num_pages = 0; |
| 328 | } | 330 | } |
| 329 | 331 | ||
| 330 | list_move_tail(&pt->shrinker_list, &shrinker_list); | 332 | return num_pages; |
| 331 | mutex_unlock(&shrinker_lock); | ||
| 332 | |||
| 333 | return num_freed; | ||
| 334 | } | 333 | } |
| 335 | 334 | ||
| 336 | /* Return the allocation order based for a page */ | 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 | for (j = 0; j < MAX_ORDER; ++j) | 530 | for (j = 0; j < MAX_ORDER; ++j) |
| 532 | ttm_pool_type_fini(&pool->caching[i].orders[j]); | 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 | /* As long as pages are available make sure to release at least one */ | 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 | ttm_pool_debugfs_header(m); | 610 | ttm_pool_debugfs_header(m); |
| 607 | 611 | ||
| 608 | mutex_lock(&shrinker_lock); | 612 | spin_lock(&shrinker_lock); |
| 609 | seq_puts(m, "wc\t:"); | 613 | seq_puts(m, "wc\t:"); |
| 610 | ttm_pool_debugfs_orders(global_write_combined, m); | 614 | ttm_pool_debugfs_orders(global_write_combined, m); |
| 611 | seq_puts(m, "uc\t:"); | 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 | ttm_pool_debugfs_orders(global_dma32_write_combined, m); | 618 | ttm_pool_debugfs_orders(global_dma32_write_combined, m); |
| 615 | seq_puts(m, "uc 32\t:"); | 619 | seq_puts(m, "uc 32\t:"); |
| 616 | ttm_pool_debugfs_orders(global_dma32_uncached, m); | 620 | ttm_pool_debugfs_orders(global_dma32_uncached, m); |
| 617 | mutex_unlock(&shrinker_lock); | 621 | spin_unlock(&shrinker_lock); |
| 618 | 622 | ||
| 619 | ttm_pool_debugfs_footer(m); | 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 | ttm_pool_debugfs_header(m); | 646 | ttm_pool_debugfs_header(m); |
| 643 | 647 | ||
| 644 | mutex_lock(&shrinker_lock); | 648 | spin_lock(&shrinker_lock); |
| 645 | for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) { | 649 | for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) { |
| 646 | seq_puts(m, "DMA "); | 650 | seq_puts(m, "DMA "); |
| 647 | switch (i) { | 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 | ttm_pool_debugfs_orders(pool->caching[i].orders, m); | 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 | ttm_pool_debugfs_footer(m); | 666 | ttm_pool_debugfs_footer(m); |
| 663 | return 0; | 667 | return 0; |
| @@ -694,7 +698,7 @@ int ttm_pool_mgr_init(unsigned long num_pages) · 30 unmodified lines | |||
| 694 | if (!page_pool_size) | 698 | if (!page_pool_size) |
| 695 | page_pool_size = num_pages; | 699 | page_pool_size = num_pages; |
| 696 | 700 | ||
| 697 | mutex_init(&shrinker_lock); | 701 | spin_lock_init(&shrinker_lock); |
| 698 | INIT_LIST_HEAD(&shrinker_list); | 702 | INIT_LIST_HEAD(&shrinker_list); |
| 699 | 703 | ||
| 700 | for (i = 0; i < MAX_ORDER; ++i) { | 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 | * Initialise a generic range manager for the selected memory type. | 138 | * Initialise a generic range manager for the selected memory type. |
| 139 | * The range manager is installed for this device in the type slot. | 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 | unsigned type, bool use_tt, | 142 | unsigned type, bool use_tt, |
| 143 | unsigned long p_size) | 143 | unsigned long p_size) |
| 144 | { | 144 | { |
| @@ -163,7 +163,7 @@ int ttm_range_man_init(struct ttm_device *bdev, · 18 unmodified lines | |||
| 163 | ttm_resource_manager_set_used(man, true); | 163 | ttm_resource_manager_set_used(man, true); |
| 164 | return 0; | 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 | * ttm_range_man_fini | 169 | * ttm_range_man_fini |
| @@ -173,7 +173,7 @@ EXPORT_SYMBOL(ttm_range_man_init); · 3 unmodified lines | |||
| 173 | * | 173 | * |
| 174 | * Remove the generic range manager from a slot and tear it down. | 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 | unsigned type) | 177 | unsigned type) |
| 178 | { | 178 | { |
| 179 | struct ttm_resource_manager *man = ttm_manager_type(bdev, type); | 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 | kfree(rman); | 200 | kfree(rman); |
| 201 | return 0; | 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 | EXPORT_SYMBOL(ttm_resource_free); | 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 | * ttm_resource_manager_init | 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 | return 0; | 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 | void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) | 125 | void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) |
| 137 | { | 126 | { |
| 138 | bdev->funcs->ttm_tt_destroy(bdev, ttm); | 127 | bdev->funcs->ttm_tt_destroy(bdev, ttm); |
| @@ -167,6 +156,12 @@ EXPORT_SYMBOL(ttm_tt_init); · 28 unmodified lines | |||
| 167 | 156 | ||
| 168 | void ttm_tt_fini(struct ttm_tt *ttm) | 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 | if (ttm->pages) | 165 | if (ttm->pages) |
| 171 | kvfree(ttm->pages); | 166 | kvfree(ttm->pages); |
| 172 | else | 167 | else |
| @@ -4,6 +4,7 @@ config DRM_UDL · 3 unmodified lines | |||
| 4 | depends on DRM | 4 | depends on DRM |
| 5 | depends on USB | 5 | depends on USB |
| 6 | depends on USB_ARCH_HAS_HCD | 6 | depends on USB_ARCH_HAS_HCD |
| 7 | depends on MMU | ||
| 7 | select DRM_GEM_SHMEM_HELPER | 8 | select DRM_GEM_SHMEM_HELPER |
| 8 | select DRM_KMS_HELPER | 9 | select DRM_KMS_HELPER |
| 9 | help | 10 | help |
| @@ -1,7 +1,7 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | config DRM_V3D | 2 | config DRM_V3D |
| 3 | tristate "Broadcom V3D 3.x and newer" | 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 | depends on DRM | 5 | depends on DRM |
| 6 | depends on COMMON_CLK | 6 | depends on COMMON_CLK |
| 7 | depends on MMU | 7 | depends on MMU |
| @@ -234,11 +234,6 @@ struct v3d_job { · 233 unmodified lines | |||
| 234 | struct drm_gem_object **bo; | 234 | struct drm_gem_object **bo; |
| 235 | u32 bo_count; | 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 | /* v3d fence to be signaled by IRQ handler when the job is complete. */ | 237 | /* v3d fence to be signaled by IRQ handler when the job is complete. */ |
| 243 | struct dma_fence *irq_fence; | 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 | struct drm_file *file_priv); | 374 | struct drm_file *file_priv); |
| 380 | int v3d_wait_bo_ioctl(struct drm_device *dev, void *data, | 375 | int v3d_wait_bo_ioctl(struct drm_device *dev, void *data, |
| 381 | struct drm_file *file_priv); | 376 | struct drm_file *file_priv); |
| 377 | void v3d_job_cleanup(struct v3d_job *job); | ||
| 382 | void v3d_job_put(struct v3d_job *job); | 378 | void v3d_job_put(struct v3d_job *job); |
| 383 | void v3d_reset(struct v3d_dev *v3d); | 379 | void v3d_reset(struct v3d_dev *v3d); |
| 384 | void v3d_invalidate_caches(struct v3d_dev *v3d); | 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 | V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); | 198 | V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); |
| 199 | if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & | 199 | if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & |
| 200 | V3D_L2TCACTL_L2TFLS), 100)) { | 200 | V3D_L2TCACTL_TMUWCF), 100)) { |
| 201 | DRM_ERROR("Timeout waiting for L1T write combiner flush\n"); | 201 | DRM_ERROR("Timeout waiting for TMU write combiner flush\n"); |
| 202 | } | 202 | } |
| 203 | 203 | ||
| 204 | mutex_lock(&v3d->cache_clean_lock); | 204 | mutex_lock(&v3d->cache_clean_lock); |
| @@ -259,8 +259,8 @@ v3d_lock_bo_reservations(struct v3d_job *job, · 54 unmodified lines | |||
| 259 | return ret; | 259 | return ret; |
| 260 | 260 | ||
| 261 | for (i = 0; i < job->bo_count; i++) { | 261 | for (i = 0; i < job->bo_count; i++) { |
| 262 | ret = drm_gem_fence_array_add_implicit(&job->deps, | 262 | ret = drm_sched_job_add_implicit_dependencies(&job->base, |
| 263 | job->bo[i], true); | 263 | job->bo[i], true); |
| 264 | if (ret) { | 264 | if (ret) { |
| 265 | drm_gem_unlock_reservations(job->bo, job->bo_count, | 265 | drm_gem_unlock_reservations(job->bo, job->bo_count, |
| 266 | acquire_ctx); | 266 | acquire_ctx); |
| @@ -356,8 +356,6 @@ static void · 89 unmodified lines | |||
| 356 | v3d_job_free(struct kref *ref) | 356 | v3d_job_free(struct kref *ref) |
| 357 | { | 357 | { |
| 358 | struct v3d_job *job = container_of(ref, struct v3d_job, refcount); | 358 | struct v3d_job *job = container_of(ref, struct v3d_job, refcount); |
| 359 | unsigned long index; | ||
| 360 | struct dma_fence *fence; | ||
| 361 | int i; | 359 | int i; |
| 362 | 360 | ||
| 363 | for (i = 0; i < job->bo_count; i++) { | 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 | kvfree(job->bo); | 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 | dma_fence_put(job->irq_fence); | 367 | dma_fence_put(job->irq_fence); |
| 375 | dma_fence_put(job->done_fence); | 368 | dma_fence_put(job->done_fence); |
| 376 | 369 | ||
| @@ -397,6 +390,12 @@ v3d_render_job_free(struct kref *ref) · 20 unmodified lines | |||
| 397 | v3d_job_free(ref); | 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 | void v3d_job_put(struct v3d_job *job) | 399 | void v3d_job_put(struct v3d_job *job) |
| 401 | { | 400 | { |
| 402 | kref_put(&job->refcount, job->free); | 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 | static int | 437 | static int |
| 439 | v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, | 438 | v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, |
| 440 | struct v3d_job *job, void (*free)(struct kref *ref), | 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 | struct dma_fence *in_fence = NULL; | 442 | struct dma_fence *in_fence = NULL; |
| 443 | struct v3d_file_priv *v3d_priv = file_priv->driver_priv; | ||
| 444 | int ret; | 444 | int ret; |
| 445 | 445 | ||
| 446 | job->v3d = v3d; | 446 | job->v3d = v3d; |
| @@ -450,44 +450,40 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, · 3 unmodified lines | |||
| 450 | if (ret < 0) | 450 | if (ret < 0) |
| 451 | return ret; | 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 | ret = drm_syncobj_find_fence(file_priv, in_sync, 0, 0, &in_fence); | 458 | ret = drm_syncobj_find_fence(file_priv, in_sync, 0, 0, &in_fence); |
| 456 | if (ret == -EINVAL) | 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 | if (ret) | 463 | if (ret) |
| 461 | goto fail; | 464 | goto fail_job; |
| 462 | 465 | ||
| 463 | kref_init(&job->refcount); | 466 | kref_init(&job->refcount); |
| 464 | 467 | ||
| 465 | return 0; | 468 | return 0; |
| 469 | fail_job: | ||
| 470 | drm_sched_job_cleanup(&job->base); | ||
| 466 | fail: | 471 | fail: |
| 467 | xa_destroy(&job->deps); | ||
| 468 | pm_runtime_put_autosuspend(v3d->drm.dev); | 472 | pm_runtime_put_autosuspend(v3d->drm.dev); |
| 469 | return ret; | 473 | return ret; |
| 470 | } | 474 | } |
| 471 | 475 | ||
| 472 | static int | 476 | static void |
| 473 | v3d_push_job(struct v3d_file_priv *v3d_priv, | 477 | v3d_push_job(struct v3d_job *job) |
| 474 | struct v3d_job *job, enum v3d_queue queue) | ||
| 475 | { | 478 | { |
| 476 | int ret; | 479 | drm_sched_job_arm(&job->base); |
| 477 | |||
| 478 | ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], | ||
| 479 | v3d_priv); | ||
| 480 | if (ret) | ||
| 481 | return ret; | ||
| 482 | 480 | ||
| 483 | job->done_fence = dma_fence_get(&job->base.s_fence->finished); | 481 | job->done_fence = dma_fence_get(&job->base.s_fence->finished); |
| 484 | 482 | ||
| 485 | /* put by scheduler job completion */ | 483 | /* put by scheduler job completion */ |
| 486 | kref_get(&job->refcount); | 484 | kref_get(&job->refcount); |
| 487 | 485 | ||
| 488 | drm_sched_entity_push_job(&job->base, &v3d_priv->sched_entity[queue]); | 486 | drm_sched_entity_push_job(&job->base); |
| 489 | |||
| 490 | return 0; | ||
| 491 | } | 487 | } |
| 492 | 488 | ||
| 493 | static void | 489 | static void |
| @@ -562,7 +558,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, · 68 unmodified lines | |||
| 562 | INIT_LIST_HEAD(&render->unref_list); | 558 | INIT_LIST_HEAD(&render->unref_list); |
| 563 | 559 | ||
| 564 | ret = v3d_job_init(v3d, file_priv, &render->base, | 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 | if (ret) { | 562 | if (ret) { |
| 567 | kfree(render); | 563 | kfree(render); |
| 568 | return ret; | 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 | ret = v3d_job_init(v3d, file_priv, &bin->base, | 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 | if (ret) { | 576 | if (ret) { |
| 581 | v3d_job_put(&render->base); | 577 | v3d_job_put(&render->base); |
| 582 | kfree(bin); | 578 | kfree(bin); |
| @@ -598,7 +594,7 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, · 15 unmodified lines | |||
| 598 | goto fail; | 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 | if (ret) { | 598 | if (ret) { |
| 603 | kfree(clean_job); | 599 | kfree(clean_job); |
| 604 | clean_job = NULL; | 600 | clean_job = NULL; |
| @@ -633,31 +629,26 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, · 28 unmodified lines | |||
| 633 | if (bin) { | 629 | if (bin) { |
| 634 | bin->base.perfmon = render->base.perfmon; | 630 | bin->base.perfmon = render->base.perfmon; |
| 635 | v3d_perfmon_get(bin->base.perfmon); | 631 | v3d_perfmon_get(bin->base.perfmon); |
| 636 | ret = v3d_push_job(v3d_priv, &bin->base, V3D_BIN); | 632 | v3d_push_job(&bin->base); |
| 637 | if (ret) | ||
| 638 | goto fail_unreserve; | ||
| 639 | 633 | ||
| 640 | ret = drm_gem_fence_array_add(&render->base.deps, | 634 | ret = drm_sched_job_add_dependency(&render->base.base, |
| 641 | dma_fence_get(bin->base.done_fence)); | 635 | dma_fence_get(bin->base.done_fence)); |
| 642 | if (ret) | 636 | if (ret) |
| 643 | goto fail_unreserve; | 637 | goto fail_unreserve; |
| 644 | } | 638 | } |
| 645 | 639 | ||
| 646 | ret = v3d_push_job(v3d_priv, &render->base, V3D_RENDER); | 640 | v3d_push_job(&render->base); |
| 647 | if (ret) | ||
| 648 | goto fail_unreserve; | ||
| 649 | 641 | ||
| 650 | if (clean_job) { | 642 | if (clean_job) { |
| 651 | struct dma_fence *render_fence = | 643 | struct dma_fence *render_fence = |
| 652 | dma_fence_get(render->base.done_fence); | 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 | if (ret) | 647 | if (ret) |
| 655 | goto fail_unreserve; | 648 | goto fail_unreserve; |
| 656 | clean_job->perfmon = render->base.perfmon; | 649 | clean_job->perfmon = render->base.perfmon; |
| 657 | v3d_perfmon_get(clean_job->perfmon); | 650 | v3d_perfmon_get(clean_job->perfmon); |
| 658 | ret = v3d_push_job(v3d_priv, clean_job, V3D_CACHE_CLEAN); | 651 | v3d_push_job(clean_job); |
| 659 | if (ret) | ||
| 660 | goto fail_unreserve; | ||
| 661 | } | 652 | } |
| 662 | 653 | ||
| 663 | mutex_unlock(&v3d->sched_lock); | 654 | mutex_unlock(&v3d->sched_lock); |
| @@ -682,10 +673,10 @@ fail_unreserve: · 18 unmodified lines | |||
| 682 | last_job->bo_count, &acquire_ctx); | 673 | last_job->bo_count, &acquire_ctx); |
| 683 | fail: | 674 | fail: |
| 684 | if (bin) | 675 | if (bin) |
| 685 | v3d_job_put(&bin->base); | 676 | v3d_job_cleanup(&bin->base); |
| 686 | v3d_job_put(&render->base); | 677 | v3d_job_cleanup(&render->base); |
| 687 | if (clean_job) | 678 | if (clean_job) |
| 688 | v3d_job_put(clean_job); | 679 | v3d_job_cleanup(clean_job); |
| 689 | 680 | ||
| 690 | return ret; | 681 | return ret; |
| 691 | } | 682 | } |
| @@ -704,7 +695,6 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, · 12 unmodified lines | |||
| 704 | struct drm_file *file_priv) | 695 | struct drm_file *file_priv) |
| 705 | { | 696 | { |
| 706 | struct v3d_dev *v3d = to_v3d_dev(dev); | 697 | struct v3d_dev *v3d = to_v3d_dev(dev); |
| 707 | struct v3d_file_priv *v3d_priv = file_priv->driver_priv; | ||
| 708 | struct drm_v3d_submit_tfu *args = data; | 698 | struct drm_v3d_submit_tfu *args = data; |
| 709 | struct v3d_tfu_job *job; | 699 | struct v3d_tfu_job *job; |
| 710 | struct ww_acquire_ctx acquire_ctx; | 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 | return -ENOMEM; | 707 | return -ENOMEM; |
| 718 | 708 | ||
| 719 | ret = v3d_job_init(v3d, file_priv, &job->base, | 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 | if (ret) { | 711 | if (ret) { |
| 722 | kfree(job); | 712 | kfree(job); |
| 723 | return ret; | 713 | return ret; |
| @@ -761,9 +751,7 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, · 37 unmodified lines | |||
| 761 | goto fail; | 751 | goto fail; |
| 762 | 752 | ||
| 763 | mutex_lock(&v3d->sched_lock); | 753 | mutex_lock(&v3d->sched_lock); |
| 764 | ret = v3d_push_job(v3d_priv, &job->base, V3D_TFU); | 754 | v3d_push_job(&job->base); |
| 765 | if (ret) | ||
| 766 | goto fail_unreserve; | ||
| 767 | mutex_unlock(&v3d->sched_lock); | 755 | mutex_unlock(&v3d->sched_lock); |
| 768 | 756 | ||
| 769 | v3d_attach_fences_and_unlock_reservation(file_priv, | 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 | return 0; | 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 | fail: | 766 | fail: |
| 783 | v3d_job_put(&job->base); | 767 | v3d_job_cleanup(&job->base); |
| 784 | 768 | ||
| 785 | return ret; | 769 | return ret; |
| 786 | } | 770 | } |
| @@ -818,7 +802,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data, · 31 unmodified lines | |||
| 818 | return -ENOMEM; | 802 | return -ENOMEM; |
| 819 | 803 | ||
| 820 | ret = v3d_job_init(v3d, file_priv, &job->base, | 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 | if (ret) { | 806 | if (ret) { |
| 823 | kfree(job); | 807 | kfree(job); |
| 824 | return ret; | 808 | return ret; |
| @@ -831,7 +815,7 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data, · 6 unmodified lines | |||
| 831 | return -ENOMEM; | 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 | if (ret) { | 819 | if (ret) { |
| 836 | v3d_job_put(&job->base); | 820 | v3d_job_put(&job->base); |
| 837 | kfree(clean_job); | 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 | mutex_lock(&v3d->sched_lock); | 845 | mutex_lock(&v3d->sched_lock); |
| 862 | ret = v3d_push_job(v3d_priv, &job->base, V3D_CSD); | 846 | v3d_push_job(&job->base); |
| 863 | if (ret) | ||
| 864 | goto fail_unreserve; | ||
| 865 | 847 | ||
| 866 | ret = drm_gem_fence_array_add(&clean_job->deps, | 848 | ret = drm_sched_job_add_dependency(&clean_job->base, |
| 867 | dma_fence_get(job->base.done_fence)); | 849 | dma_fence_get(job->base.done_fence)); |
| 868 | if (ret) | 850 | if (ret) |
| 869 | goto fail_unreserve; | 851 | goto fail_unreserve; |
| 870 | 852 | ||
| 871 | ret = v3d_push_job(v3d_priv, clean_job, V3D_CACHE_CLEAN); | 853 | v3d_push_job(clean_job); |
| 872 | if (ret) | ||
| 873 | goto fail_unreserve; | ||
| 874 | mutex_unlock(&v3d->sched_lock); | 854 | mutex_unlock(&v3d->sched_lock); |
| 875 | 855 | ||
| 876 | v3d_attach_fences_and_unlock_reservation(file_priv, | 856 | v3d_attach_fences_and_unlock_reservation(file_priv, |
| @@ -889,8 +869,8 @@ fail_unreserve: · 12 unmodified lines | |||
| 889 | drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count, | 869 | drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count, |
| 890 | &acquire_ctx); | 870 | &acquire_ctx); |
| 891 | fail: | 871 | fail: |
| 892 | v3d_job_put(&job->base); | 872 | v3d_job_cleanup(&job->base); |
| 893 | v3d_job_put(clean_job); | 873 | v3d_job_cleanup(clean_job); |
| 894 | 874 | ||
| 895 | return ret; | 875 | return ret; |
| 896 | } | 876 | } |
| @@ -13,7 +13,7 @@ · 12 unmodified lines | |||
| 13 | * jobs when bulk background jobs are queued up, we submit a new job | 13 | * jobs when bulk background jobs are queued up, we submit a new job |
| 14 | * to the HW only when it has completed the last one, instead of | 14 | * to the HW only when it has completed the last one, instead of |
| 15 | * filling up the CT[01]Q FIFOs with jobs. Similarly, we use | 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 | * render, instead of having the clients submit jobs using the HW's | 17 | * render, instead of having the clients submit jobs using the HW's |
| 18 | * semaphores to interlock between them. | 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 | static void | 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 | struct v3d_job *job = to_v3d_job(sched_job); | 60 | struct v3d_job *job = to_v3d_job(sched_job); |
| 61 | 61 | ||
| 62 | drm_sched_job_cleanup(sched_job); | 62 | v3d_job_cleanup(job); |
| 63 | v3d_job_put(job); | ||
| 64 | } | 63 | } |
| 65 | 64 | ||
| 66 | static void | 65 | static void |
| @@ -73,28 +72,6 @@ v3d_switch_perfmon(struct v3d_dev *v3d, struct v3d_job *job) · 6 unmodified lines | |||
| 73 | v3d_perfmon_start(v3d, job->perfmon); | 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 | static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) | 75 | static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) |
| 99 | { | 76 | { |
| 100 | struct v3d_bin_job *job = to_bin_job(sched_job); | 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 | static const struct drm_sched_backend_ops v3d_bin_sched_ops = { | 352 | static const struct drm_sched_backend_ops v3d_bin_sched_ops = { |
| 376 | .dependency = v3d_job_dependency, | ||
| 377 | .run_job = v3d_bin_job_run, | 353 | .run_job = v3d_bin_job_run, |
| 378 | .timedout_job = v3d_bin_job_timedout, | 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 | static const struct drm_sched_backend_ops v3d_render_sched_ops = { | 358 | static const struct drm_sched_backend_ops v3d_render_sched_ops = { |
| 383 | .dependency = v3d_job_dependency, | ||
| 384 | .run_job = v3d_render_job_run, | 359 | .run_job = v3d_render_job_run, |
| 385 | .timedout_job = v3d_render_job_timedout, | 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 | static const struct drm_sched_backend_ops v3d_tfu_sched_ops = { | 364 | static const struct drm_sched_backend_ops v3d_tfu_sched_ops = { |
| 390 | .dependency = v3d_job_dependency, | ||
| 391 | .run_job = v3d_tfu_job_run, | 365 | .run_job = v3d_tfu_job_run, |
| 392 | .timedout_job = v3d_generic_job_timedout, | 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 | static const struct drm_sched_backend_ops v3d_csd_sched_ops = { | 370 | static const struct drm_sched_backend_ops v3d_csd_sched_ops = { |
| 397 | .dependency = v3d_job_dependency, | ||
| 398 | .run_job = v3d_csd_job_run, | 371 | .run_job = v3d_csd_job_run, |
| 399 | .timedout_job = v3d_csd_job_timedout, | 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 | static const struct drm_sched_backend_ops v3d_cache_clean_sched_ops = { | 376 | static const struct drm_sched_backend_ops v3d_cache_clean_sched_ops = { |
| 404 | .dependency = v3d_job_dependency, | ||
| 405 | .run_job = v3d_cache_clean_job_run, | 377 | .run_job = v3d_cache_clean_job_run, |
| 406 | .timedout_job = v3d_generic_job_timedout, | 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 | int | 382 | int |
| @@ -229,26 +229,19 @@ static const struct of_device_id vc4_dpi_dt_match[] = { · 228 unmodified lines | |||
| 229 | static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) | 229 | static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) |
| 230 | { | 230 | { |
| 231 | struct device *dev = &dpi->pdev->dev; | 231 | struct device *dev = &dpi->pdev->dev; |
| 232 | struct drm_panel *panel; | ||
| 233 | struct drm_bridge *bridge; | 232 | struct drm_bridge *bridge; |
| 234 | int ret; | ||
| 235 | 233 | ||
| 236 | ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, | 234 | bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); |
| 237 | &panel, &bridge); | 235 | if (IS_ERR(bridge)) { |
| 238 | if (ret) { | ||
| 239 | /* If nothing was connected in the DT, that's not an | 236 | /* If nothing was connected in the DT, that's not an |
| 240 | * error. | 237 | * error. |
| 241 | */ | 238 | */ |
| 242 | if (ret == -ENODEV) | 239 | if (PTR_ERR(bridge) == -ENODEV) |
| 243 | return 0; | 240 | return 0; |
| 244 | else | 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 | return drm_bridge_attach(dpi->encoder, bridge, NULL, 0); | 245 | return drm_bridge_attach(dpi->encoder, bridge, NULL, 0); |
| 253 | } | 246 | } |
| 254 | 247 | ||
| @@ -50,13 +50,11 @@ · 49 unmodified lines | |||
| 50 | #define DRIVER_PATCHLEVEL 0 | 50 | #define DRIVER_PATCHLEVEL 0 |
| 51 | 51 | ||
| 52 | /* Helper function for mapping the regs on a platform device. */ | 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 | void __iomem *map; | 55 | void __iomem *map; |
| 57 | 56 | ||
| 58 | res = platform_get_resource(dev, IORESOURCE_MEM, index); | 57 | map = devm_platform_ioremap_resource(pdev, index); |
| 59 | map = devm_ioremap_resource(&dev->dev, res); | ||
| 60 | if (IS_ERR(map)) | 58 | if (IS_ERR(map)) |
| 61 | return map; | 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 | struct drm_device *drm = dev_get_drvdata(master); | 1497 | struct drm_device *drm = dev_get_drvdata(master); |
| 1498 | struct vc4_dsi *dsi = dev_get_drvdata(dev); | 1498 | struct vc4_dsi *dsi = dev_get_drvdata(dev); |
| 1499 | struct vc4_dsi_encoder *vc4_dsi_encoder; | 1499 | struct vc4_dsi_encoder *vc4_dsi_encoder; |
| 1500 | struct drm_panel *panel; | ||
| 1501 | const struct of_device_id *match; | 1500 | const struct of_device_id *match; |
| 1502 | dma_cap_mask_t dma_mask; | 1501 | dma_cap_mask_t dma_mask; |
| 1503 | int ret; | 1502 | int ret; |
| @@ -1609,27 +1608,9 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) · 105 unmodified lines | |||
| 1609 | return ret; | 1608 | return ret; |
| 1610 | } | 1609 | } |
| 1611 | 1610 | ||
| 1612 | ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, | 1611 | dsi->bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); |
| 1613 | &panel, &dsi->bridge); | 1612 | if (IS_ERR(dsi->bridge)) |
| 1614 | if (ret) { | 1613 | return PTR_ERR(dsi->bridge); |
| 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 | } | ||
| 1633 | 1614 | ||
| 1634 | /* The esc clock rate is supposed to always be 100Mhz. */ | 1615 | /* The esc clock rate is supposed to always be 100Mhz. */ |
| 1635 | ret = clk_set_rate(dsi->escape_clock, 100 * 1000000); | 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 | struct vc4_dsi *dsi = dev_get_drvdata(dev); | 1649 | struct vc4_dsi *dsi = dev_get_drvdata(dev); |
| 1669 | 1650 | ||
| 1670 | if (dsi->bridge) | 1651 | pm_runtime_disable(dev); |
| 1671 | pm_runtime_disable(dev); | ||
| 1672 | 1652 | ||
| 1673 | /* | 1653 | /* |
| 1674 | * Restore the bridge_chain so the bridge detach procedure can happen | 1654 | * Restore the bridge_chain so the bridge detach procedure can happen |
| @@ -38,6 +38,7 @@ · 37 unmodified lines | |||
| 38 | 38 | ||
| 39 | #include <drm/drm_drv.h> | 39 | #include <drm/drm_drv.h> |
| 40 | #include <drm/drm_file.h> | 40 | #include <drm/drm_file.h> |
| 41 | #include <drm/drm_gem_shmem_helper.h> | ||
| 41 | #include <drm/drm_ioctl.h> | 42 | #include <drm/drm_ioctl.h> |
| 42 | #include <drm/drm_managed.h> | 43 | #include <drm/drm_managed.h> |
| 43 | #include <drm/drm_prime.h> | 44 | #include <drm/drm_prime.h> |
| @@ -50,87 +51,11 @@ · 6 unmodified lines | |||
| 50 | #define DRIVER_MAJOR 1 | 51 | #define DRIVER_MAJOR 1 |
| 51 | #define DRIVER_MINOR 0 | 52 | #define DRIVER_MINOR 0 |
| 52 | 53 | ||
| 53 | static const struct drm_gem_object_funcs vgem_gem_object_funcs; | ||
| 54 | |||
| 55 | static struct vgem_device { | 54 | static struct vgem_device { |
| 56 | struct drm_device drm; | 55 | struct drm_device drm; |
| 57 | struct platform_device *platform; | 56 | struct platform_device *platform; |
| 58 | } *vgem_device; | 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 | static int vgem_open(struct drm_device *dev, struct drm_file *file) | 59 | static int vgem_open(struct drm_device *dev, struct drm_file *file) |
| 135 | { | 60 | { |
| 136 | struct vgem_file *vfile; | 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 | kfree(vfile); | 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 | static struct drm_ioctl_desc vgem_ioctls[] = { | 87 | static struct drm_ioctl_desc vgem_ioctls[] = { |
| 238 | DRM_IOCTL_DEF_DRV(VGEM_FENCE_ATTACH, vgem_fence_attach_ioctl, DRM_RENDER_ALLOW), | 88 | DRM_IOCTL_DEF_DRV(VGEM_FENCE_ATTACH, vgem_fence_attach_ioctl, DRM_RENDER_ALLOW), |
| 239 | DRM_IOCTL_DEF_DRV(VGEM_FENCE_SIGNAL, vgem_fence_signal_ioctl, DRM_RENDER_ALLOW), | 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) | 92 | DEFINE_DRM_GEM_FOPS(vgem_driver_fops); |
| 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 | } | ||
| 257 | 93 | ||
| 258 | static const struct file_operations vgem_driver_fops = { | 94 | static struct drm_gem_object *vgem_gem_create_object(struct drm_device *dev, size_t size) |
| 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) | ||
| 300 | { | 95 | { |
| 301 | struct drm_vgem_gem_object *bo = to_vgem_bo(obj); | 96 | struct drm_gem_shmem_object *obj; |
| 302 | long n_pages = obj->size >> PAGE_SHIFT; | ||
| 303 | struct page **pages; | ||
| 304 | 97 | ||
| 305 | pages = vgem_pin_pages(bo); | 98 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); |
| 306 | if (IS_ERR(pages)) | 99 | if (!obj) |
| 307 | return PTR_ERR(pages); | 100 | return NULL; |
| 308 | 101 | ||
| 309 | /* Flush the object from the CPU cache so that importers can rely | 102 | /* |
| 310 | * on coherent indirect access via the exported dma-address. | 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); | 106 | obj->map_wc = true; |
| 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); | ||
| 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 | return &obj->base; | 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 | static const struct drm_driver vgem_driver = { | 111 | static const struct drm_driver vgem_driver = { |
| 423 | .driver_features = DRIVER_GEM | DRIVER_RENDER, | 112 | .driver_features = DRIVER_GEM | DRIVER_RENDER, |
| 424 | .open = vgem_open, | 113 | .open = vgem_open, |
| @@ -427,13 +116,8 @@ static const struct drm_driver vgem_driver = { · 2 unmodified lines | |||
| 427 | .num_ioctls = ARRAY_SIZE(vgem_ioctls), | 116 | .num_ioctls = ARRAY_SIZE(vgem_ioctls), |
| 428 | .fops = &vgem_driver_fops, | 117 | .fops = &vgem_driver_fops, |
| 429 | 118 | ||
| 430 | .dumb_create = vgem_gem_dumb_create, | 119 | DRM_GEM_SHMEM_DRIVER_OPS, |
| 431 | 120 | .gem_create_object = vgem_gem_create_object, | |
| 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, | ||
| 437 | 121 | ||
| 438 | .name = DRIVER_NAME, | 122 | .name = DRIVER_NAME, |
| 439 | .desc = DRIVER_DESC, | 123 | .desc = DRIVER_DESC, |
| @@ -26,6 +26,7 @@ · 25 unmodified lines | |||
| 26 | #ifndef VIRTIO_DRV_H | 26 | #ifndef VIRTIO_DRV_H |
| 27 | #define VIRTIO_DRV_H | 27 | #define VIRTIO_DRV_H |
| 28 | 28 | ||
| 29 | #include <linux/dma-direction.h> | ||
| 29 | #include <linux/virtio.h> | 30 | #include <linux/virtio.h> |
| 30 | #include <linux/virtio_ids.h> | 31 | #include <linux/virtio_ids.h> |
| 31 | #include <linux/virtio_config.h> | 32 | #include <linux/virtio_config.h> |
| @@ -459,4 +460,11 @@ bool virtio_gpu_is_vram(struct virtio_gpu_object *bo); · 427 unmodified lines | |||
| 459 | int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev, | 460 | int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev, |
| 460 | struct virtio_gpu_object_params *params, | 461 | struct virtio_gpu_object_params *params, |
| 461 | struct virtio_gpu_object **bo_ptr); | 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 | #endif | 470 | #endif |
| @@ -43,13 +43,41 @@ static int virtgpu_virtio_get_uuid(struct dma_buf *buf, · 42 unmodified lines | |||
| 43 | return 0; | 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 | static const struct virtio_dma_buf_ops virtgpu_dmabuf_ops = { | 74 | static const struct virtio_dma_buf_ops virtgpu_dmabuf_ops = { |
| 47 | .ops = { | 75 | .ops = { |
| 48 | .cache_sgt_mapping = true, | 76 | .cache_sgt_mapping = true, |
| 49 | .attach = virtio_dma_buf_attach, | 77 | .attach = virtio_dma_buf_attach, |
| 50 | .detach = drm_gem_map_detach, | 78 | .detach = drm_gem_map_detach, |
| 51 | .map_dma_buf = drm_gem_map_dma_buf, | 79 | .map_dma_buf = virtgpu_gem_map_dma_buf, |
| 52 | .unmap_dma_buf = drm_gem_unmap_dma_buf, | 80 | .unmap_dma_buf = virtgpu_gem_unmap_dma_buf, |
| 53 | .release = drm_gem_dmabuf_release, | 81 | .release = drm_gem_dmabuf_release, |
| 54 | .mmap = drm_gem_dmabuf_mmap, | 82 | .mmap = drm_gem_dmabuf_mmap, |
| 55 | .vmap = drm_gem_dmabuf_vmap, | 83 | .vmap = drm_gem_dmabuf_vmap, |
| @@ -1,6 +1,8 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | #include "virtgpu_drv.h" | 2 | #include "virtgpu_drv.h" |
| 3 | 3 | ||
| 4 | #include <linux/dma-mapping.h> | ||
| 5 | |||
| 4 | static void virtio_gpu_vram_free(struct drm_gem_object *obj) | 6 | static void virtio_gpu_vram_free(struct drm_gem_object *obj) |
| 5 | { | 7 | { |
| 6 | struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj); | 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 | return ret; | 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 | static const struct drm_gem_object_funcs virtio_gpu_vram_funcs = { | 128 | static const struct drm_gem_object_funcs virtio_gpu_vram_funcs = { |
| 68 | .open = virtio_gpu_gem_object_open, | 129 | .open = virtio_gpu_gem_object_open, |
| 69 | .close = virtio_gpu_gem_object_close, | 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 | struct ttm_operation_ctx ctx = {interruptible, false }; | 94 | struct ttm_operation_ctx ctx = {interruptible, false }; |
| 95 | struct ttm_buffer_object *bo = &buf->base; | 95 | struct ttm_buffer_object *bo = &buf->base; |
| 96 | int ret; | 96 | int ret; |
| 97 | uint32_t new_flags; | ||
| 98 | 97 | ||
| 99 | vmw_execbuf_release_pinned_bo(dev_priv); | 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 | goto err; | 102 | goto err; |
| 104 | 103 | ||
| 105 | if (buf->base.pin_count > 0) | 104 | if (buf->base.pin_count > 0) |
| 106 | ret = ttm_bo_mem_compat(placement, bo->resource, | 105 | ret = ttm_resource_compat(bo->resource, placement) |
| 107 | &new_flags) == true ? 0 : -EINVAL; | 106 | ? 0 : -EINVAL; |
| 108 | else | 107 | else |
| 109 | ret = ttm_bo_validate(bo, placement, &ctx); | 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 | struct ttm_operation_ctx ctx = {interruptible, false }; | 135 | struct ttm_operation_ctx ctx = {interruptible, false }; |
| 137 | struct ttm_buffer_object *bo = &buf->base; | 136 | struct ttm_buffer_object *bo = &buf->base; |
| 138 | int ret; | 137 | int ret; |
| 139 | uint32_t new_flags; | ||
| 140 | 138 | ||
| 141 | vmw_execbuf_release_pinned_bo(dev_priv); | 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 | goto err; | 143 | goto err; |
| 146 | 144 | ||
| 147 | if (buf->base.pin_count > 0) { | 145 | if (buf->base.pin_count > 0) { |
| 148 | ret = ttm_bo_mem_compat(&vmw_vram_gmr_placement, bo->resource, | 146 | ret = ttm_resource_compat(bo->resource, &vmw_vram_gmr_placement) |
| 149 | &new_flags) == true ? 0 : -EINVAL; | 147 | ? 0 : -EINVAL; |
| 150 | goto out_unreserve; | 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 | struct ttm_placement placement; | 206 | struct ttm_placement placement; |
| 209 | struct ttm_place place; | 207 | struct ttm_place place; |
| 210 | int ret = 0; | 208 | int ret = 0; |
| 211 | uint32_t new_flags; | ||
| 212 | 209 | ||
| 213 | place = vmw_vram_placement.placement[0]; | 210 | place = vmw_vram_placement.placement[0]; |
| 214 | place.lpfn = bo->resource->num_pages; | 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 | if (buf->base.pin_count > 0) | 235 | if (buf->base.pin_count > 0) |
| 239 | ret = ttm_bo_mem_compat(&placement, bo->resource, | 236 | ret = ttm_resource_compat(bo->resource, &placement) |
| 240 | &new_flags) == true ? 0 : -EINVAL; | 237 | ? 0 : -EINVAL; |
| 241 | else | 238 | else |
| 242 | ret = ttm_bo_validate(bo, &placement, &ctx); | 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 | struct vmw_ttm_tt *vmw_be = | 522 | struct vmw_ttm_tt *vmw_be = |
| 523 | container_of(ttm, struct vmw_ttm_tt, dma_ttm); | 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 | vmw_ttm_unmap_dma(vmw_be); | 525 | vmw_ttm_unmap_dma(vmw_be); |
| 528 | if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) | 526 | ttm_tt_fini(ttm); |
| 529 | ttm_tt_fini(&vmw_be->dma_ttm); | ||
| 530 | else | ||
| 531 | ttm_tt_fini(ttm); | ||
| 532 | |||
| 533 | if (vmw_be->mob) | 527 | if (vmw_be->mob) |
| 534 | vmw_mob_destroy(vmw_be->mob); | 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 | dma_ttm); | 568 | dma_ttm); |
| 575 | unsigned int i; | 569 | unsigned int i; |
| 576 | 570 | ||
| 571 | vmw_ttm_unbind(bdev, ttm); | ||
| 572 | |||
| 577 | if (vmw_tt->mob) { | 573 | if (vmw_tt->mob) { |
| 578 | vmw_mob_destroy(vmw_tt->mob); | 574 | vmw_mob_destroy(vmw_tt->mob); |
| 579 | vmw_tt->mob = NULL; | 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 | config TYPEC_DP_ALTMODE | 5 | config TYPEC_DP_ALTMODE |
| 6 | tristate "DisplayPort Alternate Mode driver" | 6 | tristate "DisplayPort Alternate Mode driver" |
| 7 | depends on DRM | ||
| 7 | help | 8 | help |
| 8 | DisplayPort USB Type-C Alternate Mode allows DisplayPort | 9 | DisplayPort USB Type-C Alternate Mode allows DisplayPort |
| 9 | displays and adapters to be attached to the USB Type-C | 10 | displays and adapters to be attached to the USB Type-C |
| @@ -11,8 +11,10 @@ · 10 unmodified lines | |||
| 11 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
| 12 | #include <linux/mutex.h> | 12 | #include <linux/mutex.h> |
| 13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
| 14 | #include <linux/property.h> | ||
| 14 | #include <linux/usb/pd_vdo.h> | 15 | #include <linux/usb/pd_vdo.h> |
| 15 | #include <linux/usb/typec_dp.h> | 16 | #include <linux/usb/typec_dp.h> |
| 17 | #include <drm/drm_connector.h> | ||
| 16 | #include "displayport.h" | 18 | #include "displayport.h" |
| 17 | 19 | ||
| 18 | #define DP_HEADER(_dp, ver, cmd) (VDO((_dp)->alt->svid, 1, ver, cmd) \ | 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 | struct typec_displayport_data data; | 59 | struct typec_displayport_data data; |
| 58 | 60 | ||
| 59 | enum dp_state state; | 61 | enum dp_state state; |
| 62 | bool hpd; | ||
| 60 | 63 | ||
| 61 | struct mutex lock; /* device lock */ | 64 | struct mutex lock; /* device lock */ |
| 62 | struct work_struct work; | 65 | struct work_struct work; |
| 63 | struct typec_altmode *alt; | 66 | struct typec_altmode *alt; |
| 64 | const struct typec_altmode *port; | 67 | const struct typec_altmode *port; |
| 68 | struct fwnode_handle *connector_fwnode; | ||
| 65 | }; | 69 | }; |
| 66 | 70 | ||
| 67 | static int dp_altmode_notify(struct dp_altmode *dp) | 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), | 83 | return typec_altmode_notify(dp->alt, conf, &dp->data); |
| 72 | &dp->data); | ||
| 73 | } | 84 | } |
| 74 | 85 | ||
| 75 | static int dp_altmode_configure(struct dp_altmode *dp, u8 con) | 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 | static int dp_altmode_status_update(struct dp_altmode *dp) | 129 | static int dp_altmode_status_update(struct dp_altmode *dp) |
| 119 | { | 130 | { |
| 120 | bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf); | 131 | bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf); |
| 132 | bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE); | ||
| 121 | u8 con = DP_STATUS_CONNECTION(dp->data.status); | 133 | u8 con = DP_STATUS_CONNECTION(dp->data.status); |
| 122 | int ret = 0; | 134 | int ret = 0; |
| 123 | 135 | ||
| @@ -130,6 +142,11 @@ static int dp_altmode_status_update(struct dp_altmode *dp) · 6 unmodified lines | |||
| 130 | ret = dp_altmode_configure(dp, con); | 142 | ret = dp_altmode_configure(dp, con); |
| 131 | if (!ret) | 143 | if (!ret) |
| 132 | dp->state = DP_STATE_CONFIGURE; | 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 | return ret; | 152 | return ret; |
| @@ -137,21 +154,10 @@ static int dp_altmode_status_update(struct dp_altmode *dp) · 1 unmodified line | |||
| 137 | 154 | ||
| 138 | static int dp_altmode_configured(struct dp_altmode *dp) | 155 | static int dp_altmode_configured(struct dp_altmode *dp) |
| 139 | { | 156 | { |
| 140 | int ret; | ||
| 141 | |||
| 142 | sysfs_notify(&dp->alt->dev.kobj, "displayport", "configuration"); | 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 | sysfs_notify(&dp->alt->dev.kobj, "displayport", "pin_assignment"); | 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 | static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) | 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 | ret = typec_altmode_vdm(dp->alt, header, &conf, 2); | 180 | ret = typec_altmode_vdm(dp->alt, header, &conf, 2); |
| 175 | if (ret) { | 181 | if (ret) |
| 176 | if (DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) | 182 | dp_altmode_notify(dp); |
| 177 | dp_altmode_notify(dp); | ||
| 178 | else | ||
| 179 | typec_altmode_notify(dp->alt, TYPEC_STATE_USB, | ||
| 180 | &dp->data); | ||
| 181 | } | ||
| 182 | 183 | ||
| 183 | return ret; | 184 | return ret; |
| 184 | } | 185 | } |
| @@ -521,6 +522,7 @@ static const struct attribute_group dp_altmode_group = { · 336 unmodified lines | |||
| 521 | int dp_altmode_probe(struct typec_altmode *alt) | 522 | int dp_altmode_probe(struct typec_altmode *alt) |
| 522 | { | 523 | { |
| 523 | const struct typec_altmode *port = typec_altmode_get_partner(alt); | 524 | const struct typec_altmode *port = typec_altmode_get_partner(alt); |
| 525 | struct fwnode_handle *fwnode; | ||
| 524 | struct dp_altmode *dp; | 526 | struct dp_altmode *dp; |
| 525 | int ret; | 527 | int ret; |
| 526 | 528 | ||
| @@ -549,6 +551,11 @@ int dp_altmode_probe(struct typec_altmode *alt) · 22 unmodified lines | |||
| 549 | alt->desc = "DisplayPort"; | 551 | alt->desc = "DisplayPort"; |
| 550 | alt->ops = &dp_altmode_ops; | 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 | typec_altmode_set_drvdata(alt, dp); | 559 | typec_altmode_set_drvdata(alt, dp); |
| 553 | 560 | ||
| 554 | dp->state = DP_STATE_ENTER; | 561 | dp->state = DP_STATE_ENTER; |
| @@ -564,6 +571,13 @@ void dp_altmode_remove(struct typec_altmode *alt) · 9 unmodified lines | |||
| 564 | 571 | ||
| 565 | sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); | 572 | sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); |
| 566 | cancel_work_sync(&dp->work); | 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 | EXPORT_SYMBOL_GPL(dp_altmode_remove); | 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 | struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, | 911 | struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, |
| 912 | struct drm_panel *panel, | 912 | struct drm_panel *panel, |
| 913 | u32 connector_type); | 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 | struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge); | 916 | struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge); |
| 915 | #endif | 917 | #endif |
| 916 | 918 | ||
| @@ -1084,6 +1084,14 @@ struct drm_connector_funcs { · 1083 unmodified lines | |||
| 1084 | */ | 1084 | */ |
| 1085 | void (*atomic_print_state)(struct drm_printer *p, | 1085 | void (*atomic_print_state)(struct drm_printer *p, |
| 1086 | const struct drm_connector_state *state); | 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 | struct device *kdev; | 1236 | struct device *kdev; |
| 1229 | /** @attr: sysfs attributes */ | 1237 | /** @attr: sysfs attributes */ |
| 1230 | struct device_attribute *attr; | 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 | * @head: | 1249 | * @head: |
| @@ -1239,6 +1255,14 @@ struct drm_connector { · 5 unmodified lines | |||
| 1239 | */ | 1255 | */ |
| 1240 | struct list_head head; | 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 | /** @base: base KMS object */ | 1266 | /** @base: base KMS object */ |
| 1243 | struct drm_mode_object base; | 1267 | struct drm_mode_object base; |
| 1244 | 1268 | ||
| @@ -1650,6 +1674,7 @@ drm_connector_is_unregistered(struct drm_connector *connector) · 405 unmodified lines | |||
| 1650 | DRM_CONNECTOR_UNREGISTERED; | 1674 | DRM_CONNECTOR_UNREGISTERED; |
| 1651 | } | 1675 | } |
| 1652 | 1676 | ||
| 1677 | void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode); | ||
| 1653 | const char *drm_get_connector_type_name(unsigned int connector_type); | 1678 | const char *drm_get_connector_type_name(unsigned int connector_type); |
| 1654 | const char *drm_get_connector_status_name(enum drm_connector_status status); | 1679 | const char *drm_get_connector_status_name(enum drm_connector_status status); |
| 1655 | const char *drm_get_subpixel_order_name(enum subpixel_order order); | 1680 | const char *drm_get_subpixel_order_name(enum subpixel_order order); |
| @@ -167,7 +167,6 @@ struct drm_ioctl_desc { · 166 unmodified lines | |||
| 167 | .name = #ioctl \ | 167 | .name = #ioctl \ |
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | int drm_ioctl_permit(u32 flags, struct drm_file *file_priv); | ||
| 171 | long drm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); | 170 | long drm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); |
| 172 | long drm_ioctl_kernel(struct file *, drm_ioctl_t, void *, u32); | 171 | long drm_ioctl_kernel(struct file *, drm_ioctl_t, void *, u32); |
| 173 | #ifdef CONFIG_COMPAT | 172 | #ifdef CONFIG_COMPAT |
| @@ -103,14 +103,13 @@ struct drm_mode_config_funcs { · 102 unmodified lines | |||
| 103 | * Callback used by helpers to inform the driver of output configuration | 103 | * Callback used by helpers to inform the driver of output configuration |
| 104 | * changes. | 104 | * changes. |
| 105 | * | 105 | * |
| 106 | * Drivers implementing fbdev emulation with the helpers can call | 106 | * Drivers implementing fbdev emulation use drm_kms_helper_hotplug_event() |
| 107 | * drm_fb_helper_hotplug_changed from this hook to inform the fbdev | 107 | * to call this hook to inform the fbdev helper of output changes. |
| 108 | * helper of output changes. | ||
| 109 | * | 108 | * |
| 110 | * FIXME: | 109 | * This hook is deprecated, drivers should instead use |
| 111 | * | 110 | * drm_fbdev_generic_setup() which takes care of any necessary |
| 112 | * Except that there's no vtable for device-level helper callbacks | 111 | * hotplug event forwarding already without further involvement by |
| 113 | * there's no reason this is a core function. | 112 | * the driver. |
| 114 | */ | 113 | */ |
| 115 | void (*output_poll_changed)(struct drm_device *dev); | 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 | * struct drm_plane_state - mutable plane state | 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 | * @crtc_w and the source coordinates @src_x, @src_y, @src_h and @src_w are the | 47 | * @crtc_w and the source coordinates @src_x, @src_y, @src_h and @src_w are the |
| 48 | * raw coordinates provided by userspace. Drivers should use | 48 | * raw coordinates provided by userspace. Drivers should use |
| 49 | * drm_atomic_helper_check_plane_state() and only use the derived rectangles in | 49 | * drm_atomic_helper_check_plane_state() and only use the derived rectangles in |
| @@ -27,9 +27,12 @@ · 26 unmodified lines | |||
| 27 | #include <drm/spsc_queue.h> | 27 | #include <drm/spsc_queue.h> |
| 28 | #include <linux/dma-fence.h> | 28 | #include <linux/dma-fence.h> |
| 29 | #include <linux/completion.h> | 29 | #include <linux/completion.h> |
| 30 | #include <linux/xarray.h> | ||
| 30 | 31 | ||
| 31 | #define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000) | 32 | #define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000) |
| 32 | 33 | ||
| 34 | struct drm_gem_object; | ||
| 35 | |||
| 33 | struct drm_gpu_scheduler; | 36 | struct drm_gpu_scheduler; |
| 34 | struct drm_sched_rq; | 37 | struct drm_sched_rq; |
| 35 | 38 | ||
| @@ -50,56 +53,147 @@ enum drm_sched_priority { · 14 unmodified lines | |||
| 50 | * struct drm_sched_entity - A wrapper around a job queue (typically | 53 | * struct drm_sched_entity - A wrapper around a job queue (typically |
| 51 | * attached to the DRM file_priv). | 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 | * Entities will emit jobs in order to their corresponding hardware | 56 | * Entities will emit jobs in order to their corresponding hardware |
| 81 | * ring, and the scheduler will alternate between entities based on | 57 | * ring, and the scheduler will alternate between entities based on |
| 82 | * scheduling policy. | 58 | * scheduling policy. |
| 83 | */ | 59 | */ |
| 84 | struct drm_sched_entity { | 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 | struct list_head list; | 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 | struct drm_sched_rq *rq; | 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 | struct drm_gpu_scheduler **sched_list; | 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 | unsigned int num_sched_list; | 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 | enum drm_sched_priority priority; | 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 | spinlock_t rq_lock; | 120 | spinlock_t rq_lock; |
| 91 | 121 | ||
| 122 | /** | ||
| 123 | * @job_queue: the list of jobs of this entity. | ||
| 124 | */ | ||
| 92 | struct spsc_queue job_queue; | 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 | atomic_t fence_seq; | 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 | uint64_t fence_context; | 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 | struct dma_fence *dependency; | 152 | struct dma_fence *dependency; |
| 153 | |||
| 154 | /** | ||
| 155 | * @cb: | ||
| 156 | * | ||
| 157 | * Callback for the dependency fence above. | ||
| 158 | */ | ||
| 98 | struct dma_fence_cb cb; | 159 | struct dma_fence_cb cb; |
| 160 | |||
| 161 | /** | ||
| 162 | * @guilty: | ||
| 163 | * | ||
| 164 | * Points to entities' guilty. | ||
| 165 | */ | ||
| 99 | atomic_t *guilty; | 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 | struct dma_fence *last_scheduled; | 175 | struct dma_fence *last_scheduled; |
| 176 | |||
| 177 | /** | ||
| 178 | * @last_user: last group leader pushing a job into the entity. | ||
| 179 | */ | ||
| 101 | struct task_struct *last_user; | 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 | bool stopped; | 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 | struct completion entity_idle; | 197 | struct completion entity_idle; |
| 104 | }; | 198 | }; |
| 105 | 199 | ||
| @@ -198,6 +292,17 @@ struct drm_sched_job { · 92 unmodified lines | |||
| 198 | enum drm_sched_priority s_priority; | 292 | enum drm_sched_priority s_priority; |
| 199 | struct drm_sched_entity *entity; | 293 | struct drm_sched_entity *entity; |
| 200 | struct dma_fence_cb cb; | 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 | static inline bool drm_sched_invalidate_job(struct drm_sched_job *s_job, | 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 | struct drm_sched_backend_ops { | 326 | struct drm_sched_backend_ops { |
| 222 | /** | 327 | /** |
| 223 | * @dependency: Called when the scheduler is considering scheduling | 328 | * @dependency: |
| 224 | * this job next, to get another struct dma_fence for this job to | 329 | * |
| 225 | * block on. Once it returns NULL, run_job() may be called. | 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 | struct dma_fence *(*dependency)(struct drm_sched_job *sched_job, | 338 | struct dma_fence *(*dependency)(struct drm_sched_job *sched_job, |
| 228 | struct drm_sched_entity *s_entity); | 339 | struct drm_sched_entity *s_entity); |
| @@ -348,6 +459,14 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched); · 119 unmodified lines | |||
| 348 | int drm_sched_job_init(struct drm_sched_job *job, | 459 | int drm_sched_job_init(struct drm_sched_job *job, |
| 349 | struct drm_sched_entity *entity, | 460 | struct drm_sched_entity *entity, |
| 350 | void *owner); | 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 | void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, | 470 | void drm_sched_entity_modify_sched(struct drm_sched_entity *entity, |
| 352 | struct drm_gpu_scheduler **sched_list, | 471 | struct drm_gpu_scheduler **sched_list, |
| 353 | unsigned int num_sched_list); | 472 | unsigned int num_sched_list); |
| @@ -381,14 +500,17 @@ void drm_sched_entity_fini(struct drm_sched_entity *entity); · 27 unmodified lines | |||
| 381 | void drm_sched_entity_destroy(struct drm_sched_entity *entity); | 500 | void drm_sched_entity_destroy(struct drm_sched_entity *entity); |
| 382 | void drm_sched_entity_select_rq(struct drm_sched_entity *entity); | 501 | void drm_sched_entity_select_rq(struct drm_sched_entity *entity); |
| 383 | struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity); | 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, | 503 | void drm_sched_entity_push_job(struct drm_sched_job *sched_job); |
| 385 | struct drm_sched_entity *entity); | ||
| 386 | void drm_sched_entity_set_priority(struct drm_sched_entity *entity, | 504 | void drm_sched_entity_set_priority(struct drm_sched_entity *entity, |
| 387 | enum drm_sched_priority priority); | 505 | enum drm_sched_priority priority); |
| 388 | bool drm_sched_entity_is_ready(struct drm_sched_entity *entity); | 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 | struct drm_sched_entity *s_entity, void *owner); | 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 | void drm_sched_fence_scheduled(struct drm_sched_fence *fence); | 514 | void drm_sched_fence_scheduled(struct drm_sched_fence *fence); |
| 393 | void drm_sched_fence_finished(struct drm_sched_fence *fence); | 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 | return ttm_bo_wait(bo, ctx->interruptible, ctx->no_wait_gpu); | 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 | * ttm_bo_validate | 268 | * ttm_bo_validate |
| 281 | * | 269 | * |
| @@ -27,9 +27,26 @@ · 26 unmodified lines | |||
| 27 | 27 | ||
| 28 | #define TTM_NUM_CACHING_TYPES 3 | 28 | #define TTM_NUM_CACHING_TYPES 3 |
| 29 | 29 | ||
| 30 | /** | ||
| 31 | * enum ttm_caching - CPU caching and BUS snooping behavior. | ||
| 32 | */ | ||
| 30 | enum ttm_caching { | 33 | enum ttm_caching { |
| 34 | /** | ||
| 35 | * @ttm_uncached: Most defensive option for device mappings, | ||
| 36 | * don't even allow write combining. | ||
| 37 | */ | ||
| 31 | ttm_uncached, | 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 | ttm_write_combined, | 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 | ttm_cached | 50 | ttm_cached |
| 34 | }; | 51 | }; |
| 35 | 52 | ||
| @@ -39,31 +39,23 @@ struct ttm_operation_ctx; · 38 unmodified lines | |||
| 39 | 39 | ||
| 40 | /** | 40 | /** |
| 41 | * struct ttm_global - Buffer object driver global data. | 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 | extern struct ttm_global { | 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 | struct page *dummy_read_page; | 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 | struct list_head device_list; | 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 | atomic_t bo_count; | 60 | atomic_t bo_count; |
| 69 | } ttm_glob; | 61 | } ttm_glob; |
| @@ -230,49 +222,64 @@ struct ttm_device_funcs { · 160 unmodified lines | |||
| 230 | 222 | ||
| 231 | /** | 223 | /** |
| 232 | * struct ttm_device - Buffer object driver device-specific data. | 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 | struct ttm_device { | 226 | struct ttm_device { |
| 245 | /* | 227 | /** |
| 228 | * @device_list: Our entry in the global device list. | ||
| 246 | * Constant after bo device init | 229 | * Constant after bo device init |
| 247 | */ | 230 | */ |
| 248 | struct list_head device_list; | 231 | struct list_head device_list; |
| 232 | |||
| 233 | /** | ||
| 234 | * @funcs: Function table for the device. | ||
| 235 | * Constant after bo device init | ||
| 236 | */ | ||
| 249 | struct ttm_device_funcs *funcs; | 237 | struct ttm_device_funcs *funcs; |
| 250 | 238 | ||
| 251 | /* | 239 | /** |
| 240 | * @sysman: Resource manager for the system domain. | ||
| 252 | * Access via ttm_manager_type. | 241 | * Access via ttm_manager_type. |
| 253 | */ | 242 | */ |
| 254 | struct ttm_resource_manager sysman; | 243 | struct ttm_resource_manager sysman; |
| 244 | |||
| 245 | /** | ||
| 246 | * @man_drv: An array of resource_managers, one per resource type. | ||
| 247 | */ | ||
| 255 | struct ttm_resource_manager *man_drv[TTM_NUM_MEM_TYPES]; | 248 | struct ttm_resource_manager *man_drv[TTM_NUM_MEM_TYPES]; |
| 256 | 249 | ||
| 257 | /* | 250 | /** |
| 258 | * Protected by internal locks. | 251 | * @vma_manager: Address space manager for finding BOs to mmap. |
| 259 | */ | 252 | */ |
| 260 | struct drm_vma_offset_manager *vma_manager; | 253 | struct drm_vma_offset_manager *vma_manager; |
| 254 | |||
| 255 | /** | ||
| 256 | * @pool: page pool for the device. | ||
| 257 | */ | ||
| 261 | struct ttm_pool pool; | 258 | struct ttm_pool pool; |
| 262 | 259 | ||
| 263 | /* | 260 | /** |
| 264 | * Protection for the per manager LRU and ddestroy lists. | 261 | * @lru_lock: Protection for the per manager LRU and ddestroy lists. |
| 265 | */ | 262 | */ |
| 266 | spinlock_t lru_lock; | 263 | spinlock_t lru_lock; |
| 264 | |||
| 265 | /** | ||
| 266 | * @ddestroy: Destroyed but not yet cleaned up buffer objects. | ||
| 267 | */ | ||
| 267 | struct list_head ddestroy; | 268 | struct list_head ddestroy; |
| 268 | 269 | ||
| 269 | /* | 270 | /** |
| 270 | * Protected by load / firstopen / lastclose /unload sync. | 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 | struct address_space *dev_mapping; | 279 | struct address_space *dev_mapping; |
| 273 | 280 | ||
| 274 | /* | 281 | /** |
| 275 | * Internal protection. | 282 | * @wq: Work queue structure for the delayed delete workqueue. |
| 276 | */ | 283 | */ |
| 277 | struct delayed_work wq; | 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 | static inline struct ttm_resource_manager * | 291 | static inline struct ttm_resource_manager * |
| 285 | ttm_manager_type(struct ttm_device *bdev, int mem_type) | 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 | return bdev->man_drv[mem_type]; | 296 | return bdev->man_drv[mem_type]; |
| 288 | } | 297 | } |
| 289 | 298 | ||
| 290 | static inline void ttm_set_driver_manager(struct ttm_device *bdev, int type, | 299 | static inline void ttm_set_driver_manager(struct ttm_device *bdev, int type, |
| 291 | struct ttm_resource_manager *manager) | 300 | struct ttm_resource_manager *manager) |
| 292 | { | 301 | { |
| 302 | BUILD_BUG_ON(__builtin_constant_p(type) && type >= TTM_NUM_MEM_TYPES); | ||
| 293 | bdev->man_drv[type] = manager; | 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 | struct drm_vma_offset_manager *vma_manager, | 308 | struct drm_vma_offset_manager *vma_manager, |
| 299 | bool use_dma_alloc, bool use_dma32); | 309 | bool use_dma_alloc, bool use_dma32); |
| 300 | void ttm_device_fini(struct ttm_device *bdev); | 310 | void ttm_device_fini(struct ttm_device *bdev); |
| 311 | void ttm_device_clear_dma_mappings(struct ttm_device *bdev); | ||
| 301 | 312 | ||
| 302 | #endif | 313 | #endif |
| @@ -58,6 +58,7 @@ · 57 unmodified lines | |||
| 58 | * | 58 | * |
| 59 | * @fpfn: first valid page frame number to put the object | 59 | * @fpfn: first valid page frame number to put the object |
| 60 | * @lpfn: last valid page frame number to put the object | 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 | * @flags: memory domain and caching flags for the object | 62 | * @flags: memory domain and caching flags for the object |
| 62 | * | 63 | * |
| 63 | * Structure indicating a possible place to put an object. | 64 | * Structure indicating a possible place to put an object. |
| @@ -37,7 +37,7 @@ struct ttm_pool; · 36 unmodified lines | |||
| 37 | struct ttm_operation_ctx; | 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 | * @pool: the pool we belong to, might be NULL for the global ones | 42 | * @pool: the pool we belong to, might be NULL for the global ones |
| 43 | * @order: the allocation order our pages have | 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 | * @use_dma_alloc: if coherent DMA allocations should be used | 64 | * @use_dma_alloc: if coherent DMA allocations should be used |
| 64 | * @use_dma32: if GFP_DMA32 should be used | 65 | * @use_dma32: if GFP_DMA32 should be used |
| 65 | * @caching: pools for each caching/order | 66 | * @caching: pools for each caching/order |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | |||
| 4 | #define _TTM_RANGE_MANAGER_H_ | 4 | #define _TTM_RANGE_MANAGER_H_ |
| 5 | 5 | ||
| 6 | #include <drm/ttm/ttm_resource.h> | 6 | #include <drm/ttm/ttm_resource.h> |
| 7 | #include <drm/ttm/ttm_device.h> | ||
| 7 | #include <drm/drm_mm.h> | 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 | return container_of(res, struct ttm_range_mgr_node, base); | 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 | unsigned type, bool use_tt, | 38 | unsigned type, bool use_tt, |
| 38 | unsigned long p_size); | 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 | unsigned type); | 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 | #endif | 56 | #endif |
| @@ -40,6 +40,7 @@ struct ttm_resource_manager; · 39 unmodified lines | |||
| 40 | struct ttm_resource; | 40 | struct ttm_resource; |
| 41 | struct ttm_place; | 41 | struct ttm_place; |
| 42 | struct ttm_buffer_object; | 42 | struct ttm_buffer_object; |
| 43 | struct ttm_placement; | ||
| 43 | struct dma_buf_map; | 44 | struct dma_buf_map; |
| 44 | struct io_mapping; | 45 | struct io_mapping; |
| 45 | struct sg_table; | 46 | struct sg_table; |
| @@ -102,10 +103,7 @@ struct ttm_resource_manager_func { · 56 unmodified lines | |||
| 102 | * struct ttm_resource_manager | 103 | * struct ttm_resource_manager |
| 103 | * | 104 | * |
| 104 | * @use_type: The memory type is enabled. | 105 | * @use_type: The memory type is enabled. |
| 105 | * @flags: TTM_MEMTYPE_XX flags identifying the traits of the memory | 106 | * @use_tt: If a TT object should be used for the backing store. |
| 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. | ||
| 109 | * @size: Size of the managed region. | 107 | * @size: Size of the managed region. |
| 110 | * @func: structure pointer implementing the range manager. See above | 108 | * @func: structure pointer implementing the range manager. See above |
| 111 | * @move_lock: lock for move fence | 109 | * @move_lock: lock for move fence |
| @@ -143,6 +141,7 @@ struct ttm_resource_manager { · 31 unmodified lines | |||
| 143 | * @addr: mapped virtual address | 141 | * @addr: mapped virtual address |
| 144 | * @offset: physical addr | 142 | * @offset: physical addr |
| 145 | * @is_iomem: is this io memory ? | 143 | * @is_iomem: is this io memory ? |
| 144 | * @caching: See enum ttm_caching | ||
| 146 | * | 145 | * |
| 147 | * Structure indicating the bus placement of an object. | 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 | const struct ttm_place *place, | 265 | const struct ttm_place *place, |
| 267 | struct ttm_resource **res); | 266 | struct ttm_resource **res); |
| 268 | void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res); | 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 | void ttm_resource_manager_init(struct ttm_resource_manager *man, | 271 | void ttm_resource_manager_init(struct ttm_resource_manager *man, |
| 271 | unsigned long p_size); | 272 | unsigned long p_size); |
| @@ -55,7 +55,7 @@ struct ttm_operation_ctx; · 54 unmodified lines | |||
| 55 | * @dma_address: The DMA (bus) addresses of the pages | 55 | * @dma_address: The DMA (bus) addresses of the pages |
| 56 | * @swap_storage: Pointer to shmem struct file for swap storage. | 56 | * @swap_storage: Pointer to shmem struct file for swap storage. |
| 57 | * @pages_list: used by some page allocation backend | 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 | * This is a structure holding the pages, caching- and aperture binding | 60 | * This is a structure holding the pages, caching- and aperture binding |
| 61 | * status for a buffer object that isn't backed by fixed (VRAM / AGP) | 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 | void ttm_tt_fini(struct ttm_tt *ttm); | 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 | * @ttm: The struct ttm_tt. | 133 | * @ttm: The struct ttm_tt. |
| 133 | * | 134 | * |
| 134 | * Unbind, unpopulate and destroy common struct ttm_tt. | 135 | * Unbind, unpopulate and destroy common struct ttm_tt. |
| 135 | */ | 136 | */ |
| 136 | void ttm_tt_destroy(struct ttm_device *bdev, struct ttm_tt *ttm); | 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 | * ttm_tt_swapin: | 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 | * ttm_tt_populate - allocate pages for a ttm | 151 | * ttm_tt_populate - allocate pages for a ttm |
| 158 | * | 152 | * |
| 153 | * @bdev: the ttm_device this object belongs to | ||
| 159 | * @ttm: Pointer to the ttm_tt structure | 154 | * @ttm: Pointer to the ttm_tt structure |
| 155 | * @ctx: operation context for populating the tt object. | ||
| 160 | * | 156 | * |
| 161 | * Calls the driver method to allocate pages for a ttm | 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 | * ttm_tt_unpopulate - free pages from a ttm | 163 | * ttm_tt_unpopulate - free pages from a ttm |
| 167 | * | 164 | * |
| 165 | * @bdev: the ttm_device this object belongs to | ||
| 168 | * @ttm: Pointer to the ttm_tt structure | 166 | * @ttm: Pointer to the ttm_tt structure |
| 169 | * | 167 | * |
| 170 | * Calls the driver method to free all pages from a ttm | 168 | * Calls the driver method to free all pages from a ttm |
| @@ -420,6 +420,13 @@ struct dma_buf { · 419 unmodified lines | |||
| 420 | * - Dynamic importers should set fences for any access that they can't | 420 | * - Dynamic importers should set fences for any access that they can't |
| 421 | * disable immediately from their &dma_buf_attach_ops.move_notify | 421 | * disable immediately from their &dma_buf_attach_ops.move_notify |
| 422 | * callback. | 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 | struct dma_resv *resv; | 431 | struct dma_resv *resv; |
| 425 | 432 | ||
| @@ -214,19 +214,15 @@ struct dma_fence_ops { · 213 unmodified lines | |||
| 214 | * Custom wait implementation, defaults to dma_fence_default_wait() if | 214 | * Custom wait implementation, defaults to dma_fence_default_wait() if |
| 215 | * not set. | 215 | * not set. |
| 216 | * | 216 | * |
| 217 | * The dma_fence_default_wait implementation should work for any fence, as long | 217 | * Deprecated and should not be used by new implementations. Only used |
| 218 | * as @enable_signaling works correctly. This hook allows drivers to | 218 | * by existing implementations which need special handling for their |
| 219 | * have an optimized version for the case where a process context is | 219 | * hardware reset procedure. |
| 220 | * already available, e.g. if @enable_signaling for the general case | ||
| 221 | * needs to set up a worker thread. | ||
| 222 | * | 220 | * |
| 223 | * Must return -ERESTARTSYS if the wait is intr = true and the wait was | 221 | * Must return -ERESTARTSYS if the wait is intr = true and the wait was |
| 224 | * interrupted, and remaining jiffies if fence has signaled, or 0 if wait | 222 | * interrupted, and remaining jiffies if fence has signaled, or 0 if wait |
| 225 | * timed out. Can also return other error values on custom implementations, | 223 | * timed out. Can also return other error values on custom implementations, |
| 226 | * which should be treated as if the fence is signaled. For example a hardware | 224 | * which should be treated as if the fence is signaled. For example a hardware |
| 227 | * lockup could be reported like that. | 225 | * lockup could be reported like that. |
| 228 | * | ||
| 229 | * This callback is optional. | ||
| 230 | */ | 226 | */ |
| 231 | signed long (*wait)(struct dma_fence *fence, | 227 | signed long (*wait)(struct dma_fence *fence, |
| 232 | bool intr, signed long timeout); | 228 | bool intr, signed long timeout); |
| @@ -590,26 +586,4 @@ struct dma_fence *dma_fence_get_stub(void); · 357 unmodified lines | |||
| 590 | struct dma_fence *dma_fence_allocate_private_stub(void); | 586 | struct dma_fence *dma_fence_allocate_private_stub(void); |
| 591 | u64 dma_fence_context_alloc(unsigned num); | 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 | #endif /* __LINUX_DMA_FENCE_H */ | 589 | #endif /* __LINUX_DMA_FENCE_H */ |
| @@ -62,16 +62,90 @@ struct dma_resv_list { · 61 unmodified lines | |||
| 62 | 62 | ||
| 63 | /** | 63 | /** |
| 64 | * struct dma_resv - a reservation object manages fences for a buffer | 64 | * struct dma_resv - a reservation object manages fences for a buffer |
| 65 | * @lock: update side lock | 65 | * |
| 66 | * @seq: sequence count for managing RCU read-side synchronization | 66 | * There are multiple uses for this, with sometimes slightly different rules in |
| 67 | * @fence_excl: the exclusive fence, if there is one currently | 67 | * how the fence slots are used. |
| 68 | * @fence: list of current shared fences | 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 | struct dma_resv { | 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 | struct ww_mutex lock; | 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 | seqcount_ww_mutex_t seq; | 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 | struct dma_fence __rcu *fence_excl; | 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 | struct dma_resv_list __rcu *fence; | 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 | * undefined order, a #ww_acquire_ctx is passed to unwind if a cycle | 172 | * undefined order, a #ww_acquire_ctx is passed to unwind if a cycle |
| 99 | * is detected. See ww_mutex_lock() and ww_acquire_init(). A reservation | 173 | * is detected. See ww_mutex_lock() and ww_acquire_init(). A reservation |
| 100 | * object may be locked by itself by passing NULL as @ctx. | 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 | static inline int dma_resv_lock(struct dma_resv *obj, | 183 | static inline int dma_resv_lock(struct dma_resv *obj, |
| 103 | struct ww_acquire_ctx *ctx) | 184 | struct ww_acquire_ctx *ctx) |
| @@ -119,6 +200,12 @@ static inline int dma_resv_lock(struct dma_resv *obj, · 15 unmodified lines | |||
| 119 | * undefined order, a #ww_acquire_ctx is passed to unwind if a cycle | 200 | * undefined order, a #ww_acquire_ctx is passed to unwind if a cycle |
| 120 | * is detected. See ww_mutex_lock() and ww_acquire_init(). A reservation | 201 | * is detected. See ww_mutex_lock() and ww_acquire_init(). A reservation |
| 121 | * object may be locked by itself by passing NULL as @ctx. | 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 | static inline int dma_resv_lock_interruptible(struct dma_resv *obj, | 210 | static inline int dma_resv_lock_interruptible(struct dma_resv *obj, |
| 124 | struct ww_acquire_ctx *ctx) | 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 | * Acquires the reservation object after a die case. This function | 221 | * Acquires the reservation object after a die case. This function |
| 135 | * will sleep until the lock becomes available. See dma_resv_lock() as | 222 | * will sleep until the lock becomes available. See dma_resv_lock() as |
| 136 | * well. | 223 | * well. |
| 224 | * | ||
| 225 | * See also dma_resv_lock_slow_interruptible() for the interruptible variant. | ||
| 137 | */ | 226 | */ |
| 138 | static inline void dma_resv_lock_slow(struct dma_resv *obj, | 227 | static inline void dma_resv_lock_slow(struct dma_resv *obj, |
| 139 | struct ww_acquire_ctx *ctx) | 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 | * if they overlap with a writer. | 256 | * if they overlap with a writer. |
| 168 | * | 257 | * |
| 169 | * Also note that since no context is provided, no deadlock protection is | 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 | * Returns true if the lock was acquired, false otherwise. | 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 | * Returns the context used to lock a reservation object or NULL if no context | 283 | * Returns the context used to lock a reservation object or NULL if no context |
| 195 | * was used or the object is not locked at all. | 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 | static inline struct ww_acquire_ctx *dma_resv_locking_ctx(struct dma_resv *obj) | 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 | extern int register_shrinker(struct shrinker *shrinker); | 93 | extern int register_shrinker(struct shrinker *shrinker); |
| 94 | extern void unregister_shrinker(struct shrinker *shrinker); | 94 | extern void unregister_shrinker(struct shrinker *shrinker); |
| 95 | extern void free_prealloced_shrinker(struct shrinker *shrinker); | 95 | extern void free_prealloced_shrinker(struct shrinker *shrinker); |
| 96 | extern void synchronize_shrinkers(void); | ||
| 96 | #endif | 97 | #endif |
| @@ -373,6 +373,12 @@ extern "C" { · 372 unmodified lines | |||
| 373 | 373 | ||
| 374 | #define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) | 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 | #define fourcc_mod_code(vendor, val) \ | 382 | #define fourcc_mod_code(vendor, val) \ |
| 377 | ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) | 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 | * struct drm_mode_create_lease - Create lease | 1110 | * struct drm_mode_create_lease - Create lease |
| 1111 | * | 1111 | * |
| 1112 | * Lease mode resources, creating another drm_master. | 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 | struct drm_mode_create_lease { | 1117 | struct drm_mode_create_lease { |
| 1115 | /** @object_ids: Pointer to array of object ids (__u32) */ | 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 | EXPORT_SYMBOL(unregister_shrinker); | 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 | #define SHRINK_BATCH 128 | 705 | #define SHRINK_BATCH 128 |
| 691 | 706 | ||
| 692 | static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, | 707 | static unsigned long do_shrink_slab(struct shrink_control *shrinkctl, |