diff options
| author | Dave Airlie <airlied@redhat.com> | 2019-04-23 20:12:34 -0400 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2019-04-23 20:12:50 -0400 |
| commit | 8d8f6f704495b1135ef77b7860d833fda97ea841 (patch) | |
| tree | 459c4abf0d5e908f8107caa0a9e174d274e62bb3 /drivers | |
| parent | b1c4f7feada5a5cf4e13db1631fb4784b1ddcb31 (diff) | |
| parent | debcd8f954be2b1f643e76b2400bc7c3d12b4594 (diff) | |
Merge tag 'drm-misc-next-2019-04-18' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v5.2:
UAPI Changes:
- Document which feature flags belong to which command in virtio_gpu.h
- Make the FB_DAMAGE_CLIPS available for atomic userspace only, it's useless for legacy.
Cross-subsystem Changes:
- Add device tree bindings for lg,acx467akm-7 panel and ST-Ericsson Multi Channel Display Engine MCDE
- Add parameters to the device tree bindings for tfp410
- iommu/io-pgtable: Add ARM Mali midgard MMU page table format
- dma-buf: Only do a 64-bits seqno compare when driver explicitly asks for it, else wraparound.
- Use the 64-bits compare for dma-fence-chains
Core Changes:
- Make the fb conversion functions use __iomem dst.
- Rename drm_client_add to drm_client_register
- Move intel_fb_initial_config to core.
- Add a drm_gem_objects_lookup helper
- Add drm_gem_fence_array helpers, and use it in lima.
- Add drm_format_helper.c to kerneldoc.
Driver Changes:
- Add panfrost driver for mali midgard/bitfrost.
- Converts bochs to use the simple display type.
- Small fixes to sun4i, tinydrm, ti-fp410.
- Fid aspeed's Kconfig options.
- Make some symbols/functions static in lima, sun4i and meson.
- Add a driver for the lg,acx467akm-7 panel.
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/737ad994-213d-45b5-207a-b99d795acd21@linux.intel.com
Diffstat (limited to 'drivers')
53 files changed, 4183 insertions, 643 deletions
diff --git a/drivers/dma-buf/dma-fence-chain.c b/drivers/dma-buf/dma-fence-chain.c index c729f98a7bd3..93c42078cb57 100644 --- a/drivers/dma-buf/dma-fence-chain.c +++ b/drivers/dma-buf/dma-fence-chain.c | |||
| @@ -193,6 +193,7 @@ static void dma_fence_chain_release(struct dma_fence *fence) | |||
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | const struct dma_fence_ops dma_fence_chain_ops = { | 195 | const struct dma_fence_ops dma_fence_chain_ops = { |
| 196 | .use_64bit_seqno = true, | ||
| 196 | .get_driver_name = dma_fence_chain_get_driver_name, | 197 | .get_driver_name = dma_fence_chain_get_driver_name, |
| 197 | .get_timeline_name = dma_fence_chain_get_timeline_name, | 198 | .get_timeline_name = dma_fence_chain_get_timeline_name, |
| 198 | .enable_signaling = dma_fence_chain_enable_signaling, | 199 | .enable_signaling = dma_fence_chain_enable_signaling, |
| @@ -225,7 +226,7 @@ void dma_fence_chain_init(struct dma_fence_chain *chain, | |||
| 225 | init_irq_work(&chain->work, dma_fence_chain_irq_work); | 226 | init_irq_work(&chain->work, dma_fence_chain_irq_work); |
| 226 | 227 | ||
| 227 | /* Try to reuse the context of the previous chain node. */ | 228 | /* Try to reuse the context of the previous chain node. */ |
| 228 | if (prev_chain && __dma_fence_is_later(seqno, prev->seqno)) { | 229 | if (prev_chain && __dma_fence_is_later(seqno, prev->seqno, prev->ops)) { |
| 229 | context = prev->context; | 230 | context = prev->context; |
| 230 | chain->prev_seqno = prev->seqno; | 231 | chain->prev_seqno = prev->seqno; |
| 231 | } else { | 232 | } else { |
diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c index 32dcf7b4c935..119b2ffbc2c9 100644 --- a/drivers/dma-buf/sw_sync.c +++ b/drivers/dma-buf/sw_sync.c | |||
| @@ -161,7 +161,7 @@ static bool timeline_fence_signaled(struct dma_fence *fence) | |||
| 161 | { | 161 | { |
| 162 | struct sync_timeline *parent = dma_fence_parent(fence); | 162 | struct sync_timeline *parent = dma_fence_parent(fence); |
| 163 | 163 | ||
| 164 | return !__dma_fence_is_later(fence->seqno, parent->value); | 164 | return !__dma_fence_is_later(fence->seqno, parent->value, fence->ops); |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | static bool timeline_fence_enable_signaling(struct dma_fence *fence) | 167 | static bool timeline_fence_enable_signaling(struct dma_fence *fence) |
diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 4f6305ca52c8..ed3fb6e5224c 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c | |||
| @@ -258,7 +258,8 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a, | |||
| 258 | 258 | ||
| 259 | i_b++; | 259 | i_b++; |
| 260 | } else { | 260 | } else { |
| 261 | if (__dma_fence_is_later(pt_a->seqno, pt_b->seqno)) | 261 | if (__dma_fence_is_later(pt_a->seqno, pt_b->seqno, |
| 262 | pt_a->ops)) | ||
| 262 | add_fence(fences, &i, pt_a); | 263 | add_fence(fences, &i, pt_a); |
| 263 | else | 264 | else |
| 264 | add_fence(fences, &i, pt_b); | 265 | add_fence(fences, &i, pt_b); |
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index bcbc4234893a..39d5f7562f1c 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig | |||
| @@ -337,6 +337,8 @@ source "drivers/gpu/drm/vboxvideo/Kconfig" | |||
| 337 | 337 | ||
| 338 | source "drivers/gpu/drm/lima/Kconfig" | 338 | source "drivers/gpu/drm/lima/Kconfig" |
| 339 | 339 | ||
| 340 | source "drivers/gpu/drm/panfrost/Kconfig" | ||
| 341 | |||
| 340 | source "drivers/gpu/drm/aspeed/Kconfig" | 342 | source "drivers/gpu/drm/aspeed/Kconfig" |
| 341 | 343 | ||
| 342 | # Keep legacy drivers last | 344 | # Keep legacy drivers last |
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7ebae3d45505..3d0c75cd687c 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
| @@ -112,4 +112,5 @@ obj-$(CONFIG_DRM_TVE200) += tve200/ | |||
| 112 | obj-$(CONFIG_DRM_XEN) += xen/ | 112 | obj-$(CONFIG_DRM_XEN) += xen/ |
| 113 | obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ | 113 | obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ |
| 114 | obj-$(CONFIG_DRM_LIMA) += lima/ | 114 | obj-$(CONFIG_DRM_LIMA) += lima/ |
| 115 | obj-$(CONFIG_DRM_PANFROST) += panfrost/ | ||
| 115 | obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ | 116 | obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/ |
diff --git a/drivers/gpu/drm/aspeed/Kconfig b/drivers/gpu/drm/aspeed/Kconfig index 42b74d18a41b..cccab520e02f 100644 --- a/drivers/gpu/drm/aspeed/Kconfig +++ b/drivers/gpu/drm/aspeed/Kconfig | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | config DRM_ASPEED_GFX | 1 | config DRM_ASPEED_GFX |
| 2 | tristate "ASPEED BMC Display Controller" | 2 | tristate "ASPEED BMC Display Controller" |
| 3 | depends on DRM && OF | 3 | depends on DRM && OF |
| 4 | depends on (COMPILE_TEST || ARCH_ASPEED) | ||
| 4 | select DRM_KMS_HELPER | 5 | select DRM_KMS_HELPER |
| 5 | select DRM_KMS_CMA_HELPER | 6 | select DRM_KMS_CMA_HELPER |
| 6 | select DRM_PANEL | 7 | select DMA_CMA if HAVE_DMA_CONTIGUOUS |
| 7 | select DMA_CMA | 8 | select CMA if HAVE_DMA_CONTIGUOUS |
| 8 | select CMA | ||
| 9 | select MFD_SYSCON | 9 | select MFD_SYSCON |
| 10 | help | 10 | help |
| 11 | Chose this option if you have an ASPEED AST2500 SOC Display | 11 | Chose this option if you have an ASPEED AST2500 SOC Display |
diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h index 049d058571d4..341cc9d1bab4 100644 --- a/drivers/gpu/drm/bochs/bochs.h +++ b/drivers/gpu/drm/bochs/bochs.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <drm/drm_crtc_helper.h> | 7 | #include <drm/drm_crtc_helper.h> |
| 8 | #include <drm/drm_encoder.h> | 8 | #include <drm/drm_encoder.h> |
| 9 | #include <drm/drm_fb_helper.h> | 9 | #include <drm/drm_fb_helper.h> |
| 10 | #include <drm/drm_simple_kms_helper.h> | ||
| 10 | 11 | ||
| 11 | #include <drm/drm_gem.h> | 12 | #include <drm/drm_gem.h> |
| 12 | 13 | ||
| @@ -69,9 +70,8 @@ struct bochs_device { | |||
| 69 | struct edid *edid; | 70 | struct edid *edid; |
| 70 | 71 | ||
| 71 | /* drm */ | 72 | /* drm */ |
| 72 | struct drm_device *dev; | 73 | struct drm_device *dev; |
| 73 | struct drm_crtc crtc; | 74 | struct drm_simple_display_pipe pipe; |
| 74 | struct drm_encoder encoder; | ||
| 75 | struct drm_connector connector; | 75 | struct drm_connector connector; |
| 76 | 76 | ||
| 77 | /* ttm */ | 77 | /* ttm */ |
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index 485f9cf05e8b..5e905f50449d 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c | |||
| @@ -22,76 +22,55 @@ MODULE_PARM_DESC(defy, "default y resolution"); | |||
| 22 | 22 | ||
| 23 | /* ---------------------------------------------------------------------- */ | 23 | /* ---------------------------------------------------------------------- */ |
| 24 | 24 | ||
| 25 | static void bochs_crtc_mode_set_nofb(struct drm_crtc *crtc) | 25 | static const uint32_t bochs_formats[] = { |
| 26 | DRM_FORMAT_XRGB8888, | ||
| 27 | DRM_FORMAT_BGRX8888, | ||
| 28 | }; | ||
| 29 | |||
| 30 | static void bochs_plane_update(struct bochs_device *bochs, | ||
| 31 | struct drm_plane_state *state) | ||
| 26 | { | 32 | { |
| 27 | struct bochs_device *bochs = | 33 | struct bochs_bo *bo; |
| 28 | container_of(crtc, struct bochs_device, crtc); | ||
| 29 | 34 | ||
| 30 | bochs_hw_setmode(bochs, &crtc->mode); | 35 | if (!state->fb || !bochs->stride) |
| 36 | return; | ||
| 37 | |||
| 38 | bo = gem_to_bochs_bo(state->fb->obj[0]); | ||
| 39 | bochs_hw_setbase(bochs, | ||
| 40 | state->crtc_x, | ||
| 41 | state->crtc_y, | ||
| 42 | bo->bo.offset); | ||
| 43 | bochs_hw_setformat(bochs, state->fb->format); | ||
| 31 | } | 44 | } |
| 32 | 45 | ||
| 33 | static void bochs_crtc_atomic_enable(struct drm_crtc *crtc, | 46 | static void bochs_pipe_enable(struct drm_simple_display_pipe *pipe, |
| 34 | struct drm_crtc_state *old_crtc_state) | 47 | struct drm_crtc_state *crtc_state, |
| 48 | struct drm_plane_state *plane_state) | ||
| 35 | { | 49 | { |
| 50 | struct bochs_device *bochs = pipe->crtc.dev->dev_private; | ||
| 51 | |||
| 52 | bochs_hw_setmode(bochs, &crtc_state->mode); | ||
| 53 | bochs_plane_update(bochs, plane_state); | ||
| 36 | } | 54 | } |
| 37 | 55 | ||
| 38 | static void bochs_crtc_atomic_flush(struct drm_crtc *crtc, | 56 | static void bochs_pipe_update(struct drm_simple_display_pipe *pipe, |
| 39 | struct drm_crtc_state *old_crtc_state) | 57 | struct drm_plane_state *old_state) |
| 40 | { | 58 | { |
| 41 | struct drm_device *dev = crtc->dev; | 59 | struct bochs_device *bochs = pipe->crtc.dev->dev_private; |
| 42 | struct drm_pending_vblank_event *event; | 60 | struct drm_crtc *crtc = &pipe->crtc; |
| 43 | 61 | ||
| 44 | if (crtc->state && crtc->state->event) { | 62 | bochs_plane_update(bochs, pipe->plane.state); |
| 45 | unsigned long irqflags; | ||
| 46 | 63 | ||
| 47 | spin_lock_irqsave(&dev->event_lock, irqflags); | 64 | if (crtc->state->event) { |
| 48 | event = crtc->state->event; | 65 | spin_lock_irq(&crtc->dev->event_lock); |
| 66 | drm_crtc_send_vblank_event(crtc, crtc->state->event); | ||
| 49 | crtc->state->event = NULL; | 67 | crtc->state->event = NULL; |
| 50 | drm_crtc_send_vblank_event(crtc, event); | 68 | spin_unlock_irq(&crtc->dev->event_lock); |
| 51 | spin_unlock_irqrestore(&dev->event_lock, irqflags); | ||
| 52 | } | 69 | } |
| 53 | } | 70 | } |
| 54 | 71 | ||
| 55 | 72 | static int bochs_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, | |
| 56 | /* These provide the minimum set of functions required to handle a CRTC */ | 73 | struct drm_plane_state *new_state) |
| 57 | static const struct drm_crtc_funcs bochs_crtc_funcs = { | ||
| 58 | .set_config = drm_atomic_helper_set_config, | ||
| 59 | .destroy = drm_crtc_cleanup, | ||
| 60 | .page_flip = drm_atomic_helper_page_flip, | ||
| 61 | .reset = drm_atomic_helper_crtc_reset, | ||
| 62 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | ||
| 63 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static const struct drm_crtc_helper_funcs bochs_helper_funcs = { | ||
| 67 | .mode_set_nofb = bochs_crtc_mode_set_nofb, | ||
| 68 | .atomic_enable = bochs_crtc_atomic_enable, | ||
| 69 | .atomic_flush = bochs_crtc_atomic_flush, | ||
| 70 | }; | ||
| 71 | |||
| 72 | static const uint32_t bochs_formats[] = { | ||
| 73 | DRM_FORMAT_XRGB8888, | ||
| 74 | DRM_FORMAT_BGRX8888, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static void bochs_plane_atomic_update(struct drm_plane *plane, | ||
| 78 | struct drm_plane_state *old_state) | ||
| 79 | { | ||
| 80 | struct bochs_device *bochs = plane->dev->dev_private; | ||
| 81 | struct bochs_bo *bo; | ||
| 82 | |||
| 83 | if (!plane->state->fb) | ||
| 84 | return; | ||
| 85 | bo = gem_to_bochs_bo(plane->state->fb->obj[0]); | ||
| 86 | bochs_hw_setbase(bochs, | ||
| 87 | plane->state->crtc_x, | ||
| 88 | plane->state->crtc_y, | ||
| 89 | bo->bo.offset); | ||
| 90 | bochs_hw_setformat(bochs, plane->state->fb->format); | ||
| 91 | } | ||
| 92 | |||
| 93 | static int bochs_plane_prepare_fb(struct drm_plane *plane, | ||
| 94 | struct drm_plane_state *new_state) | ||
| 95 | { | 74 | { |
| 96 | struct bochs_bo *bo; | 75 | struct bochs_bo *bo; |
| 97 | 76 | ||
| @@ -101,8 +80,8 @@ static int bochs_plane_prepare_fb(struct drm_plane *plane, | |||
| 101 | return bochs_bo_pin(bo, TTM_PL_FLAG_VRAM); | 80 | return bochs_bo_pin(bo, TTM_PL_FLAG_VRAM); |
| 102 | } | 81 | } |
| 103 | 82 | ||
| 104 | static void bochs_plane_cleanup_fb(struct drm_plane *plane, | 83 | static void bochs_pipe_cleanup_fb(struct drm_simple_display_pipe *pipe, |
| 105 | struct drm_plane_state *old_state) | 84 | struct drm_plane_state *old_state) |
| 106 | { | 85 | { |
| 107 | struct bochs_bo *bo; | 86 | struct bochs_bo *bo; |
| 108 | 87 | ||
| @@ -112,73 +91,13 @@ static void bochs_plane_cleanup_fb(struct drm_plane *plane, | |||
| 112 | bochs_bo_unpin(bo); | 91 | bochs_bo_unpin(bo); |
| 113 | } | 92 | } |
| 114 | 93 | ||
| 115 | static const struct drm_plane_helper_funcs bochs_plane_helper_funcs = { | 94 | static const struct drm_simple_display_pipe_funcs bochs_pipe_funcs = { |
| 116 | .atomic_update = bochs_plane_atomic_update, | 95 | .enable = bochs_pipe_enable, |
| 117 | .prepare_fb = bochs_plane_prepare_fb, | 96 | .update = bochs_pipe_update, |
| 118 | .cleanup_fb = bochs_plane_cleanup_fb, | 97 | .prepare_fb = bochs_pipe_prepare_fb, |
| 119 | }; | 98 | .cleanup_fb = bochs_pipe_cleanup_fb, |
| 120 | |||
| 121 | static const struct drm_plane_funcs bochs_plane_funcs = { | ||
| 122 | .update_plane = drm_atomic_helper_update_plane, | ||
| 123 | .disable_plane = drm_atomic_helper_disable_plane, | ||
| 124 | .destroy = drm_primary_helper_destroy, | ||
| 125 | .reset = drm_atomic_helper_plane_reset, | ||
| 126 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, | ||
| 127 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, | ||
| 128 | }; | ||
| 129 | |||
| 130 | static struct drm_plane *bochs_primary_plane(struct drm_device *dev) | ||
| 131 | { | ||
| 132 | struct drm_plane *primary; | ||
| 133 | int ret; | ||
| 134 | |||
| 135 | primary = kzalloc(sizeof(*primary), GFP_KERNEL); | ||
| 136 | if (primary == NULL) { | ||
| 137 | DRM_DEBUG_KMS("Failed to allocate primary plane\n"); | ||
| 138 | return NULL; | ||
| 139 | } | ||
| 140 | |||
| 141 | ret = drm_universal_plane_init(dev, primary, 0, | ||
| 142 | &bochs_plane_funcs, | ||
| 143 | bochs_formats, | ||
| 144 | ARRAY_SIZE(bochs_formats), | ||
| 145 | NULL, | ||
| 146 | DRM_PLANE_TYPE_PRIMARY, NULL); | ||
| 147 | if (ret) { | ||
| 148 | kfree(primary); | ||
| 149 | return NULL; | ||
| 150 | } | ||
| 151 | |||
| 152 | drm_plane_helper_add(primary, &bochs_plane_helper_funcs); | ||
| 153 | return primary; | ||
| 154 | } | ||
| 155 | |||
| 156 | static void bochs_crtc_init(struct drm_device *dev) | ||
| 157 | { | ||
| 158 | struct bochs_device *bochs = dev->dev_private; | ||
| 159 | struct drm_crtc *crtc = &bochs->crtc; | ||
| 160 | struct drm_plane *primary = bochs_primary_plane(dev); | ||
| 161 | |||
| 162 | drm_crtc_init_with_planes(dev, crtc, primary, NULL, | ||
| 163 | &bochs_crtc_funcs, NULL); | ||
| 164 | drm_crtc_helper_add(crtc, &bochs_helper_funcs); | ||
| 165 | } | ||
| 166 | |||
| 167 | static const struct drm_encoder_funcs bochs_encoder_encoder_funcs = { | ||
| 168 | .destroy = drm_encoder_cleanup, | ||
| 169 | }; | 99 | }; |
| 170 | 100 | ||
| 171 | static void bochs_encoder_init(struct drm_device *dev) | ||
| 172 | { | ||
| 173 | struct bochs_device *bochs = dev->dev_private; | ||
| 174 | struct drm_encoder *encoder = &bochs->encoder; | ||
| 175 | |||
| 176 | encoder->possible_crtcs = 0x1; | ||
| 177 | drm_encoder_init(dev, encoder, &bochs_encoder_encoder_funcs, | ||
| 178 | DRM_MODE_ENCODER_DAC, NULL); | ||
| 179 | } | ||
| 180 | |||
| 181 | |||
| 182 | static int bochs_connector_get_modes(struct drm_connector *connector) | 101 | static int bochs_connector_get_modes(struct drm_connector *connector) |
| 183 | { | 102 | { |
| 184 | struct bochs_device *bochs = | 103 | struct bochs_device *bochs = |
| @@ -278,11 +197,14 @@ int bochs_kms_init(struct bochs_device *bochs) | |||
| 278 | 197 | ||
| 279 | bochs->dev->mode_config.funcs = &bochs_mode_funcs; | 198 | bochs->dev->mode_config.funcs = &bochs_mode_funcs; |
| 280 | 199 | ||
| 281 | bochs_crtc_init(bochs->dev); | ||
| 282 | bochs_encoder_init(bochs->dev); | ||
| 283 | bochs_connector_init(bochs->dev); | 200 | bochs_connector_init(bochs->dev); |
| 284 | drm_connector_attach_encoder(&bochs->connector, | 201 | drm_simple_display_pipe_init(bochs->dev, |
| 285 | &bochs->encoder); | 202 | &bochs->pipe, |
| 203 | &bochs_pipe_funcs, | ||
| 204 | bochs_formats, | ||
| 205 | ARRAY_SIZE(bochs_formats), | ||
| 206 | NULL, | ||
| 207 | &bochs->connector); | ||
| 286 | 208 | ||
| 287 | drm_mode_config_reset(bochs->dev); | 209 | drm_mode_config_reset(bochs->dev); |
| 288 | 210 | ||
diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 285be4a0f4bd..8b0e71bd3ca7 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c | |||
| @@ -29,8 +29,10 @@ struct tfp410 { | |||
| 29 | struct drm_connector connector; | 29 | struct drm_connector connector; |
| 30 | unsigned int connector_type; | 30 | unsigned int connector_type; |
| 31 | 31 | ||
| 32 | u32 bus_format; | ||
| 32 | struct i2c_adapter *ddc; | 33 | struct i2c_adapter *ddc; |
| 33 | struct gpio_desc *hpd; | 34 | struct gpio_desc *hpd; |
| 35 | int hpd_irq; | ||
| 34 | struct delayed_work hpd_work; | 36 | struct delayed_work hpd_work; |
| 35 | struct gpio_desc *powerdown; | 37 | struct gpio_desc *powerdown; |
| 36 | 38 | ||
| @@ -124,8 +126,10 @@ static int tfp410_attach(struct drm_bridge *bridge) | |||
| 124 | return -ENODEV; | 126 | return -ENODEV; |
| 125 | } | 127 | } |
| 126 | 128 | ||
| 127 | if (dvi->hpd) | 129 | if (dvi->hpd_irq >= 0) |
| 128 | dvi->connector.polled = DRM_CONNECTOR_POLL_HPD; | 130 | dvi->connector.polled = DRM_CONNECTOR_POLL_HPD; |
| 131 | else | ||
| 132 | dvi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; | ||
| 129 | 133 | ||
| 130 | drm_connector_helper_add(&dvi->connector, | 134 | drm_connector_helper_add(&dvi->connector, |
| 131 | &tfp410_con_helper_funcs); | 135 | &tfp410_con_helper_funcs); |
| @@ -136,6 +140,9 @@ static int tfp410_attach(struct drm_bridge *bridge) | |||
| 136 | return ret; | 140 | return ret; |
| 137 | } | 141 | } |
| 138 | 142 | ||
| 143 | drm_display_info_set_bus_formats(&dvi->connector.display_info, | ||
| 144 | &dvi->bus_format, 1); | ||
| 145 | |||
| 139 | drm_connector_attach_encoder(&dvi->connector, | 146 | drm_connector_attach_encoder(&dvi->connector, |
| 140 | bridge->encoder); | 147 | bridge->encoder); |
| 141 | 148 | ||
| @@ -194,6 +201,7 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) | |||
| 194 | struct drm_bridge_timings *timings = &dvi->timings; | 201 | struct drm_bridge_timings *timings = &dvi->timings; |
| 195 | struct device_node *ep; | 202 | struct device_node *ep; |
| 196 | u32 pclk_sample = 0; | 203 | u32 pclk_sample = 0; |
| 204 | u32 bus_width = 24; | ||
| 197 | s32 deskew = 0; | 205 | s32 deskew = 0; |
| 198 | 206 | ||
| 199 | /* Start with defaults. */ | 207 | /* Start with defaults. */ |
| @@ -218,6 +226,7 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) | |||
| 218 | 226 | ||
| 219 | /* Get the sampling edge from the endpoint. */ | 227 | /* Get the sampling edge from the endpoint. */ |
| 220 | of_property_read_u32(ep, "pclk-sample", &pclk_sample); | 228 | of_property_read_u32(ep, "pclk-sample", &pclk_sample); |
| 229 | of_property_read_u32(ep, "bus-width", &bus_width); | ||
| 221 | of_node_put(ep); | 230 | of_node_put(ep); |
| 222 | 231 | ||
| 223 | timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH; | 232 | timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH; |
| @@ -235,6 +244,17 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c) | |||
| 235 | return -EINVAL; | 244 | return -EINVAL; |
| 236 | } | 245 | } |
| 237 | 246 | ||
| 247 | switch (bus_width) { | ||
| 248 | case 12: | ||
| 249 | dvi->bus_format = MEDIA_BUS_FMT_RGB888_2X12_LE; | ||
| 250 | break; | ||
| 251 | case 24: | ||
| 252 | dvi->bus_format = MEDIA_BUS_FMT_RGB888_1X24; | ||
| 253 | break; | ||
| 254 | default: | ||
| 255 | return -EINVAL; | ||
| 256 | } | ||
| 257 | |||
| 238 | /* Get the setup and hold time from vendor-specific properties. */ | 258 | /* Get the setup and hold time from vendor-specific properties. */ |
| 239 | of_property_read_u32(dvi->dev->of_node, "ti,deskew", (u32 *)&deskew); | 259 | of_property_read_u32(dvi->dev->of_node, "ti,deskew", (u32 *)&deskew); |
| 240 | if (deskew < -4 || deskew > 3) | 260 | if (deskew < -4 || deskew > 3) |
| @@ -324,10 +344,15 @@ static int tfp410_init(struct device *dev, bool i2c) | |||
| 324 | return PTR_ERR(dvi->powerdown); | 344 | return PTR_ERR(dvi->powerdown); |
| 325 | } | 345 | } |
| 326 | 346 | ||
| 327 | if (dvi->hpd) { | 347 | if (dvi->hpd) |
| 348 | dvi->hpd_irq = gpiod_to_irq(dvi->hpd); | ||
| 349 | else | ||
| 350 | dvi->hpd_irq = -ENXIO; | ||
| 351 | |||
| 352 | if (dvi->hpd_irq >= 0) { | ||
| 328 | INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func); | 353 | INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func); |
| 329 | 354 | ||
| 330 | ret = devm_request_threaded_irq(dev, gpiod_to_irq(dvi->hpd), | 355 | ret = devm_request_threaded_irq(dev, dvi->hpd_irq, |
| 331 | NULL, tfp410_hpd_irq_thread, IRQF_TRIGGER_RISING | | 356 | NULL, tfp410_hpd_irq_thread, IRQF_TRIGGER_RISING | |
| 332 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | 357 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| 333 | "hdmi-hpd", dvi); | 358 | "hdmi-hpd", dvi); |
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c index 5095b8ce52c2..be4ea370ba31 100644 --- a/drivers/gpu/drm/cirrus/cirrus.c +++ b/drivers/gpu/drm/cirrus/cirrus.c | |||
| @@ -307,16 +307,16 @@ static int cirrus_fb_blit_rect(struct drm_framebuffer *fb, | |||
| 307 | return -ENOMEM; | 307 | return -ENOMEM; |
| 308 | 308 | ||
| 309 | if (cirrus->cpp == fb->format->cpp[0]) | 309 | if (cirrus->cpp == fb->format->cpp[0]) |
| 310 | drm_fb_memcpy_dstclip(__io_virt(cirrus->vram), | 310 | drm_fb_memcpy_dstclip(cirrus->vram, |
| 311 | vmap, fb, rect); | 311 | vmap, fb, rect); |
| 312 | 312 | ||
| 313 | else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2) | 313 | else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2) |
| 314 | drm_fb_xrgb8888_to_rgb565_dstclip(__io_virt(cirrus->vram), | 314 | drm_fb_xrgb8888_to_rgb565_dstclip(cirrus->vram, |
| 315 | cirrus->pitch, | 315 | cirrus->pitch, |
| 316 | vmap, fb, rect, false); | 316 | vmap, fb, rect, false); |
| 317 | 317 | ||
| 318 | else if (fb->format->cpp[0] == 4 && cirrus->cpp == 3) | 318 | else if (fb->format->cpp[0] == 4 && cirrus->cpp == 3) |
| 319 | drm_fb_xrgb8888_to_rgb888_dstclip(__io_virt(cirrus->vram), | 319 | drm_fb_xrgb8888_to_rgb888_dstclip(cirrus->vram, |
| 320 | cirrus->pitch, | 320 | cirrus->pitch, |
| 321 | vmap, fb, rect); | 321 | vmap, fb, rect); |
| 322 | 322 | ||
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index 9b2bd28dde0a..f20d1dda3961 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c | |||
| @@ -69,7 +69,8 @@ EXPORT_SYMBOL(drm_client_close); | |||
| 69 | * @name: Client name | 69 | * @name: Client name |
| 70 | * @funcs: DRM client functions (optional) | 70 | * @funcs: DRM client functions (optional) |
| 71 | * | 71 | * |
| 72 | * This initialises the client and opens a &drm_file. Use drm_client_add() to complete the process. | 72 | * This initialises the client and opens a &drm_file. |
| 73 | * Use drm_client_register() to complete the process. | ||
| 73 | * The caller needs to hold a reference on @dev before calling this function. | 74 | * The caller needs to hold a reference on @dev before calling this function. |
| 74 | * The client is freed when the &drm_device is unregistered. See drm_client_release(). | 75 | * The client is freed when the &drm_device is unregistered. See drm_client_release(). |
| 75 | * | 76 | * |
| @@ -108,16 +109,16 @@ err_put_module: | |||
| 108 | EXPORT_SYMBOL(drm_client_init); | 109 | EXPORT_SYMBOL(drm_client_init); |
| 109 | 110 | ||
| 110 | /** | 111 | /** |
| 111 | * drm_client_add - Add client to the device list | 112 | * drm_client_register - Register client |
| 112 | * @client: DRM client | 113 | * @client: DRM client |
| 113 | * | 114 | * |
| 114 | * Add the client to the &drm_device client list to activate its callbacks. | 115 | * Add the client to the &drm_device client list to activate its callbacks. |
| 115 | * @client must be initialized by a call to drm_client_init(). After | 116 | * @client must be initialized by a call to drm_client_init(). After |
| 116 | * drm_client_add() it is no longer permissible to call drm_client_release() | 117 | * drm_client_register() it is no longer permissible to call drm_client_release() |
| 117 | * directly (outside the unregister callback), instead cleanup will happen | 118 | * directly (outside the unregister callback), instead cleanup will happen |
| 118 | * automatically on driver unload. | 119 | * automatically on driver unload. |
| 119 | */ | 120 | */ |
| 120 | void drm_client_add(struct drm_client_dev *client) | 121 | void drm_client_register(struct drm_client_dev *client) |
| 121 | { | 122 | { |
| 122 | struct drm_device *dev = client->dev; | 123 | struct drm_device *dev = client->dev; |
| 123 | 124 | ||
| @@ -125,7 +126,7 @@ void drm_client_add(struct drm_client_dev *client) | |||
| 125 | list_add(&client->list, &dev->clientlist); | 126 | list_add(&client->list, &dev->clientlist); |
| 126 | mutex_unlock(&dev->clientlist_mutex); | 127 | mutex_unlock(&dev->clientlist_mutex); |
| 127 | } | 128 | } |
| 128 | EXPORT_SYMBOL(drm_client_add); | 129 | EXPORT_SYMBOL(drm_client_register); |
| 129 | 130 | ||
| 130 | /** | 131 | /** |
| 131 | * drm_client_release - Release DRM client resources | 132 | * drm_client_release - Release DRM client resources |
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d1ce7bd04cad..4de4b9d59d49 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c | |||
| @@ -2559,6 +2559,194 @@ static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper, | |||
| 2559 | fb_helper->sw_rotations |= DRM_MODE_ROTATE_0; | 2559 | fb_helper->sw_rotations |= DRM_MODE_ROTATE_0; |
| 2560 | } | 2560 | } |
| 2561 | 2561 | ||
| 2562 | static struct drm_fb_helper_crtc * | ||
| 2563 | drm_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) | ||
| 2564 | { | ||
| 2565 | int i; | ||
| 2566 | |||
| 2567 | for (i = 0; i < fb_helper->crtc_count; i++) | ||
| 2568 | if (fb_helper->crtc_info[i].mode_set.crtc == crtc) | ||
| 2569 | return &fb_helper->crtc_info[i]; | ||
| 2570 | |||
| 2571 | return NULL; | ||
| 2572 | } | ||
| 2573 | |||
| 2574 | /* Try to read the BIOS display configuration and use it for the initial config */ | ||
| 2575 | static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper, | ||
| 2576 | struct drm_fb_helper_crtc **crtcs, | ||
| 2577 | struct drm_display_mode **modes, | ||
| 2578 | struct drm_fb_offset *offsets, | ||
| 2579 | bool *enabled, int width, int height) | ||
| 2580 | { | ||
| 2581 | struct drm_device *dev = fb_helper->dev; | ||
| 2582 | unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG); | ||
| 2583 | unsigned long conn_configured, conn_seq; | ||
| 2584 | int i, j; | ||
| 2585 | bool *save_enabled; | ||
| 2586 | bool fallback = true, ret = true; | ||
| 2587 | int num_connectors_enabled = 0; | ||
| 2588 | int num_connectors_detected = 0; | ||
| 2589 | struct drm_modeset_acquire_ctx ctx; | ||
| 2590 | |||
| 2591 | save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); | ||
| 2592 | if (!save_enabled) | ||
| 2593 | return false; | ||
| 2594 | |||
| 2595 | drm_modeset_acquire_init(&ctx, 0); | ||
| 2596 | |||
| 2597 | while (drm_modeset_lock_all_ctx(dev, &ctx) != 0) | ||
| 2598 | drm_modeset_backoff(&ctx); | ||
| 2599 | |||
| 2600 | memcpy(save_enabled, enabled, count); | ||
| 2601 | conn_seq = GENMASK(count - 1, 0); | ||
| 2602 | conn_configured = 0; | ||
| 2603 | retry: | ||
| 2604 | for (i = 0; i < count; i++) { | ||
| 2605 | struct drm_fb_helper_connector *fb_conn; | ||
| 2606 | struct drm_connector *connector; | ||
| 2607 | struct drm_encoder *encoder; | ||
| 2608 | struct drm_fb_helper_crtc *new_crtc; | ||
| 2609 | |||
| 2610 | fb_conn = fb_helper->connector_info[i]; | ||
| 2611 | connector = fb_conn->connector; | ||
| 2612 | |||
| 2613 | if (conn_configured & BIT(i)) | ||
| 2614 | continue; | ||
| 2615 | |||
| 2616 | /* First pass, only consider tiled connectors */ | ||
| 2617 | if (conn_seq == GENMASK(count - 1, 0) && !connector->has_tile) | ||
| 2618 | continue; | ||
| 2619 | |||
| 2620 | if (connector->status == connector_status_connected) | ||
| 2621 | num_connectors_detected++; | ||
| 2622 | |||
| 2623 | if (!enabled[i]) { | ||
| 2624 | DRM_DEBUG_KMS("connector %s not enabled, skipping\n", | ||
| 2625 | connector->name); | ||
| 2626 | conn_configured |= BIT(i); | ||
| 2627 | continue; | ||
| 2628 | } | ||
| 2629 | |||
| 2630 | if (connector->force == DRM_FORCE_OFF) { | ||
| 2631 | DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", | ||
| 2632 | connector->name); | ||
| 2633 | enabled[i] = false; | ||
| 2634 | continue; | ||
| 2635 | } | ||
| 2636 | |||
| 2637 | encoder = connector->state->best_encoder; | ||
| 2638 | if (!encoder || WARN_ON(!connector->state->crtc)) { | ||
| 2639 | if (connector->force > DRM_FORCE_OFF) | ||
| 2640 | goto bail; | ||
| 2641 | |||
| 2642 | DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", | ||
| 2643 | connector->name); | ||
| 2644 | enabled[i] = false; | ||
| 2645 | conn_configured |= BIT(i); | ||
| 2646 | continue; | ||
| 2647 | } | ||
| 2648 | |||
| 2649 | num_connectors_enabled++; | ||
| 2650 | |||
| 2651 | new_crtc = drm_fb_helper_crtc(fb_helper, connector->state->crtc); | ||
| 2652 | |||
| 2653 | /* | ||
| 2654 | * Make sure we're not trying to drive multiple connectors | ||
| 2655 | * with a single CRTC, since our cloning support may not | ||
| 2656 | * match the BIOS. | ||
| 2657 | */ | ||
| 2658 | for (j = 0; j < count; j++) { | ||
| 2659 | if (crtcs[j] == new_crtc) { | ||
| 2660 | DRM_DEBUG_KMS("fallback: cloned configuration\n"); | ||
| 2661 | goto bail; | ||
| 2662 | } | ||
| 2663 | } | ||
| 2664 | |||
| 2665 | DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", | ||
| 2666 | connector->name); | ||
| 2667 | |||
| 2668 | /* go for command line mode first */ | ||
| 2669 | modes[i] = drm_pick_cmdline_mode(fb_conn); | ||
| 2670 | |||
| 2671 | /* try for preferred next */ | ||
| 2672 | if (!modes[i]) { | ||
| 2673 | DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", | ||
| 2674 | connector->name, connector->has_tile); | ||
| 2675 | modes[i] = drm_has_preferred_mode(fb_conn, width, | ||
| 2676 | height); | ||
| 2677 | } | ||
| 2678 | |||
| 2679 | /* No preferred mode marked by the EDID? Are there any modes? */ | ||
| 2680 | if (!modes[i] && !list_empty(&connector->modes)) { | ||
| 2681 | DRM_DEBUG_KMS("using first mode listed on connector %s\n", | ||
| 2682 | connector->name); | ||
| 2683 | modes[i] = list_first_entry(&connector->modes, | ||
| 2684 | struct drm_display_mode, | ||
| 2685 | head); | ||
| 2686 | } | ||
| 2687 | |||
| 2688 | /* last resort: use current mode */ | ||
| 2689 | if (!modes[i]) { | ||
| 2690 | /* | ||
| 2691 | * IMPORTANT: We want to use the adjusted mode (i.e. | ||
| 2692 | * after the panel fitter upscaling) as the initial | ||
| 2693 | * config, not the input mode, which is what crtc->mode | ||
| 2694 | * usually contains. But since our current | ||
| 2695 | * code puts a mode derived from the post-pfit timings | ||
| 2696 | * into crtc->mode this works out correctly. | ||
| 2697 | * | ||
| 2698 | * This is crtc->mode and not crtc->state->mode for the | ||
| 2699 | * fastboot check to work correctly. | ||
| 2700 | */ | ||
| 2701 | DRM_DEBUG_KMS("looking for current mode on connector %s\n", | ||
| 2702 | connector->name); | ||
| 2703 | modes[i] = &connector->state->crtc->mode; | ||
| 2704 | } | ||
| 2705 | crtcs[i] = new_crtc; | ||
| 2706 | |||
| 2707 | DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", | ||
| 2708 | connector->name, | ||
| 2709 | connector->state->crtc->base.id, | ||
| 2710 | connector->state->crtc->name, | ||
| 2711 | modes[i]->hdisplay, modes[i]->vdisplay, | ||
| 2712 | modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : ""); | ||
| 2713 | |||
| 2714 | fallback = false; | ||
| 2715 | conn_configured |= BIT(i); | ||
| 2716 | } | ||
| 2717 | |||
| 2718 | if (conn_configured != conn_seq) { /* repeat until no more are found */ | ||
| 2719 | conn_seq = conn_configured; | ||
| 2720 | goto retry; | ||
| 2721 | } | ||
| 2722 | |||
| 2723 | /* | ||
| 2724 | * If the BIOS didn't enable everything it could, fall back to have the | ||
| 2725 | * same user experiencing of lighting up as much as possible like the | ||
| 2726 | * fbdev helper library. | ||
| 2727 | */ | ||
| 2728 | if (num_connectors_enabled != num_connectors_detected && | ||
| 2729 | num_connectors_enabled < dev->mode_config.num_crtc) { | ||
| 2730 | DRM_DEBUG_KMS("fallback: Not all outputs enabled\n"); | ||
| 2731 | DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled, | ||
| 2732 | num_connectors_detected); | ||
| 2733 | fallback = true; | ||
| 2734 | } | ||
| 2735 | |||
| 2736 | if (fallback) { | ||
| 2737 | bail: | ||
| 2738 | DRM_DEBUG_KMS("Not using firmware configuration\n"); | ||
| 2739 | memcpy(enabled, save_enabled, count); | ||
| 2740 | ret = false; | ||
| 2741 | } | ||
| 2742 | |||
| 2743 | drm_modeset_drop_locks(&ctx); | ||
| 2744 | drm_modeset_acquire_fini(&ctx); | ||
| 2745 | |||
| 2746 | kfree(save_enabled); | ||
| 2747 | return ret; | ||
| 2748 | } | ||
| 2749 | |||
| 2562 | static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, | 2750 | static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, |
| 2563 | u32 width, u32 height) | 2751 | u32 width, u32 height) |
| 2564 | { | 2752 | { |
| @@ -2591,10 +2779,8 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper, | |||
| 2591 | DRM_DEBUG_KMS("No connectors reported connected with modes\n"); | 2779 | DRM_DEBUG_KMS("No connectors reported connected with modes\n"); |
| 2592 | drm_enable_connectors(fb_helper, enabled); | 2780 | drm_enable_connectors(fb_helper, enabled); |
| 2593 | 2781 | ||
| 2594 | if (!(fb_helper->funcs->initial_config && | 2782 | if (!drm_fb_helper_firmware_config(fb_helper, crtcs, modes, offsets, |
| 2595 | fb_helper->funcs->initial_config(fb_helper, crtcs, modes, | 2783 | enabled, width, height)) { |
| 2596 | offsets, | ||
| 2597 | enabled, width, height))) { | ||
| 2598 | memset(modes, 0, fb_helper->connector_count*sizeof(modes[0])); | 2784 | memset(modes, 0, fb_helper->connector_count*sizeof(modes[0])); |
| 2599 | memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0])); | 2785 | memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0])); |
| 2600 | memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0])); | 2786 | memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0])); |
| @@ -3322,7 +3508,7 @@ int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) | |||
| 3322 | if (ret) | 3508 | if (ret) |
| 3323 | DRM_DEV_DEBUG(dev->dev, "client hotplug ret=%d\n", ret); | 3509 | DRM_DEV_DEBUG(dev->dev, "client hotplug ret=%d\n", ret); |
| 3324 | 3510 | ||
| 3325 | drm_client_add(&fb_helper->client); | 3511 | drm_client_register(&fb_helper->client); |
| 3326 | 3512 | ||
| 3327 | return 0; | 3513 | return 0; |
| 3328 | } | 3514 | } |
diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 00d716f14173..a18da35145b7 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c | |||
| @@ -10,23 +10,17 @@ | |||
| 10 | 10 | ||
| 11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
| 12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
| 13 | #include <linux/io.h> | ||
| 13 | 14 | ||
| 14 | #include <drm/drm_format_helper.h> | 15 | #include <drm/drm_format_helper.h> |
| 15 | #include <drm/drm_framebuffer.h> | 16 | #include <drm/drm_framebuffer.h> |
| 16 | #include <drm/drm_fourcc.h> | 17 | #include <drm/drm_fourcc.h> |
| 17 | #include <drm/drm_rect.h> | 18 | #include <drm/drm_rect.h> |
| 18 | 19 | ||
| 19 | static void drm_fb_memcpy_lines(void *dst, unsigned int dst_pitch, | 20 | static unsigned int clip_offset(struct drm_rect *clip, |
| 20 | void *src, unsigned int src_pitch, | 21 | unsigned int pitch, unsigned int cpp) |
| 21 | unsigned int linelength, unsigned int lines) | ||
| 22 | { | 22 | { |
| 23 | int line; | 23 | return clip->y1 * pitch + clip->x1 * cpp; |
| 24 | |||
| 25 | for (line = 0; line < lines; line++) { | ||
| 26 | memcpy(dst, src, linelength); | ||
| 27 | src += src_pitch; | ||
| 28 | dst += dst_pitch; | ||
| 29 | } | ||
| 30 | } | 24 | } |
| 31 | 25 | ||
| 32 | /** | 26 | /** |
| @@ -43,35 +37,44 @@ void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, | |||
| 43 | struct drm_rect *clip) | 37 | struct drm_rect *clip) |
| 44 | { | 38 | { |
| 45 | unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); | 39 | unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); |
| 46 | unsigned int offset = (clip->y1 * fb->pitches[0]) + (clip->x1 * cpp); | ||
| 47 | size_t len = (clip->x2 - clip->x1) * cpp; | 40 | size_t len = (clip->x2 - clip->x1) * cpp; |
| 41 | unsigned int y, lines = clip->y2 - clip->y1; | ||
| 48 | 42 | ||
| 49 | drm_fb_memcpy_lines(dst, len, | 43 | vaddr += clip_offset(clip, fb->pitches[0], cpp); |
| 50 | vaddr + offset, fb->pitches[0], | 44 | for (y = 0; y < lines; y++) { |
| 51 | len, clip->y2 - clip->y1); | 45 | memcpy(dst, vaddr, len); |
| 46 | vaddr += fb->pitches[0]; | ||
| 47 | dst += len; | ||
| 48 | } | ||
| 52 | } | 49 | } |
| 53 | EXPORT_SYMBOL(drm_fb_memcpy); | 50 | EXPORT_SYMBOL(drm_fb_memcpy); |
| 54 | 51 | ||
| 55 | /** | 52 | /** |
| 56 | * drm_fb_memcpy_dstclip - Copy clip buffer | 53 | * drm_fb_memcpy_dstclip - Copy clip buffer |
| 57 | * @dst: Destination buffer | 54 | * @dst: Destination buffer (iomem) |
| 58 | * @vaddr: Source buffer | 55 | * @vaddr: Source buffer |
| 59 | * @fb: DRM framebuffer | 56 | * @fb: DRM framebuffer |
| 60 | * @clip: Clip rectangle area to copy | 57 | * @clip: Clip rectangle area to copy |
| 61 | * | 58 | * |
| 62 | * This function applies clipping on dst, i.e. the destination is a | 59 | * This function applies clipping on dst, i.e. the destination is a |
| 63 | * full framebuffer but only the clip rect content is copied over. | 60 | * full (iomem) framebuffer but only the clip rect content is copied over. |
| 64 | */ | 61 | */ |
| 65 | void drm_fb_memcpy_dstclip(void *dst, void *vaddr, struct drm_framebuffer *fb, | 62 | void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr, |
| 63 | struct drm_framebuffer *fb, | ||
| 66 | struct drm_rect *clip) | 64 | struct drm_rect *clip) |
| 67 | { | 65 | { |
| 68 | unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); | 66 | unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); |
| 69 | unsigned int offset = (clip->y1 * fb->pitches[0]) + (clip->x1 * cpp); | 67 | unsigned int offset = clip_offset(clip, fb->pitches[0], cpp); |
| 70 | size_t len = (clip->x2 - clip->x1) * cpp; | 68 | size_t len = (clip->x2 - clip->x1) * cpp; |
| 69 | unsigned int y, lines = clip->y2 - clip->y1; | ||
| 71 | 70 | ||
| 72 | drm_fb_memcpy_lines(dst + offset, fb->pitches[0], | 71 | vaddr += offset; |
| 73 | vaddr + offset, fb->pitches[0], | 72 | dst += offset; |
| 74 | len, clip->y2 - clip->y1); | 73 | for (y = 0; y < lines; y++) { |
| 74 | memcpy_toio(dst, vaddr, len); | ||
| 75 | vaddr += fb->pitches[0]; | ||
| 76 | dst += fb->pitches[0]; | ||
| 77 | } | ||
| 75 | } | 78 | } |
| 76 | EXPORT_SYMBOL(drm_fb_memcpy_dstclip); | 79 | EXPORT_SYMBOL(drm_fb_memcpy_dstclip); |
| 77 | 80 | ||
| @@ -110,42 +113,22 @@ void drm_fb_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb, | |||
| 110 | } | 113 | } |
| 111 | EXPORT_SYMBOL(drm_fb_swab16); | 114 | EXPORT_SYMBOL(drm_fb_swab16); |
| 112 | 115 | ||
| 113 | static void drm_fb_xrgb8888_to_rgb565_lines(void *dst, unsigned int dst_pitch, | 116 | static void drm_fb_xrgb8888_to_rgb565_line(u16 *dbuf, u32 *sbuf, |
| 114 | void *src, unsigned int src_pitch, | 117 | unsigned int pixels, |
| 115 | unsigned int src_linelength, | 118 | bool swab) |
| 116 | unsigned int lines, | ||
| 117 | bool swap) | ||
| 118 | { | 119 | { |
| 119 | unsigned int linepixels = src_linelength / sizeof(u32); | 120 | unsigned int x; |
| 120 | unsigned int x, y; | 121 | u16 val16; |
| 121 | u32 *sbuf; | 122 | |
| 122 | u16 *dbuf, val16; | 123 | for (x = 0; x < pixels; x++) { |
| 123 | 124 | val16 = ((sbuf[x] & 0x00F80000) >> 8) | | |
| 124 | /* | 125 | ((sbuf[x] & 0x0000FC00) >> 5) | |
| 125 | * The cma memory is write-combined so reads are uncached. | 126 | ((sbuf[x] & 0x000000F8) >> 3); |
| 126 | * Speed up by fetching one line at a time. | 127 | if (swab) |
| 127 | */ | 128 | dbuf[x] = swab16(val16); |
| 128 | sbuf = kmalloc(src_linelength, GFP_KERNEL); | 129 | else |
| 129 | if (!sbuf) | 130 | dbuf[x] = val16; |
| 130 | return; | ||
| 131 | |||
| 132 | for (y = 0; y < lines; y++) { | ||
| 133 | memcpy(sbuf, src, src_linelength); | ||
| 134 | dbuf = dst; | ||
| 135 | for (x = 0; x < linepixels; x++) { | ||
| 136 | val16 = ((sbuf[x] & 0x00F80000) >> 8) | | ||
| 137 | ((sbuf[x] & 0x0000FC00) >> 5) | | ||
| 138 | ((sbuf[x] & 0x000000F8) >> 3); | ||
| 139 | if (swap) | ||
| 140 | *dbuf++ = swab16(val16); | ||
| 141 | else | ||
| 142 | *dbuf++ = val16; | ||
| 143 | } | ||
| 144 | src += src_pitch; | ||
| 145 | dst += dst_pitch; | ||
| 146 | } | 131 | } |
| 147 | |||
| 148 | kfree(sbuf); | ||
| 149 | } | 132 | } |
| 150 | 133 | ||
| 151 | /** | 134 | /** |
| @@ -154,7 +137,7 @@ static void drm_fb_xrgb8888_to_rgb565_lines(void *dst, unsigned int dst_pitch, | |||
| 154 | * @vaddr: XRGB8888 source buffer | 137 | * @vaddr: XRGB8888 source buffer |
| 155 | * @fb: DRM framebuffer | 138 | * @fb: DRM framebuffer |
| 156 | * @clip: Clip rectangle area to copy | 139 | * @clip: Clip rectangle area to copy |
| 157 | * @swap: Swap bytes | 140 | * @swab: Swap bytes |
| 158 | * | 141 | * |
| 159 | * Drivers can use this function for RGB565 devices that don't natively | 142 | * Drivers can use this function for RGB565 devices that don't natively |
| 160 | * support XRGB8888. | 143 | * support XRGB8888. |
| @@ -164,109 +147,124 @@ static void drm_fb_xrgb8888_to_rgb565_lines(void *dst, unsigned int dst_pitch, | |||
| 164 | */ | 147 | */ |
| 165 | void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr, | 148 | void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr, |
| 166 | struct drm_framebuffer *fb, | 149 | struct drm_framebuffer *fb, |
| 167 | struct drm_rect *clip, bool swap) | 150 | struct drm_rect *clip, bool swab) |
| 168 | { | 151 | { |
| 169 | unsigned int src_offset = (clip->y1 * fb->pitches[0]) | 152 | size_t linepixels = clip->x2 - clip->x1; |
| 170 | + (clip->x1 * sizeof(u32)); | 153 | size_t src_len = linepixels * sizeof(u32); |
| 171 | size_t src_len = (clip->x2 - clip->x1) * sizeof(u32); | 154 | size_t dst_len = linepixels * sizeof(u16); |
| 172 | size_t dst_len = (clip->x2 - clip->x1) * sizeof(u16); | 155 | unsigned y, lines = clip->y2 - clip->y1; |
| 173 | 156 | void *sbuf; | |
| 174 | drm_fb_xrgb8888_to_rgb565_lines(dst, dst_len, | 157 | |
| 175 | vaddr + src_offset, fb->pitches[0], | 158 | /* |
| 176 | src_len, clip->y2 - clip->y1, | 159 | * The cma memory is write-combined so reads are uncached. |
| 177 | swap); | 160 | * Speed up by fetching one line at a time. |
| 161 | */ | ||
| 162 | sbuf = kmalloc(src_len, GFP_KERNEL); | ||
| 163 | if (!sbuf) | ||
| 164 | return; | ||
| 165 | |||
| 166 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); | ||
| 167 | for (y = 0; y < lines; y++) { | ||
| 168 | memcpy(sbuf, vaddr, src_len); | ||
| 169 | drm_fb_xrgb8888_to_rgb565_line(dst, sbuf, linepixels, swab); | ||
| 170 | vaddr += fb->pitches[0]; | ||
| 171 | dst += dst_len; | ||
| 172 | } | ||
| 173 | |||
| 174 | kfree(sbuf); | ||
| 178 | } | 175 | } |
| 179 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); | 176 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565); |
| 180 | 177 | ||
| 181 | /** | 178 | /** |
| 182 | * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer | 179 | * drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer |
| 183 | * @dst: RGB565 destination buffer | 180 | * @dst: RGB565 destination buffer (iomem) |
| 184 | * @dst_pitch: destination buffer pitch | 181 | * @dst_pitch: destination buffer pitch |
| 185 | * @vaddr: XRGB8888 source buffer | 182 | * @vaddr: XRGB8888 source buffer |
| 186 | * @fb: DRM framebuffer | 183 | * @fb: DRM framebuffer |
| 187 | * @clip: Clip rectangle area to copy | 184 | * @clip: Clip rectangle area to copy |
| 188 | * @swap: Swap bytes | 185 | * @swab: Swap bytes |
| 189 | * | 186 | * |
| 190 | * Drivers can use this function for RGB565 devices that don't natively | 187 | * Drivers can use this function for RGB565 devices that don't natively |
| 191 | * support XRGB8888. | 188 | * support XRGB8888. |
| 192 | * | 189 | * |
| 193 | * This function applies clipping on dst, i.e. the destination is a | 190 | * This function applies clipping on dst, i.e. the destination is a |
| 194 | * full framebuffer but only the clip rect content is copied over. | 191 | * full (iomem) framebuffer but only the clip rect content is copied over. |
| 195 | */ | 192 | */ |
| 196 | void drm_fb_xrgb8888_to_rgb565_dstclip(void *dst, unsigned int dst_pitch, | 193 | void drm_fb_xrgb8888_to_rgb565_dstclip(void __iomem *dst, unsigned int dst_pitch, |
| 197 | void *vaddr, struct drm_framebuffer *fb, | 194 | void *vaddr, struct drm_framebuffer *fb, |
| 198 | struct drm_rect *clip, bool swap) | 195 | struct drm_rect *clip, bool swab) |
| 199 | { | ||
| 200 | unsigned int src_offset = (clip->y1 * fb->pitches[0]) | ||
| 201 | + (clip->x1 * sizeof(u32)); | ||
| 202 | unsigned int dst_offset = (clip->y1 * dst_pitch) | ||
| 203 | + (clip->x1 * sizeof(u16)); | ||
| 204 | size_t src_len = (clip->x2 - clip->x1) * sizeof(u32); | ||
| 205 | |||
| 206 | drm_fb_xrgb8888_to_rgb565_lines(dst + dst_offset, dst_pitch, | ||
| 207 | vaddr + src_offset, fb->pitches[0], | ||
| 208 | src_len, clip->y2 - clip->y1, | ||
| 209 | swap); | ||
| 210 | } | ||
| 211 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip); | ||
| 212 | |||
| 213 | static void drm_fb_xrgb8888_to_rgb888_lines(void *dst, unsigned int dst_pitch, | ||
| 214 | void *src, unsigned int src_pitch, | ||
| 215 | unsigned int src_linelength, | ||
| 216 | unsigned int lines) | ||
| 217 | { | 196 | { |
| 218 | unsigned int linepixels = src_linelength / 3; | 197 | size_t linepixels = clip->x2 - clip->x1; |
| 219 | unsigned int x, y; | 198 | size_t dst_len = linepixels * sizeof(u16); |
| 220 | u32 *sbuf; | 199 | unsigned y, lines = clip->y2 - clip->y1; |
| 221 | u8 *dbuf; | 200 | void *dbuf; |
| 222 | 201 | ||
| 223 | sbuf = kmalloc(src_linelength, GFP_KERNEL); | 202 | dbuf = kmalloc(dst_len, GFP_KERNEL); |
| 224 | if (!sbuf) | 203 | if (!dbuf) |
| 225 | return; | 204 | return; |
| 226 | 205 | ||
| 206 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); | ||
| 207 | dst += clip_offset(clip, dst_pitch, sizeof(u16)); | ||
| 227 | for (y = 0; y < lines; y++) { | 208 | for (y = 0; y < lines; y++) { |
| 228 | memcpy(sbuf, src, src_linelength); | 209 | drm_fb_xrgb8888_to_rgb565_line(dbuf, vaddr, linepixels, swab); |
| 229 | dbuf = dst; | 210 | memcpy_toio(dst, dbuf, dst_len); |
| 230 | for (x = 0; x < linepixels; x++) { | 211 | vaddr += fb->pitches[0]; |
| 231 | *dbuf++ = (sbuf[x] & 0x000000FF) >> 0; | 212 | dst += dst_len; |
| 232 | *dbuf++ = (sbuf[x] & 0x0000FF00) >> 8; | ||
| 233 | *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16; | ||
| 234 | } | ||
| 235 | src += src_pitch; | ||
| 236 | dst += dst_pitch; | ||
| 237 | } | 213 | } |
| 238 | 214 | ||
| 239 | kfree(sbuf); | 215 | kfree(dbuf); |
| 216 | } | ||
| 217 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip); | ||
| 218 | |||
| 219 | static void drm_fb_xrgb8888_to_rgb888_line(u8 *dbuf, u32 *sbuf, | ||
| 220 | unsigned int pixels) | ||
| 221 | { | ||
| 222 | unsigned int x; | ||
| 223 | |||
| 224 | for (x = 0; x < pixels; x++) { | ||
| 225 | *dbuf++ = (sbuf[x] & 0x000000FF) >> 0; | ||
| 226 | *dbuf++ = (sbuf[x] & 0x0000FF00) >> 8; | ||
| 227 | *dbuf++ = (sbuf[x] & 0x00FF0000) >> 16; | ||
| 228 | } | ||
| 240 | } | 229 | } |
| 241 | 230 | ||
| 242 | /** | 231 | /** |
| 243 | * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer | 232 | * drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer |
| 244 | * @dst: RGB565 destination buffer | 233 | * @dst: RGB565 destination buffer (iomem) |
| 245 | * @dst_pitch: destination buffer pitch | 234 | * @dst_pitch: destination buffer pitch |
| 246 | * @vaddr: XRGB8888 source buffer | 235 | * @vaddr: XRGB8888 source buffer |
| 247 | * @fb: DRM framebuffer | 236 | * @fb: DRM framebuffer |
| 248 | * @clip: Clip rectangle area to copy | 237 | * @clip: Clip rectangle area to copy |
| 249 | * @dstclip: Clip destination too. | ||
| 250 | * | 238 | * |
| 251 | * Drivers can use this function for RGB888 devices that don't natively | 239 | * Drivers can use this function for RGB888 devices that don't natively |
| 252 | * support XRGB8888. | 240 | * support XRGB8888. |
| 253 | * | 241 | * |
| 254 | * This function applies clipping on dst, i.e. the destination is a | 242 | * This function applies clipping on dst, i.e. the destination is a |
| 255 | * full framebuffer but only the clip rect content is copied over. | 243 | * full (iomem) framebuffer but only the clip rect content is copied over. |
| 256 | */ | 244 | */ |
| 257 | void drm_fb_xrgb8888_to_rgb888_dstclip(void *dst, unsigned int dst_pitch, | 245 | void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch, |
| 258 | void *vaddr, struct drm_framebuffer *fb, | 246 | void *vaddr, struct drm_framebuffer *fb, |
| 259 | struct drm_rect *clip) | 247 | struct drm_rect *clip) |
| 260 | { | 248 | { |
| 261 | unsigned int src_offset = (clip->y1 * fb->pitches[0]) | 249 | size_t linepixels = clip->x2 - clip->x1; |
| 262 | + (clip->x1 * sizeof(u32)); | 250 | size_t dst_len = linepixels * 3; |
| 263 | unsigned int dst_offset = (clip->y1 * dst_pitch) | 251 | unsigned y, lines = clip->y2 - clip->y1; |
| 264 | + (clip->x1 * 3); | 252 | void *dbuf; |
| 265 | size_t src_len = (clip->x2 - clip->x1) * sizeof(u32); | 253 | |
| 266 | 254 | dbuf = kmalloc(dst_len, GFP_KERNEL); | |
| 267 | drm_fb_xrgb8888_to_rgb888_lines(dst + dst_offset, dst_pitch, | 255 | if (!dbuf) |
| 268 | vaddr + src_offset, fb->pitches[0], | 256 | return; |
| 269 | src_len, clip->y2 - clip->y1); | 257 | |
| 258 | vaddr += clip_offset(clip, fb->pitches[0], sizeof(u32)); | ||
| 259 | dst += clip_offset(clip, dst_pitch, sizeof(u16)); | ||
| 260 | for (y = 0; y < lines; y++) { | ||
| 261 | drm_fb_xrgb8888_to_rgb888_line(dbuf, vaddr, linepixels); | ||
| 262 | memcpy_toio(dst, dbuf, dst_len); | ||
| 263 | vaddr += fb->pitches[0]; | ||
| 264 | dst += dst_len; | ||
| 265 | } | ||
| 266 | |||
| 267 | kfree(dbuf); | ||
| 270 | } | 268 | } |
| 271 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip); | 269 | EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip); |
| 272 | 270 | ||
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 52c0a837a3b2..fae4676707b6 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c | |||
| @@ -646,6 +646,85 @@ void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, | |||
| 646 | } | 646 | } |
| 647 | EXPORT_SYMBOL(drm_gem_put_pages); | 647 | EXPORT_SYMBOL(drm_gem_put_pages); |
| 648 | 648 | ||
| 649 | static int objects_lookup(struct drm_file *filp, u32 *handle, int count, | ||
| 650 | struct drm_gem_object **objs) | ||
| 651 | { | ||
| 652 | int i, ret = 0; | ||
| 653 | struct drm_gem_object *obj; | ||
| 654 | |||
| 655 | spin_lock(&filp->table_lock); | ||
| 656 | |||
| 657 | for (i = 0; i < count; i++) { | ||
| 658 | /* Check if we currently have a reference on the object */ | ||
| 659 | obj = idr_find(&filp->object_idr, handle[i]); | ||
| 660 | if (!obj) { | ||
| 661 | ret = -ENOENT; | ||
| 662 | break; | ||
| 663 | } | ||
| 664 | drm_gem_object_get(obj); | ||
| 665 | objs[i] = obj; | ||
| 666 | } | ||
| 667 | spin_unlock(&filp->table_lock); | ||
| 668 | |||
| 669 | return ret; | ||
| 670 | } | ||
| 671 | |||
| 672 | /** | ||
| 673 | * drm_gem_objects_lookup - look up GEM objects from an array of handles | ||
| 674 | * @filp: DRM file private date | ||
| 675 | * @bo_handles: user pointer to array of userspace handle | ||
| 676 | * @count: size of handle array | ||
| 677 | * @objs_out: returned pointer to array of drm_gem_object pointers | ||
| 678 | * | ||
| 679 | * Takes an array of userspace handles and returns a newly allocated array of | ||
| 680 | * GEM objects. | ||
| 681 | * | ||
| 682 | * For a single handle lookup, use drm_gem_object_lookup(). | ||
| 683 | * | ||
| 684 | * Returns: | ||
| 685 | * | ||
| 686 | * @objs filled in with GEM object pointers. Returned GEM objects need to be | ||
| 687 | * released with drm_gem_object_put(). -ENOENT is returned on a lookup | ||
| 688 | * failure. 0 is returned on success. | ||
| 689 | * | ||
| 690 | */ | ||
| 691 | int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles, | ||
| 692 | int count, struct drm_gem_object ***objs_out) | ||
| 693 | { | ||
| 694 | int ret; | ||
| 695 | u32 *handles; | ||
| 696 | struct drm_gem_object **objs; | ||
| 697 | |||
| 698 | if (!count) | ||
| 699 | return 0; | ||
| 700 | |||
| 701 | objs = kvmalloc_array(count, sizeof(struct drm_gem_object *), | ||
| 702 | GFP_KERNEL | __GFP_ZERO); | ||
| 703 | if (!objs) | ||
| 704 | return -ENOMEM; | ||
| 705 | |||
| 706 | handles = kvmalloc_array(count, sizeof(u32), GFP_KERNEL); | ||
| 707 | if (!handles) { | ||
| 708 | ret = -ENOMEM; | ||
| 709 | goto out; | ||
| 710 | } | ||
| 711 | |||
| 712 | if (copy_from_user(handles, bo_handles, count * sizeof(u32))) { | ||
| 713 | ret = -EFAULT; | ||
| 714 | DRM_DEBUG("Failed to copy in GEM handles\n"); | ||
| 715 | goto out; | ||
| 716 | } | ||
| 717 | |||
| 718 | ret = objects_lookup(filp, handles, count, objs); | ||
| 719 | *objs_out = objs; | ||
| 720 | |||
| 721 | out: | ||
| 722 | kvfree(handles); | ||
| 723 | return ret; | ||
| 724 | |||
| 725 | } | ||
| 726 | EXPORT_SYMBOL(drm_gem_objects_lookup); | ||
| 727 | |||
| 649 | /** | 728 | /** |
| 650 | * drm_gem_object_lookup - look up a GEM object from its handle | 729 | * drm_gem_object_lookup - look up a GEM object from its handle |
| 651 | * @filp: DRM file private date | 730 | * @filp: DRM file private date |
| @@ -655,21 +734,15 @@ EXPORT_SYMBOL(drm_gem_put_pages); | |||
| 655 | * | 734 | * |
| 656 | * A reference to the object named by the handle if such exists on @filp, NULL | 735 | * A reference to the object named by the handle if such exists on @filp, NULL |
| 657 | * otherwise. | 736 | * otherwise. |
| 737 | * | ||
| 738 | * If looking up an array of handles, use drm_gem_objects_lookup(). | ||
| 658 | */ | 739 | */ |
| 659 | struct drm_gem_object * | 740 | struct drm_gem_object * |
| 660 | drm_gem_object_lookup(struct drm_file *filp, u32 handle) | 741 | drm_gem_object_lookup(struct drm_file *filp, u32 handle) |
| 661 | { | 742 | { |
| 662 | struct drm_gem_object *obj; | 743 | struct drm_gem_object *obj = NULL; |
| 663 | |||
| 664 | spin_lock(&filp->table_lock); | ||
| 665 | |||
| 666 | /* Check if we currently have a reference on the object */ | ||
| 667 | obj = idr_find(&filp->object_idr, handle); | ||
| 668 | if (obj) | ||
| 669 | drm_gem_object_get(obj); | ||
| 670 | |||
| 671 | spin_unlock(&filp->table_lock); | ||
| 672 | 744 | ||
| 745 | objects_lookup(filp, &handle, 1, &obj); | ||
| 673 | return obj; | 746 | return obj; |
| 674 | } | 747 | } |
| 675 | EXPORT_SYMBOL(drm_gem_object_lookup); | 748 | EXPORT_SYMBOL(drm_gem_object_lookup); |
| @@ -1294,3 +1367,96 @@ drm_gem_unlock_reservations(struct drm_gem_object **objs, int count, | |||
| 1294 | ww_acquire_fini(acquire_ctx); | 1367 | ww_acquire_fini(acquire_ctx); |
| 1295 | } | 1368 | } |
| 1296 | EXPORT_SYMBOL(drm_gem_unlock_reservations); | 1369 | EXPORT_SYMBOL(drm_gem_unlock_reservations); |
| 1370 | |||
| 1371 | /** | ||
| 1372 | * drm_gem_fence_array_add - Adds the fence to an array of fences to be | ||
| 1373 | * waited on, deduplicating fences from the same context. | ||
| 1374 | * | ||
| 1375 | * @fence_array array of dma_fence * for the job to block on. | ||
| 1376 | * @fence the dma_fence to add to the list of dependencies. | ||
| 1377 | * | ||
| 1378 | * Returns: | ||
| 1379 | * 0 on success, or an error on failing to expand the array. | ||
| 1380 | */ | ||
| 1381 | int drm_gem_fence_array_add(struct xarray *fence_array, | ||
| 1382 | struct dma_fence *fence) | ||
| 1383 | { | ||
| 1384 | struct dma_fence *entry; | ||
| 1385 | unsigned long index; | ||
| 1386 | u32 id = 0; | ||
| 1387 | int ret; | ||
| 1388 | |||
| 1389 | if (!fence) | ||
| 1390 | return 0; | ||
| 1391 | |||
| 1392 | /* Deduplicate if we already depend on a fence from the same context. | ||
| 1393 | * This lets the size of the array of deps scale with the number of | ||
| 1394 | * engines involved, rather than the number of BOs. | ||
| 1395 | */ | ||
| 1396 | xa_for_each(fence_array, index, entry) { | ||
| 1397 | if (entry->context != fence->context) | ||
| 1398 | continue; | ||
| 1399 | |||
| 1400 | if (dma_fence_is_later(fence, entry)) { | ||
| 1401 | dma_fence_put(entry); | ||
| 1402 | xa_store(fence_array, index, fence, GFP_KERNEL); | ||
| 1403 | } else { | ||
| 1404 | dma_fence_put(fence); | ||
| 1405 | } | ||
| 1406 | return 0; | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | ret = xa_alloc(fence_array, &id, fence, xa_limit_32b, GFP_KERNEL); | ||
| 1410 | if (ret != 0) | ||
| 1411 | dma_fence_put(fence); | ||
| 1412 | |||
| 1413 | return ret; | ||
| 1414 | } | ||
| 1415 | EXPORT_SYMBOL(drm_gem_fence_array_add); | ||
| 1416 | |||
| 1417 | /** | ||
| 1418 | * drm_gem_fence_array_add_implicit - Adds the implicit dependencies tracked | ||
| 1419 | * in the GEM object's reservation object to an array of dma_fences for use in | ||
| 1420 | * scheduling a rendering job. | ||
| 1421 | * | ||
| 1422 | * This should be called after drm_gem_lock_reservations() on your array of | ||
| 1423 | * GEM objects used in the job but before updating the reservations with your | ||
| 1424 | * own fences. | ||
| 1425 | * | ||
| 1426 | * @fence_array array of dma_fence * for the job to block on. | ||
| 1427 | * @obj the gem object to add new dependencies from. | ||
| 1428 | * @write whether the job might write the object (so we need to depend on | ||
| 1429 | * shared fences in the reservation object). | ||
| 1430 | */ | ||
| 1431 | int drm_gem_fence_array_add_implicit(struct xarray *fence_array, | ||
| 1432 | struct drm_gem_object *obj, | ||
| 1433 | bool write) | ||
| 1434 | { | ||
| 1435 | int ret; | ||
| 1436 | struct dma_fence **fences; | ||
| 1437 | unsigned int i, fence_count; | ||
| 1438 | |||
| 1439 | if (!write) { | ||
| 1440 | struct dma_fence *fence = | ||
| 1441 | reservation_object_get_excl_rcu(obj->resv); | ||
| 1442 | |||
| 1443 | return drm_gem_fence_array_add(fence_array, fence); | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | ret = reservation_object_get_fences_rcu(obj->resv, NULL, | ||
| 1447 | &fence_count, &fences); | ||
| 1448 | if (ret || !fence_count) | ||
| 1449 | return ret; | ||
| 1450 | |||
| 1451 | for (i = 0; i < fence_count; i++) { | ||
| 1452 | ret = drm_gem_fence_array_add(fence_array, fences[i]); | ||
| 1453 | if (ret) | ||
| 1454 | break; | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | for (; i < fence_count; i++) | ||
| 1458 | dma_fence_put(fences[i]); | ||
| 1459 | kfree(fences); | ||
| 1460 | return ret; | ||
| 1461 | } | ||
| 1462 | EXPORT_SYMBOL(drm_gem_fence_array_add_implicit); | ||
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 4a1c2023ccf0..1a346ae1599d 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c | |||
| @@ -297,8 +297,9 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) | |||
| 297 | return -ENOMEM; | 297 | return -ENOMEM; |
| 298 | dev->mode_config.prop_crtc_id = prop; | 298 | dev->mode_config.prop_crtc_id = prop; |
| 299 | 299 | ||
| 300 | prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "FB_DAMAGE_CLIPS", | 300 | prop = drm_property_create(dev, |
| 301 | 0); | 301 | DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB, |
| 302 | "FB_DAMAGE_CLIPS", 0); | ||
| 302 | if (!prop) | 303 | if (!prop) |
| 303 | return -ENOMEM; | 304 | return -ENOMEM; |
| 304 | dev->mode_config.prop_fb_damage_clips = prop; | 305 | dev->mode_config.prop_fb_damage_clips = prop; |
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index bc532e99b5dc..89db71996148 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c | |||
| @@ -285,225 +285,7 @@ out_unlock: | |||
| 285 | return ret; | 285 | return ret; |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | static struct drm_fb_helper_crtc * | ||
| 289 | intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc) | ||
| 290 | { | ||
| 291 | int i; | ||
| 292 | |||
| 293 | for (i = 0; i < fb_helper->crtc_count; i++) | ||
| 294 | if (fb_helper->crtc_info[i].mode_set.crtc == crtc) | ||
| 295 | return &fb_helper->crtc_info[i]; | ||
| 296 | |||
| 297 | return NULL; | ||
| 298 | } | ||
| 299 | |||
| 300 | /* | ||
| 301 | * Try to read the BIOS display configuration and use it for the initial | ||
| 302 | * fb configuration. | ||
| 303 | * | ||
| 304 | * The BIOS or boot loader will generally create an initial display | ||
| 305 | * configuration for us that includes some set of active pipes and displays. | ||
| 306 | * This routine tries to figure out which pipes and connectors are active | ||
| 307 | * and stuffs them into the crtcs and modes array given to us by the | ||
| 308 | * drm_fb_helper code. | ||
| 309 | * | ||
| 310 | * The overall sequence is: | ||
| 311 | * intel_fbdev_init - from driver load | ||
| 312 | * intel_fbdev_init_bios - initialize the intel_fbdev using BIOS data | ||
| 313 | * drm_fb_helper_init - build fb helper structs | ||
| 314 | * drm_fb_helper_single_add_all_connectors - more fb helper structs | ||
| 315 | * intel_fbdev_initial_config - apply the config | ||
| 316 | * drm_fb_helper_initial_config - call ->probe then register_framebuffer() | ||
| 317 | * drm_setup_crtcs - build crtc config for fbdev | ||
| 318 | * intel_fb_initial_config - find active connectors etc | ||
| 319 | * drm_fb_helper_single_fb_probe - set up fbdev | ||
| 320 | * intelfb_create - re-use or alloc fb, build out fbdev structs | ||
| 321 | * | ||
| 322 | * Note that we don't make special consideration whether we could actually | ||
| 323 | * switch to the selected modes without a full modeset. E.g. when the display | ||
| 324 | * is in VGA mode we need to recalculate watermarks and set a new high-res | ||
| 325 | * framebuffer anyway. | ||
| 326 | */ | ||
| 327 | static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, | ||
| 328 | struct drm_fb_helper_crtc **crtcs, | ||
| 329 | struct drm_display_mode **modes, | ||
| 330 | struct drm_fb_offset *offsets, | ||
| 331 | bool *enabled, int width, int height) | ||
| 332 | { | ||
| 333 | struct drm_i915_private *dev_priv = to_i915(fb_helper->dev); | ||
| 334 | unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG); | ||
| 335 | unsigned long conn_configured, conn_seq; | ||
| 336 | int i, j; | ||
| 337 | bool *save_enabled; | ||
| 338 | bool fallback = true, ret = true; | ||
| 339 | int num_connectors_enabled = 0; | ||
| 340 | int num_connectors_detected = 0; | ||
| 341 | struct drm_modeset_acquire_ctx ctx; | ||
| 342 | |||
| 343 | save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); | ||
| 344 | if (!save_enabled) | ||
| 345 | return false; | ||
| 346 | |||
| 347 | drm_modeset_acquire_init(&ctx, 0); | ||
| 348 | |||
| 349 | while (drm_modeset_lock_all_ctx(fb_helper->dev, &ctx) != 0) | ||
| 350 | drm_modeset_backoff(&ctx); | ||
| 351 | |||
| 352 | memcpy(save_enabled, enabled, count); | ||
| 353 | conn_seq = GENMASK(count - 1, 0); | ||
| 354 | conn_configured = 0; | ||
| 355 | retry: | ||
| 356 | for (i = 0; i < count; i++) { | ||
| 357 | struct drm_fb_helper_connector *fb_conn; | ||
| 358 | struct drm_connector *connector; | ||
| 359 | struct drm_encoder *encoder; | ||
| 360 | struct drm_fb_helper_crtc *new_crtc; | ||
| 361 | |||
| 362 | fb_conn = fb_helper->connector_info[i]; | ||
| 363 | connector = fb_conn->connector; | ||
| 364 | |||
| 365 | if (conn_configured & BIT(i)) | ||
| 366 | continue; | ||
| 367 | |||
| 368 | /* First pass, only consider tiled connectors */ | ||
| 369 | if (conn_seq == GENMASK(count - 1, 0) && !connector->has_tile) | ||
| 370 | continue; | ||
| 371 | |||
| 372 | if (connector->status == connector_status_connected) | ||
| 373 | num_connectors_detected++; | ||
| 374 | |||
| 375 | if (!enabled[i]) { | ||
| 376 | DRM_DEBUG_KMS("connector %s not enabled, skipping\n", | ||
| 377 | connector->name); | ||
| 378 | conn_configured |= BIT(i); | ||
| 379 | continue; | ||
| 380 | } | ||
| 381 | |||
| 382 | if (connector->force == DRM_FORCE_OFF) { | ||
| 383 | DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", | ||
| 384 | connector->name); | ||
| 385 | enabled[i] = false; | ||
| 386 | continue; | ||
| 387 | } | ||
| 388 | |||
| 389 | encoder = connector->state->best_encoder; | ||
| 390 | if (!encoder || WARN_ON(!connector->state->crtc)) { | ||
| 391 | if (connector->force > DRM_FORCE_OFF) | ||
| 392 | goto bail; | ||
| 393 | |||
| 394 | DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", | ||
| 395 | connector->name); | ||
| 396 | enabled[i] = false; | ||
| 397 | conn_configured |= BIT(i); | ||
| 398 | continue; | ||
| 399 | } | ||
| 400 | |||
| 401 | num_connectors_enabled++; | ||
| 402 | |||
| 403 | new_crtc = intel_fb_helper_crtc(fb_helper, | ||
| 404 | connector->state->crtc); | ||
| 405 | |||
| 406 | /* | ||
| 407 | * Make sure we're not trying to drive multiple connectors | ||
| 408 | * with a single CRTC, since our cloning support may not | ||
| 409 | * match the BIOS. | ||
| 410 | */ | ||
| 411 | for (j = 0; j < count; j++) { | ||
| 412 | if (crtcs[j] == new_crtc) { | ||
| 413 | DRM_DEBUG_KMS("fallback: cloned configuration\n"); | ||
| 414 | goto bail; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", | ||
| 419 | connector->name); | ||
| 420 | |||
| 421 | /* go for command line mode first */ | ||
| 422 | modes[i] = drm_pick_cmdline_mode(fb_conn); | ||
| 423 | |||
| 424 | /* try for preferred next */ | ||
| 425 | if (!modes[i]) { | ||
| 426 | DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", | ||
| 427 | connector->name, connector->has_tile); | ||
| 428 | modes[i] = drm_has_preferred_mode(fb_conn, width, | ||
| 429 | height); | ||
| 430 | } | ||
| 431 | |||
| 432 | /* No preferred mode marked by the EDID? Are there any modes? */ | ||
| 433 | if (!modes[i] && !list_empty(&connector->modes)) { | ||
| 434 | DRM_DEBUG_KMS("using first mode listed on connector %s\n", | ||
| 435 | connector->name); | ||
| 436 | modes[i] = list_first_entry(&connector->modes, | ||
| 437 | struct drm_display_mode, | ||
| 438 | head); | ||
| 439 | } | ||
| 440 | |||
| 441 | /* last resort: use current mode */ | ||
| 442 | if (!modes[i]) { | ||
| 443 | /* | ||
| 444 | * IMPORTANT: We want to use the adjusted mode (i.e. | ||
| 445 | * after the panel fitter upscaling) as the initial | ||
| 446 | * config, not the input mode, which is what crtc->mode | ||
| 447 | * usually contains. But since our current | ||
| 448 | * code puts a mode derived from the post-pfit timings | ||
| 449 | * into crtc->mode this works out correctly. | ||
| 450 | * | ||
| 451 | * This is crtc->mode and not crtc->state->mode for the | ||
| 452 | * fastboot check to work correctly. crtc_state->mode has | ||
| 453 | * I915_MODE_FLAG_INHERITED, which we clear to force check | ||
| 454 | * state. | ||
| 455 | */ | ||
| 456 | DRM_DEBUG_KMS("looking for current mode on connector %s\n", | ||
| 457 | connector->name); | ||
| 458 | modes[i] = &connector->state->crtc->mode; | ||
| 459 | } | ||
| 460 | crtcs[i] = new_crtc; | ||
| 461 | |||
| 462 | DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", | ||
| 463 | connector->name, | ||
| 464 | connector->state->crtc->base.id, | ||
| 465 | connector->state->crtc->name, | ||
| 466 | modes[i]->hdisplay, modes[i]->vdisplay, | ||
| 467 | modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :""); | ||
| 468 | |||
| 469 | fallback = false; | ||
| 470 | conn_configured |= BIT(i); | ||
| 471 | } | ||
| 472 | |||
| 473 | if (conn_configured != conn_seq) { /* repeat until no more are found */ | ||
| 474 | conn_seq = conn_configured; | ||
| 475 | goto retry; | ||
| 476 | } | ||
| 477 | |||
| 478 | /* | ||
| 479 | * If the BIOS didn't enable everything it could, fall back to have the | ||
| 480 | * same user experiencing of lighting up as much as possible like the | ||
| 481 | * fbdev helper library. | ||
| 482 | */ | ||
| 483 | if (num_connectors_enabled != num_connectors_detected && | ||
| 484 | num_connectors_enabled < INTEL_INFO(dev_priv)->num_pipes) { | ||
| 485 | DRM_DEBUG_KMS("fallback: Not all outputs enabled\n"); | ||
| 486 | DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled, | ||
| 487 | num_connectors_detected); | ||
| 488 | fallback = true; | ||
| 489 | } | ||
| 490 | |||
| 491 | if (fallback) { | ||
| 492 | bail: | ||
| 493 | DRM_DEBUG_KMS("Not using firmware configuration\n"); | ||
| 494 | memcpy(enabled, save_enabled, count); | ||
| 495 | ret = false; | ||
| 496 | } | ||
| 497 | |||
| 498 | drm_modeset_drop_locks(&ctx); | ||
| 499 | drm_modeset_acquire_fini(&ctx); | ||
| 500 | |||
| 501 | kfree(save_enabled); | ||
| 502 | return ret; | ||
| 503 | } | ||
| 504 | |||
| 505 | static const struct drm_fb_helper_funcs intel_fb_helper_funcs = { | 288 | static const struct drm_fb_helper_funcs intel_fb_helper_funcs = { |
| 506 | .initial_config = intel_fb_initial_config, | ||
| 507 | .fb_probe = intelfb_create, | 289 | .fb_probe = intelfb_create, |
| 508 | }; | 290 | }; |
| 509 | 291 | ||
diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index 1d69498bc17e..477c0f766663 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c | |||
| @@ -145,40 +145,7 @@ static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo, | |||
| 145 | if (explicit) | 145 | if (explicit) |
| 146 | return 0; | 146 | return 0; |
| 147 | 147 | ||
| 148 | /* implicit sync use bo fence in resv obj */ | 148 | return drm_gem_fence_array_add_implicit(&task->deps, &bo->gem, write); |
| 149 | if (write) { | ||
| 150 | unsigned nr_fences; | ||
| 151 | struct dma_fence **fences; | ||
| 152 | int i; | ||
| 153 | |||
| 154 | err = reservation_object_get_fences_rcu( | ||
| 155 | bo->gem.resv, NULL, &nr_fences, &fences); | ||
| 156 | if (err || !nr_fences) | ||
| 157 | return err; | ||
| 158 | |||
| 159 | for (i = 0; i < nr_fences; i++) { | ||
| 160 | err = lima_sched_task_add_dep(task, fences[i]); | ||
| 161 | if (err) | ||
| 162 | break; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* for error case free remaining fences */ | ||
| 166 | for ( ; i < nr_fences; i++) | ||
| 167 | dma_fence_put(fences[i]); | ||
| 168 | |||
| 169 | kfree(fences); | ||
| 170 | } else { | ||
| 171 | struct dma_fence *fence; | ||
| 172 | |||
| 173 | fence = reservation_object_get_excl_rcu(bo->gem.resv); | ||
| 174 | if (fence) { | ||
| 175 | err = lima_sched_task_add_dep(task, fence); | ||
| 176 | if (err) | ||
| 177 | dma_fence_put(fence); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | return err; | ||
| 182 | } | 149 | } |
| 183 | 150 | ||
| 184 | static int lima_gem_lock_bos(struct lima_bo **bos, u32 nr_bos, | 151 | static int lima_gem_lock_bos(struct lima_bo **bos, u32 nr_bos, |
| @@ -251,7 +218,7 @@ static int lima_gem_add_deps(struct drm_file *file, struct lima_submit *submit) | |||
| 251 | if (err) | 218 | if (err) |
| 252 | return err; | 219 | return err; |
| 253 | 220 | ||
| 254 | err = lima_sched_task_add_dep(submit->task, fence); | 221 | err = drm_gem_fence_array_add(&submit->task->deps, fence); |
| 255 | if (err) { | 222 | if (err) { |
| 256 | dma_fence_put(fence); | 223 | dma_fence_put(fence); |
| 257 | return err; | 224 | return err; |
diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c index 97bd9c1deb87..d53bd45f8d96 100644 --- a/drivers/gpu/drm/lima/lima_sched.c +++ b/drivers/gpu/drm/lima/lima_sched.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <linux/kthread.h> | 4 | #include <linux/kthread.h> |
| 5 | #include <linux/slab.h> | 5 | #include <linux/slab.h> |
| 6 | #include <linux/xarray.h> | ||
| 6 | 7 | ||
| 7 | #include "lima_drv.h" | 8 | #include "lima_drv.h" |
| 8 | #include "lima_sched.h" | 9 | #include "lima_sched.h" |
| @@ -126,19 +127,24 @@ int lima_sched_task_init(struct lima_sched_task *task, | |||
| 126 | 127 | ||
| 127 | task->num_bos = num_bos; | 128 | task->num_bos = num_bos; |
| 128 | task->vm = lima_vm_get(vm); | 129 | task->vm = lima_vm_get(vm); |
| 130 | |||
| 131 | xa_init_flags(&task->deps, XA_FLAGS_ALLOC); | ||
| 132 | |||
| 129 | return 0; | 133 | return 0; |
| 130 | } | 134 | } |
| 131 | 135 | ||
| 132 | void lima_sched_task_fini(struct lima_sched_task *task) | 136 | void lima_sched_task_fini(struct lima_sched_task *task) |
| 133 | { | 137 | { |
| 138 | struct dma_fence *fence; | ||
| 139 | unsigned long index; | ||
| 134 | int i; | 140 | int i; |
| 135 | 141 | ||
| 136 | drm_sched_job_cleanup(&task->base); | 142 | drm_sched_job_cleanup(&task->base); |
| 137 | 143 | ||
| 138 | for (i = 0; i < task->num_dep; i++) | 144 | xa_for_each(&task->deps, index, fence) { |
| 139 | dma_fence_put(task->dep[i]); | 145 | dma_fence_put(fence); |
| 140 | 146 | } | |
| 141 | kfree(task->dep); | 147 | xa_destroy(&task->deps); |
| 142 | 148 | ||
| 143 | if (task->bos) { | 149 | if (task->bos) { |
| 144 | for (i = 0; i < task->num_bos; i++) | 150 | for (i = 0; i < task->num_bos; i++) |
| @@ -149,42 +155,6 @@ void lima_sched_task_fini(struct lima_sched_task *task) | |||
| 149 | lima_vm_put(task->vm); | 155 | lima_vm_put(task->vm); |
| 150 | } | 156 | } |
| 151 | 157 | ||
| 152 | int lima_sched_task_add_dep(struct lima_sched_task *task, struct dma_fence *fence) | ||
| 153 | { | ||
| 154 | int i, new_dep = 4; | ||
| 155 | |||
| 156 | /* same context's fence is definitly earlier then this task */ | ||
| 157 | if (fence->context == task->base.s_fence->finished.context) { | ||
| 158 | dma_fence_put(fence); | ||
| 159 | return 0; | ||
| 160 | } | ||
| 161 | |||
| 162 | if (task->dep && task->num_dep == task->max_dep) | ||
| 163 | new_dep = task->max_dep * 2; | ||
| 164 | |||
| 165 | if (task->max_dep < new_dep) { | ||
| 166 | void *dep = krealloc(task->dep, sizeof(*task->dep) * new_dep, GFP_KERNEL); | ||
| 167 | |||
| 168 | if (!dep) | ||
| 169 | return -ENOMEM; | ||
| 170 | |||
| 171 | task->max_dep = new_dep; | ||
| 172 | task->dep = dep; | ||
| 173 | } | ||
| 174 | |||
| 175 | for (i = 0; i < task->num_dep; i++) { | ||
| 176 | if (task->dep[i]->context == fence->context && | ||
| 177 | dma_fence_is_later(fence, task->dep[i])) { | ||
| 178 | dma_fence_put(task->dep[i]); | ||
| 179 | task->dep[i] = fence; | ||
| 180 | return 0; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | task->dep[task->num_dep++] = fence; | ||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | int lima_sched_context_init(struct lima_sched_pipe *pipe, | 158 | int lima_sched_context_init(struct lima_sched_pipe *pipe, |
| 189 | struct lima_sched_context *context, | 159 | struct lima_sched_context *context, |
| 190 | atomic_t *guilty) | 160 | atomic_t *guilty) |
| @@ -213,21 +183,9 @@ static struct dma_fence *lima_sched_dependency(struct drm_sched_job *job, | |||
| 213 | struct drm_sched_entity *entity) | 183 | struct drm_sched_entity *entity) |
| 214 | { | 184 | { |
| 215 | struct lima_sched_task *task = to_lima_task(job); | 185 | struct lima_sched_task *task = to_lima_task(job); |
| 216 | int i; | ||
| 217 | |||
| 218 | for (i = 0; i < task->num_dep; i++) { | ||
| 219 | struct dma_fence *fence = task->dep[i]; | ||
| 220 | |||
| 221 | if (!task->dep[i]) | ||
| 222 | continue; | ||
| 223 | |||
| 224 | task->dep[i] = NULL; | ||
| 225 | 186 | ||
| 226 | if (!dma_fence_is_signaled(fence)) | 187 | if (!xa_empty(&task->deps)) |
| 227 | return fence; | 188 | return xa_erase(&task->deps, task->last_dep++); |
| 228 | |||
| 229 | dma_fence_put(fence); | ||
| 230 | } | ||
| 231 | 189 | ||
| 232 | return NULL; | 190 | return NULL; |
| 233 | } | 191 | } |
| @@ -353,7 +311,7 @@ static void lima_sched_free_job(struct drm_sched_job *job) | |||
| 353 | kmem_cache_free(pipe->task_slab, task); | 311 | kmem_cache_free(pipe->task_slab, task); |
| 354 | } | 312 | } |
| 355 | 313 | ||
| 356 | const struct drm_sched_backend_ops lima_sched_ops = { | 314 | static const struct drm_sched_backend_ops lima_sched_ops = { |
| 357 | .dependency = lima_sched_dependency, | 315 | .dependency = lima_sched_dependency, |
| 358 | .run_job = lima_sched_run_job, | 316 | .run_job = lima_sched_run_job, |
| 359 | .timedout_job = lima_sched_timedout_job, | 317 | .timedout_job = lima_sched_timedout_job, |
diff --git a/drivers/gpu/drm/lima/lima_sched.h b/drivers/gpu/drm/lima/lima_sched.h index b017cfa7e327..928af91c1118 100644 --- a/drivers/gpu/drm/lima/lima_sched.h +++ b/drivers/gpu/drm/lima/lima_sched.h | |||
| @@ -14,9 +14,8 @@ struct lima_sched_task { | |||
| 14 | struct lima_vm *vm; | 14 | struct lima_vm *vm; |
| 15 | void *frame; | 15 | void *frame; |
| 16 | 16 | ||
| 17 | struct dma_fence **dep; | 17 | struct xarray deps; |
| 18 | int num_dep; | 18 | unsigned long last_dep; |
| 19 | int max_dep; | ||
| 20 | 19 | ||
| 21 | struct lima_bo **bos; | 20 | struct lima_bo **bos; |
| 22 | int num_bos; | 21 | int num_bos; |
| @@ -78,7 +77,6 @@ int lima_sched_task_init(struct lima_sched_task *task, | |||
| 78 | struct lima_bo **bos, int num_bos, | 77 | struct lima_bo **bos, int num_bos, |
| 79 | struct lima_vm *vm); | 78 | struct lima_vm *vm); |
| 80 | void lima_sched_task_fini(struct lima_sched_task *task); | 79 | void lima_sched_task_fini(struct lima_sched_task *task); |
| 81 | int lima_sched_task_add_dep(struct lima_sched_task *task, struct dma_fence *fence); | ||
| 82 | 80 | ||
| 83 | int lima_sched_context_init(struct lima_sched_pipe *pipe, | 81 | int lima_sched_context_init(struct lima_sched_pipe *pipe, |
| 84 | struct lima_sched_context *context, | 82 | struct lima_sched_context *context, |
diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index faf1b1b0357c..72b01e6be0d9 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c | |||
| @@ -90,6 +90,18 @@ static irqreturn_t meson_irq(int irq, void *arg) | |||
| 90 | return IRQ_HANDLED; | 90 | return IRQ_HANDLED; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | static int meson_dumb_create(struct drm_file *file, struct drm_device *dev, | ||
| 94 | struct drm_mode_create_dumb *args) | ||
| 95 | { | ||
| 96 | /* | ||
| 97 | * We need 64bytes aligned stride, and PAGE aligned size | ||
| 98 | */ | ||
| 99 | args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64); | ||
| 100 | args->size = PAGE_ALIGN(args->pitch * args->height); | ||
| 101 | |||
| 102 | return drm_gem_cma_dumb_create_internal(file, dev, args); | ||
| 103 | } | ||
| 104 | |||
| 93 | DEFINE_DRM_GEM_CMA_FOPS(fops); | 105 | DEFINE_DRM_GEM_CMA_FOPS(fops); |
| 94 | 106 | ||
| 95 | static struct drm_driver meson_driver = { | 107 | static struct drm_driver meson_driver = { |
| @@ -112,7 +124,7 @@ static struct drm_driver meson_driver = { | |||
| 112 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | 124 | .gem_prime_mmap = drm_gem_cma_prime_mmap, |
| 113 | 125 | ||
| 114 | /* GEM Ops */ | 126 | /* GEM Ops */ |
| 115 | .dumb_create = drm_gem_cma_dumb_create, | 127 | .dumb_create = meson_dumb_create, |
| 116 | .gem_free_object_unlocked = drm_gem_cma_free_object, | 128 | .gem_free_object_unlocked = drm_gem_cma_free_object, |
| 117 | .gem_vm_ops = &drm_gem_cma_vm_ops, | 129 | .gem_vm_ops = &drm_gem_cma_vm_ops, |
| 118 | 130 | ||
diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c index 0169c98b01c9..b59072342cae 100644 --- a/drivers/gpu/drm/meson/meson_viu.c +++ b/drivers/gpu/drm/meson/meson_viu.c | |||
| @@ -90,8 +90,8 @@ static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = { | |||
| 90 | EOTF_COEFF_RIGHTSHIFT /* right shift */ | 90 | EOTF_COEFF_RIGHTSHIFT /* right shift */ |
| 91 | }; | 91 | }; |
| 92 | 92 | ||
| 93 | void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv, int *m, | 93 | static void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv, |
| 94 | bool csc_on) | 94 | int *m, bool csc_on) |
| 95 | { | 95 | { |
| 96 | /* VPP WRAP OSD1 matrix */ | 96 | /* VPP WRAP OSD1 matrix */ |
| 97 | writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff), | 97 | writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff), |
| @@ -118,8 +118,8 @@ void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv, int *m, | |||
| 118 | priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL)); | 118 | priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL)); |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | void meson_viu_set_osd_matrix(struct meson_drm *priv, | 121 | static void meson_viu_set_osd_matrix(struct meson_drm *priv, |
| 122 | enum viu_matrix_sel_e m_select, | 122 | enum viu_matrix_sel_e m_select, |
| 123 | int *m, bool csc_on) | 123 | int *m, bool csc_on) |
| 124 | { | 124 | { |
| 125 | if (m_select == VIU_MATRIX_OSD) { | 125 | if (m_select == VIU_MATRIX_OSD) { |
| @@ -187,10 +187,10 @@ void meson_viu_set_osd_matrix(struct meson_drm *priv, | |||
| 187 | #define OSD_EOTF_LUT_SIZE 33 | 187 | #define OSD_EOTF_LUT_SIZE 33 |
| 188 | #define OSD_OETF_LUT_SIZE 41 | 188 | #define OSD_OETF_LUT_SIZE 41 |
| 189 | 189 | ||
| 190 | void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel, | 190 | static void |
| 191 | unsigned int *r_map, unsigned int *g_map, | 191 | meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel, |
| 192 | unsigned int *b_map, | 192 | unsigned int *r_map, unsigned int *g_map, |
| 193 | bool csc_on) | 193 | unsigned int *b_map, bool csc_on) |
| 194 | { | 194 | { |
| 195 | unsigned int addr_port; | 195 | unsigned int addr_port; |
| 196 | unsigned int data_port; | 196 | unsigned int data_port; |
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 8fee7a8b29d9..569be4efd8d1 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c | |||
| @@ -3025,6 +3025,34 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = { | |||
| 3025 | .lanes = 4, | 3025 | .lanes = 4, |
| 3026 | }; | 3026 | }; |
| 3027 | 3027 | ||
| 3028 | static const struct drm_display_mode lg_acx467akm_7_mode = { | ||
| 3029 | .clock = 150000, | ||
| 3030 | .hdisplay = 1080, | ||
| 3031 | .hsync_start = 1080 + 2, | ||
| 3032 | .hsync_end = 1080 + 2 + 2, | ||
| 3033 | .htotal = 1080 + 2 + 2 + 2, | ||
| 3034 | .vdisplay = 1920, | ||
| 3035 | .vsync_start = 1920 + 2, | ||
| 3036 | .vsync_end = 1920 + 2 + 2, | ||
| 3037 | .vtotal = 1920 + 2 + 2 + 2, | ||
| 3038 | .vrefresh = 60, | ||
| 3039 | }; | ||
| 3040 | |||
| 3041 | static const struct panel_desc_dsi lg_acx467akm_7 = { | ||
| 3042 | .desc = { | ||
| 3043 | .modes = &lg_acx467akm_7_mode, | ||
| 3044 | .num_modes = 1, | ||
| 3045 | .bpc = 8, | ||
| 3046 | .size = { | ||
| 3047 | .width = 62, | ||
| 3048 | .height = 110, | ||
| 3049 | }, | ||
| 3050 | }, | ||
| 3051 | .flags = 0, | ||
| 3052 | .format = MIPI_DSI_FMT_RGB888, | ||
| 3053 | .lanes = 4, | ||
| 3054 | }; | ||
| 3055 | |||
| 3028 | static const struct of_device_id dsi_of_match[] = { | 3056 | static const struct of_device_id dsi_of_match[] = { |
| 3029 | { | 3057 | { |
| 3030 | .compatible = "auo,b080uan01", | 3058 | .compatible = "auo,b080uan01", |
| @@ -3042,6 +3070,9 @@ static const struct of_device_id dsi_of_match[] = { | |||
| 3042 | .compatible = "panasonic,vvx10f004b00", | 3070 | .compatible = "panasonic,vvx10f004b00", |
| 3043 | .data = &panasonic_vvx10f004b00 | 3071 | .data = &panasonic_vvx10f004b00 |
| 3044 | }, { | 3072 | }, { |
| 3073 | .compatible = "lg,acx467akm-7", | ||
| 3074 | .data = &lg_acx467akm_7 | ||
| 3075 | }, { | ||
| 3045 | /* sentinel */ | 3076 | /* sentinel */ |
| 3046 | } | 3077 | } |
| 3047 | }; | 3078 | }; |
diff --git a/drivers/gpu/drm/panfrost/Kconfig b/drivers/gpu/drm/panfrost/Kconfig new file mode 100644 index 000000000000..7f5e572daa2d --- /dev/null +++ b/drivers/gpu/drm/panfrost/Kconfig | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | config DRM_PANFROST | ||
| 4 | tristate "Panfrost (DRM support for ARM Mali Midgard/Bifrost GPUs)" | ||
| 5 | depends on DRM | ||
| 6 | depends on ARM || ARM64 || COMPILE_TEST | ||
| 7 | depends on MMU | ||
| 8 | select DRM_SCHED | ||
| 9 | select IOMMU_SUPPORT | ||
| 10 | select IOMMU_IO_PGTABLE_LPAE | ||
| 11 | select DRM_GEM_SHMEM_HELPER | ||
| 12 | help | ||
| 13 | DRM driver for ARM Mali Midgard (T6xx, T7xx, T8xx) and | ||
| 14 | Bifrost (G3x, G5x, G7x) GPUs. | ||
diff --git a/drivers/gpu/drm/panfrost/Makefile b/drivers/gpu/drm/panfrost/Makefile new file mode 100644 index 000000000000..6de72d13c58f --- /dev/null +++ b/drivers/gpu/drm/panfrost/Makefile | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | panfrost-y := \ | ||
| 4 | panfrost_drv.o \ | ||
| 5 | panfrost_device.o \ | ||
| 6 | panfrost_devfreq.o \ | ||
| 7 | panfrost_gem.o \ | ||
| 8 | panfrost_gpu.o \ | ||
| 9 | panfrost_job.o \ | ||
| 10 | panfrost_mmu.o | ||
| 11 | |||
| 12 | obj-$(CONFIG_DRM_PANFROST) += panfrost.o | ||
diff --git a/drivers/gpu/drm/panfrost/TODO b/drivers/gpu/drm/panfrost/TODO new file mode 100644 index 000000000000..c2e44add37d8 --- /dev/null +++ b/drivers/gpu/drm/panfrost/TODO | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | - Thermal support. | ||
| 2 | |||
| 3 | - Bifrost support: | ||
| 4 | - DT bindings (Neil, WIP) | ||
| 5 | - MMU page table format and address space setup | ||
| 6 | - Bifrost specific feature and issue handling | ||
| 7 | - Coherent DMA support | ||
| 8 | |||
| 9 | - Support for 2MB pages. The io-pgtable code already supports this. Finishing | ||
| 10 | support involves either copying or adapting the iommu API to handle passing | ||
| 11 | aligned addresses and sizes to the io-pgtable code. | ||
| 12 | |||
| 13 | - Per FD address space support. The h/w supports multiple addresses spaces. | ||
| 14 | The hard part is handling when more address spaces are needed than what | ||
| 15 | the h/w provides. | ||
| 16 | |||
| 17 | - Support pinning pages on demand (GPU page faults). | ||
| 18 | |||
| 19 | - Support userspace controlled GPU virtual addresses. Needed for Vulkan. (Tomeu) | ||
| 20 | |||
| 21 | - Support for madvise and a shrinker. | ||
| 22 | |||
| 23 | - Compute job support. So called 'compute only' jobs need to be plumbed up to | ||
| 24 | userspace. | ||
| 25 | |||
| 26 | - Performance counter support. (Boris) | ||
| 27 | |||
diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c new file mode 100644 index 000000000000..a8121ae67ee3 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c | |||
| @@ -0,0 +1,218 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright 2019 Collabora ltd. */ | ||
| 3 | #include <linux/devfreq.h> | ||
| 4 | #include <linux/platform_device.h> | ||
| 5 | #include <linux/pm_opp.h> | ||
| 6 | #include <linux/clk.h> | ||
| 7 | #include <linux/regulator/consumer.h> | ||
| 8 | |||
| 9 | #include "panfrost_device.h" | ||
| 10 | #include "panfrost_features.h" | ||
| 11 | #include "panfrost_issues.h" | ||
| 12 | #include "panfrost_gpu.h" | ||
| 13 | #include "panfrost_regs.h" | ||
| 14 | |||
| 15 | static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot); | ||
| 16 | |||
| 17 | static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, | ||
| 18 | u32 flags) | ||
| 19 | { | ||
| 20 | struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); | ||
| 21 | struct dev_pm_opp *opp; | ||
| 22 | unsigned long old_clk_rate = pfdev->devfreq.cur_freq; | ||
| 23 | unsigned long target_volt, target_rate; | ||
| 24 | int err; | ||
| 25 | |||
| 26 | opp = devfreq_recommended_opp(dev, freq, flags); | ||
| 27 | if (IS_ERR(opp)) | ||
| 28 | return PTR_ERR(opp); | ||
| 29 | |||
| 30 | target_rate = dev_pm_opp_get_freq(opp); | ||
| 31 | target_volt = dev_pm_opp_get_voltage(opp); | ||
| 32 | dev_pm_opp_put(opp); | ||
| 33 | |||
| 34 | if (old_clk_rate == target_rate) | ||
| 35 | return 0; | ||
| 36 | |||
| 37 | /* | ||
| 38 | * If frequency scaling from low to high, adjust voltage first. | ||
| 39 | * If frequency scaling from high to low, adjust frequency first. | ||
| 40 | */ | ||
| 41 | if (old_clk_rate < target_rate) { | ||
| 42 | err = regulator_set_voltage(pfdev->regulator, target_volt, | ||
| 43 | target_volt); | ||
| 44 | if (err) { | ||
| 45 | dev_err(dev, "Cannot set voltage %lu uV\n", | ||
| 46 | target_volt); | ||
| 47 | return err; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | err = clk_set_rate(pfdev->clock, target_rate); | ||
| 52 | if (err) { | ||
| 53 | dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate, | ||
| 54 | err); | ||
| 55 | regulator_set_voltage(pfdev->regulator, pfdev->devfreq.cur_volt, | ||
| 56 | pfdev->devfreq.cur_volt); | ||
| 57 | return err; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (old_clk_rate > target_rate) { | ||
| 61 | err = regulator_set_voltage(pfdev->regulator, target_volt, | ||
| 62 | target_volt); | ||
| 63 | if (err) | ||
| 64 | dev_err(dev, "Cannot set voltage %lu uV\n", target_volt); | ||
| 65 | } | ||
| 66 | |||
| 67 | pfdev->devfreq.cur_freq = target_rate; | ||
| 68 | pfdev->devfreq.cur_volt = target_volt; | ||
| 69 | |||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | static void panfrost_devfreq_reset(struct panfrost_device *pfdev) | ||
| 74 | { | ||
| 75 | ktime_t now = ktime_get(); | ||
| 76 | int i; | ||
| 77 | |||
| 78 | for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
| 79 | pfdev->devfreq.slot[i].busy_time = 0; | ||
| 80 | pfdev->devfreq.slot[i].idle_time = 0; | ||
| 81 | pfdev->devfreq.slot[i].time_last_update = now; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | static int panfrost_devfreq_get_dev_status(struct device *dev, | ||
| 86 | struct devfreq_dev_status *status) | ||
| 87 | { | ||
| 88 | struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); | ||
| 89 | int i; | ||
| 90 | |||
| 91 | for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
| 92 | panfrost_devfreq_update_utilization(pfdev, i); | ||
| 93 | } | ||
| 94 | |||
| 95 | status->current_frequency = clk_get_rate(pfdev->clock); | ||
| 96 | status->total_time = ktime_to_ns(ktime_add(pfdev->devfreq.slot[0].busy_time, | ||
| 97 | pfdev->devfreq.slot[0].idle_time)); | ||
| 98 | |||
| 99 | status->busy_time = 0; | ||
| 100 | for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
| 101 | status->busy_time += ktime_to_ns(pfdev->devfreq.slot[i].busy_time); | ||
| 102 | } | ||
| 103 | |||
| 104 | /* We're scheduling only to one core atm, so don't divide for now */ | ||
| 105 | /* status->busy_time /= NUM_JOB_SLOTS; */ | ||
| 106 | |||
| 107 | panfrost_devfreq_reset(pfdev); | ||
| 108 | |||
| 109 | dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n", status->busy_time, | ||
| 110 | status->total_time, | ||
| 111 | status->busy_time / (status->total_time / 100), | ||
| 112 | status->current_frequency / 1000 / 1000); | ||
| 113 | |||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | static int panfrost_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) | ||
| 118 | { | ||
| 119 | struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); | ||
| 120 | |||
| 121 | *freq = pfdev->devfreq.cur_freq; | ||
| 122 | |||
| 123 | return 0; | ||
| 124 | } | ||
| 125 | |||
| 126 | static struct devfreq_dev_profile panfrost_devfreq_profile = { | ||
| 127 | .polling_ms = 50, /* ~3 frames */ | ||
| 128 | .target = panfrost_devfreq_target, | ||
| 129 | .get_dev_status = panfrost_devfreq_get_dev_status, | ||
| 130 | .get_cur_freq = panfrost_devfreq_get_cur_freq, | ||
| 131 | }; | ||
| 132 | |||
| 133 | int panfrost_devfreq_init(struct panfrost_device *pfdev) | ||
| 134 | { | ||
| 135 | int ret; | ||
| 136 | struct dev_pm_opp *opp; | ||
| 137 | |||
| 138 | if (!pfdev->regulator) | ||
| 139 | return 0; | ||
| 140 | |||
| 141 | ret = dev_pm_opp_of_add_table(&pfdev->pdev->dev); | ||
| 142 | if (ret == -ENODEV) /* Optional, continue without devfreq */ | ||
| 143 | return 0; | ||
| 144 | |||
| 145 | panfrost_devfreq_reset(pfdev); | ||
| 146 | |||
| 147 | pfdev->devfreq.cur_freq = clk_get_rate(pfdev->clock); | ||
| 148 | |||
| 149 | opp = devfreq_recommended_opp(&pfdev->pdev->dev, &pfdev->devfreq.cur_freq, 0); | ||
| 150 | if (IS_ERR(opp)) | ||
| 151 | return PTR_ERR(opp); | ||
| 152 | |||
| 153 | panfrost_devfreq_profile.initial_freq = pfdev->devfreq.cur_freq; | ||
| 154 | dev_pm_opp_put(opp); | ||
| 155 | |||
| 156 | pfdev->devfreq.devfreq = devm_devfreq_add_device(&pfdev->pdev->dev, | ||
| 157 | &panfrost_devfreq_profile, "simple_ondemand", NULL); | ||
| 158 | if (IS_ERR(pfdev->devfreq.devfreq)) { | ||
| 159 | DRM_DEV_ERROR(&pfdev->pdev->dev, "Couldn't initialize GPU devfreq\n"); | ||
| 160 | ret = PTR_ERR(pfdev->devfreq.devfreq); | ||
| 161 | pfdev->devfreq.devfreq = NULL; | ||
| 162 | return ret; | ||
| 163 | } | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | void panfrost_devfreq_resume(struct panfrost_device *pfdev) | ||
| 169 | { | ||
| 170 | int i; | ||
| 171 | |||
| 172 | if (!pfdev->devfreq.devfreq) | ||
| 173 | return; | ||
| 174 | |||
| 175 | panfrost_devfreq_reset(pfdev); | ||
| 176 | for (i = 0; i < NUM_JOB_SLOTS; i++) | ||
| 177 | pfdev->devfreq.slot[i].busy = false; | ||
| 178 | |||
| 179 | devfreq_resume_device(pfdev->devfreq.devfreq); | ||
| 180 | } | ||
| 181 | |||
| 182 | void panfrost_devfreq_suspend(struct panfrost_device *pfdev) | ||
| 183 | { | ||
| 184 | if (!pfdev->devfreq.devfreq) | ||
| 185 | return; | ||
| 186 | |||
| 187 | devfreq_suspend_device(pfdev->devfreq.devfreq); | ||
| 188 | } | ||
| 189 | |||
| 190 | static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot) | ||
| 191 | { | ||
| 192 | struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot]; | ||
| 193 | ktime_t now; | ||
| 194 | ktime_t last; | ||
| 195 | |||
| 196 | if (!pfdev->devfreq.devfreq) | ||
| 197 | return; | ||
| 198 | |||
| 199 | now = ktime_get(); | ||
| 200 | last = pfdev->devfreq.slot[slot].time_last_update; | ||
| 201 | |||
| 202 | /* If we last recorded a transition to busy, we have been idle since */ | ||
| 203 | if (devfreq_slot->busy) | ||
| 204 | pfdev->devfreq.slot[slot].busy_time += ktime_sub(now, last); | ||
| 205 | else | ||
| 206 | pfdev->devfreq.slot[slot].idle_time += ktime_sub(now, last); | ||
| 207 | |||
| 208 | pfdev->devfreq.slot[slot].time_last_update = now; | ||
| 209 | } | ||
| 210 | |||
| 211 | /* The job scheduler is expected to call this at every transition busy <-> idle */ | ||
| 212 | void panfrost_devfreq_record_transition(struct panfrost_device *pfdev, int slot) | ||
| 213 | { | ||
| 214 | struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot]; | ||
| 215 | |||
| 216 | panfrost_devfreq_update_utilization(pfdev, slot); | ||
| 217 | devfreq_slot->busy = !devfreq_slot->busy; | ||
| 218 | } | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.h b/drivers/gpu/drm/panfrost/panfrost_devfreq.h new file mode 100644 index 000000000000..eb999531ed90 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* Copyright 2019 Collabora ltd. */ | ||
| 3 | |||
| 4 | #ifndef __PANFROST_DEVFREQ_H__ | ||
| 5 | #define __PANFROST_DEVFREQ_H__ | ||
| 6 | |||
| 7 | int panfrost_devfreq_init(struct panfrost_device *pfdev); | ||
| 8 | |||
| 9 | void panfrost_devfreq_resume(struct panfrost_device *pfdev); | ||
| 10 | void panfrost_devfreq_suspend(struct panfrost_device *pfdev); | ||
| 11 | |||
| 12 | void panfrost_devfreq_record_transition(struct panfrost_device *pfdev, int slot); | ||
| 13 | |||
| 14 | #endif /* __PANFROST_DEVFREQ_H__ */ | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c new file mode 100644 index 000000000000..91e8fb0f2b25 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_device.c | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ | ||
| 3 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 4 | |||
| 5 | #include <linux/clk.h> | ||
| 6 | #include <linux/reset.h> | ||
| 7 | #include <linux/platform_device.h> | ||
| 8 | #include <linux/pm_runtime.h> | ||
| 9 | #include <linux/regulator/consumer.h> | ||
| 10 | |||
| 11 | #include "panfrost_device.h" | ||
| 12 | #include "panfrost_devfreq.h" | ||
| 13 | #include "panfrost_features.h" | ||
| 14 | #include "panfrost_gpu.h" | ||
| 15 | #include "panfrost_job.h" | ||
| 16 | #include "panfrost_mmu.h" | ||
| 17 | |||
| 18 | static int panfrost_reset_init(struct panfrost_device *pfdev) | ||
| 19 | { | ||
| 20 | int err; | ||
| 21 | |||
| 22 | pfdev->rstc = devm_reset_control_array_get(pfdev->dev, false, true); | ||
| 23 | if (IS_ERR(pfdev->rstc)) { | ||
| 24 | dev_err(pfdev->dev, "get reset failed %ld\n", PTR_ERR(pfdev->rstc)); | ||
| 25 | return PTR_ERR(pfdev->rstc); | ||
| 26 | } | ||
| 27 | |||
| 28 | err = reset_control_deassert(pfdev->rstc); | ||
| 29 | if (err) | ||
| 30 | return err; | ||
| 31 | |||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | static void panfrost_reset_fini(struct panfrost_device *pfdev) | ||
| 36 | { | ||
| 37 | reset_control_assert(pfdev->rstc); | ||
| 38 | } | ||
| 39 | |||
| 40 | static int panfrost_clk_init(struct panfrost_device *pfdev) | ||
| 41 | { | ||
| 42 | int err; | ||
| 43 | unsigned long rate; | ||
| 44 | |||
| 45 | pfdev->clock = devm_clk_get(pfdev->dev, NULL); | ||
| 46 | if (IS_ERR(pfdev->clock)) { | ||
| 47 | dev_err(pfdev->dev, "get clock failed %ld\n", PTR_ERR(pfdev->clock)); | ||
| 48 | return PTR_ERR(pfdev->clock); | ||
| 49 | } | ||
| 50 | |||
| 51 | rate = clk_get_rate(pfdev->clock); | ||
| 52 | dev_info(pfdev->dev, "clock rate = %lu\n", rate); | ||
| 53 | |||
| 54 | err = clk_prepare_enable(pfdev->clock); | ||
| 55 | if (err) | ||
| 56 | return err; | ||
| 57 | |||
| 58 | return 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | static void panfrost_clk_fini(struct panfrost_device *pfdev) | ||
| 62 | { | ||
| 63 | clk_disable_unprepare(pfdev->clock); | ||
| 64 | } | ||
| 65 | |||
| 66 | static int panfrost_regulator_init(struct panfrost_device *pfdev) | ||
| 67 | { | ||
| 68 | int ret; | ||
| 69 | |||
| 70 | pfdev->regulator = devm_regulator_get_optional(pfdev->dev, "mali"); | ||
| 71 | if (IS_ERR(pfdev->regulator)) { | ||
| 72 | ret = PTR_ERR(pfdev->regulator); | ||
| 73 | pfdev->regulator = NULL; | ||
| 74 | if (ret == -ENODEV) | ||
| 75 | return 0; | ||
| 76 | dev_err(pfdev->dev, "failed to get regulator: %d\n", ret); | ||
| 77 | return ret; | ||
| 78 | } | ||
| 79 | |||
| 80 | ret = regulator_enable(pfdev->regulator); | ||
| 81 | if (ret < 0) { | ||
| 82 | dev_err(pfdev->dev, "failed to enable regulator: %d\n", ret); | ||
| 83 | return ret; | ||
| 84 | } | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | static void panfrost_regulator_fini(struct panfrost_device *pfdev) | ||
| 90 | { | ||
| 91 | if (pfdev->regulator) | ||
| 92 | regulator_disable(pfdev->regulator); | ||
| 93 | } | ||
| 94 | |||
| 95 | int panfrost_device_init(struct panfrost_device *pfdev) | ||
| 96 | { | ||
| 97 | int err; | ||
| 98 | struct resource *res; | ||
| 99 | |||
| 100 | mutex_init(&pfdev->sched_lock); | ||
| 101 | INIT_LIST_HEAD(&pfdev->scheduled_jobs); | ||
| 102 | |||
| 103 | spin_lock_init(&pfdev->hwaccess_lock); | ||
| 104 | |||
| 105 | err = panfrost_clk_init(pfdev); | ||
| 106 | if (err) { | ||
| 107 | dev_err(pfdev->dev, "clk init failed %d\n", err); | ||
| 108 | return err; | ||
| 109 | } | ||
| 110 | |||
| 111 | err = panfrost_regulator_init(pfdev); | ||
| 112 | if (err) { | ||
| 113 | dev_err(pfdev->dev, "regulator init failed %d\n", err); | ||
| 114 | goto err_out0; | ||
| 115 | } | ||
| 116 | |||
| 117 | err = panfrost_reset_init(pfdev); | ||
| 118 | if (err) { | ||
| 119 | dev_err(pfdev->dev, "reset init failed %d\n", err); | ||
| 120 | goto err_out1; | ||
| 121 | } | ||
| 122 | |||
| 123 | res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0); | ||
| 124 | pfdev->iomem = devm_ioremap_resource(pfdev->dev, res); | ||
| 125 | if (IS_ERR(pfdev->iomem)) { | ||
| 126 | dev_err(pfdev->dev, "failed to ioremap iomem\n"); | ||
| 127 | err = PTR_ERR(pfdev->iomem); | ||
| 128 | goto err_out2; | ||
| 129 | } | ||
| 130 | |||
| 131 | err = panfrost_gpu_init(pfdev); | ||
| 132 | if (err) | ||
| 133 | goto err_out2; | ||
| 134 | |||
| 135 | err = panfrost_mmu_init(pfdev); | ||
| 136 | if (err) | ||
| 137 | goto err_out3; | ||
| 138 | |||
| 139 | err = panfrost_job_init(pfdev); | ||
| 140 | if (err) | ||
| 141 | goto err_out4; | ||
| 142 | |||
| 143 | /* runtime PM will wake us up later */ | ||
| 144 | panfrost_gpu_power_off(pfdev); | ||
| 145 | |||
| 146 | pm_runtime_set_active(pfdev->dev); | ||
| 147 | pm_runtime_get_sync(pfdev->dev); | ||
| 148 | pm_runtime_mark_last_busy(pfdev->dev); | ||
| 149 | pm_runtime_put_autosuspend(pfdev->dev); | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | err_out4: | ||
| 153 | panfrost_mmu_fini(pfdev); | ||
| 154 | err_out3: | ||
| 155 | panfrost_gpu_fini(pfdev); | ||
| 156 | err_out2: | ||
| 157 | panfrost_reset_fini(pfdev); | ||
| 158 | err_out1: | ||
| 159 | panfrost_regulator_fini(pfdev); | ||
| 160 | err_out0: | ||
| 161 | panfrost_clk_fini(pfdev); | ||
| 162 | return err; | ||
| 163 | } | ||
| 164 | |||
| 165 | void panfrost_device_fini(struct panfrost_device *pfdev) | ||
| 166 | { | ||
| 167 | panfrost_regulator_fini(pfdev); | ||
| 168 | panfrost_clk_fini(pfdev); | ||
| 169 | } | ||
| 170 | |||
| 171 | const char *panfrost_exception_name(struct panfrost_device *pfdev, u32 exception_code) | ||
| 172 | { | ||
| 173 | switch (exception_code) { | ||
| 174 | /* Non-Fault Status code */ | ||
| 175 | case 0x00: return "NOT_STARTED/IDLE/OK"; | ||
| 176 | case 0x01: return "DONE"; | ||
| 177 | case 0x02: return "INTERRUPTED"; | ||
| 178 | case 0x03: return "STOPPED"; | ||
| 179 | case 0x04: return "TERMINATED"; | ||
| 180 | case 0x08: return "ACTIVE"; | ||
| 181 | /* Job exceptions */ | ||
| 182 | case 0x40: return "JOB_CONFIG_FAULT"; | ||
| 183 | case 0x41: return "JOB_POWER_FAULT"; | ||
| 184 | case 0x42: return "JOB_READ_FAULT"; | ||
| 185 | case 0x43: return "JOB_WRITE_FAULT"; | ||
| 186 | case 0x44: return "JOB_AFFINITY_FAULT"; | ||
| 187 | case 0x48: return "JOB_BUS_FAULT"; | ||
| 188 | case 0x50: return "INSTR_INVALID_PC"; | ||
| 189 | case 0x51: return "INSTR_INVALID_ENC"; | ||
| 190 | case 0x52: return "INSTR_TYPE_MISMATCH"; | ||
| 191 | case 0x53: return "INSTR_OPERAND_FAULT"; | ||
| 192 | case 0x54: return "INSTR_TLS_FAULT"; | ||
| 193 | case 0x55: return "INSTR_BARRIER_FAULT"; | ||
| 194 | case 0x56: return "INSTR_ALIGN_FAULT"; | ||
| 195 | case 0x58: return "DATA_INVALID_FAULT"; | ||
| 196 | case 0x59: return "TILE_RANGE_FAULT"; | ||
| 197 | case 0x5A: return "ADDR_RANGE_FAULT"; | ||
| 198 | case 0x60: return "OUT_OF_MEMORY"; | ||
| 199 | /* GPU exceptions */ | ||
| 200 | case 0x80: return "DELAYED_BUS_FAULT"; | ||
| 201 | case 0x88: return "SHAREABILITY_FAULT"; | ||
| 202 | /* MMU exceptions */ | ||
| 203 | case 0xC1: return "TRANSLATION_FAULT_LEVEL1"; | ||
| 204 | case 0xC2: return "TRANSLATION_FAULT_LEVEL2"; | ||
| 205 | case 0xC3: return "TRANSLATION_FAULT_LEVEL3"; | ||
| 206 | case 0xC4: return "TRANSLATION_FAULT_LEVEL4"; | ||
| 207 | case 0xC8: return "PERMISSION_FAULT"; | ||
| 208 | case 0xC9 ... 0xCF: return "PERMISSION_FAULT"; | ||
| 209 | case 0xD1: return "TRANSTAB_BUS_FAULT_LEVEL1"; | ||
| 210 | case 0xD2: return "TRANSTAB_BUS_FAULT_LEVEL2"; | ||
| 211 | case 0xD3: return "TRANSTAB_BUS_FAULT_LEVEL3"; | ||
| 212 | case 0xD4: return "TRANSTAB_BUS_FAULT_LEVEL4"; | ||
| 213 | case 0xD8: return "ACCESS_FLAG"; | ||
| 214 | case 0xD9 ... 0xDF: return "ACCESS_FLAG"; | ||
| 215 | case 0xE0 ... 0xE7: return "ADDRESS_SIZE_FAULT"; | ||
| 216 | case 0xE8 ... 0xEF: return "MEMORY_ATTRIBUTES_FAULT"; | ||
| 217 | } | ||
| 218 | |||
| 219 | return "UNKNOWN"; | ||
| 220 | } | ||
| 221 | |||
| 222 | #ifdef CONFIG_PM | ||
| 223 | int panfrost_device_resume(struct device *dev) | ||
| 224 | { | ||
| 225 | struct platform_device *pdev = to_platform_device(dev); | ||
| 226 | struct panfrost_device *pfdev = platform_get_drvdata(pdev); | ||
| 227 | |||
| 228 | panfrost_gpu_soft_reset(pfdev); | ||
| 229 | |||
| 230 | /* TODO: Re-enable all other address spaces */ | ||
| 231 | panfrost_gpu_power_on(pfdev); | ||
| 232 | panfrost_mmu_enable(pfdev, 0); | ||
| 233 | panfrost_job_enable_interrupts(pfdev); | ||
| 234 | panfrost_devfreq_resume(pfdev); | ||
| 235 | |||
| 236 | return 0; | ||
| 237 | } | ||
| 238 | |||
| 239 | int panfrost_device_suspend(struct device *dev) | ||
| 240 | { | ||
| 241 | struct platform_device *pdev = to_platform_device(dev); | ||
| 242 | struct panfrost_device *pfdev = platform_get_drvdata(pdev); | ||
| 243 | |||
| 244 | if (!panfrost_job_is_idle(pfdev)) | ||
| 245 | return -EBUSY; | ||
| 246 | |||
| 247 | panfrost_devfreq_suspend(pfdev); | ||
| 248 | panfrost_gpu_power_off(pfdev); | ||
| 249 | |||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | #endif | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h new file mode 100644 index 000000000000..1ba48d105763 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_device.h | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ | ||
| 3 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 4 | |||
| 5 | #ifndef __PANFROST_DEVICE_H__ | ||
| 6 | #define __PANFROST_DEVICE_H__ | ||
| 7 | |||
| 8 | #include <linux/spinlock.h> | ||
| 9 | #include <drm/drm_device.h> | ||
| 10 | #include <drm/drm_mm.h> | ||
| 11 | #include <drm/gpu_scheduler.h> | ||
| 12 | |||
| 13 | struct panfrost_device; | ||
| 14 | struct panfrost_mmu; | ||
| 15 | struct panfrost_job_slot; | ||
| 16 | struct panfrost_job; | ||
| 17 | |||
| 18 | #define NUM_JOB_SLOTS 3 | ||
| 19 | |||
| 20 | struct panfrost_features { | ||
| 21 | u16 id; | ||
| 22 | u16 revision; | ||
| 23 | |||
| 24 | u64 shader_present; | ||
| 25 | u64 tiler_present; | ||
| 26 | u64 l2_present; | ||
| 27 | u64 stack_present; | ||
| 28 | u32 as_present; | ||
| 29 | u32 js_present; | ||
| 30 | |||
| 31 | u32 l2_features; | ||
| 32 | u32 core_features; | ||
| 33 | u32 tiler_features; | ||
| 34 | u32 mem_features; | ||
| 35 | u32 mmu_features; | ||
| 36 | u32 thread_features; | ||
| 37 | u32 max_threads; | ||
| 38 | u32 thread_max_workgroup_sz; | ||
| 39 | u32 thread_max_barrier_sz; | ||
| 40 | u32 coherency_features; | ||
| 41 | u32 texture_features[4]; | ||
| 42 | u32 js_features[16]; | ||
| 43 | |||
| 44 | u32 nr_core_groups; | ||
| 45 | |||
| 46 | unsigned long hw_features[64 / BITS_PER_LONG]; | ||
| 47 | unsigned long hw_issues[64 / BITS_PER_LONG]; | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct panfrost_devfreq_slot { | ||
| 51 | ktime_t busy_time; | ||
| 52 | ktime_t idle_time; | ||
| 53 | ktime_t time_last_update; | ||
| 54 | bool busy; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct panfrost_device { | ||
| 58 | struct device *dev; | ||
| 59 | struct drm_device *ddev; | ||
| 60 | struct platform_device *pdev; | ||
| 61 | |||
| 62 | spinlock_t hwaccess_lock; | ||
| 63 | |||
| 64 | struct drm_mm mm; | ||
| 65 | spinlock_t mm_lock; | ||
| 66 | |||
| 67 | void __iomem *iomem; | ||
| 68 | struct clk *clock; | ||
| 69 | struct regulator *regulator; | ||
| 70 | struct reset_control *rstc; | ||
| 71 | |||
| 72 | struct panfrost_features features; | ||
| 73 | |||
| 74 | struct panfrost_mmu *mmu; | ||
| 75 | struct panfrost_job_slot *js; | ||
| 76 | |||
| 77 | struct panfrost_job *jobs[NUM_JOB_SLOTS]; | ||
| 78 | struct list_head scheduled_jobs; | ||
| 79 | |||
| 80 | struct mutex sched_lock; | ||
| 81 | |||
| 82 | struct { | ||
| 83 | struct devfreq *devfreq; | ||
| 84 | struct thermal_cooling_device *cooling; | ||
| 85 | unsigned long cur_freq; | ||
| 86 | unsigned long cur_volt; | ||
| 87 | struct panfrost_devfreq_slot slot[NUM_JOB_SLOTS]; | ||
| 88 | } devfreq; | ||
| 89 | }; | ||
| 90 | |||
| 91 | struct panfrost_file_priv { | ||
| 92 | struct panfrost_device *pfdev; | ||
| 93 | |||
| 94 | struct drm_sched_entity sched_entity[NUM_JOB_SLOTS]; | ||
| 95 | }; | ||
| 96 | |||
| 97 | static inline struct panfrost_device *to_panfrost_device(struct drm_device *ddev) | ||
| 98 | { | ||
| 99 | return ddev->dev_private; | ||
| 100 | } | ||
| 101 | |||
| 102 | static inline int panfrost_model_cmp(struct panfrost_device *pfdev, s32 id) | ||
| 103 | { | ||
| 104 | s32 match_id = pfdev->features.id; | ||
| 105 | |||
| 106 | if (match_id & 0xf000) | ||
| 107 | match_id &= 0xf00f; | ||
| 108 | return match_id - id; | ||
| 109 | } | ||
| 110 | |||
| 111 | static inline bool panfrost_model_eq(struct panfrost_device *pfdev, s32 id) | ||
| 112 | { | ||
| 113 | return !panfrost_model_cmp(pfdev, id); | ||
| 114 | } | ||
| 115 | |||
| 116 | int panfrost_device_init(struct panfrost_device *pfdev); | ||
| 117 | void panfrost_device_fini(struct panfrost_device *pfdev); | ||
| 118 | |||
| 119 | int panfrost_device_resume(struct device *dev); | ||
| 120 | int panfrost_device_suspend(struct device *dev); | ||
| 121 | |||
| 122 | const char *panfrost_exception_name(struct panfrost_device *pfdev, u32 exception_code); | ||
| 123 | |||
| 124 | #endif | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c new file mode 100644 index 000000000000..c06af78ab833 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c | |||
| @@ -0,0 +1,463 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ | ||
| 3 | /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */ | ||
| 4 | /* Copyright 2019 Collabora ltd. */ | ||
| 5 | |||
| 6 | #include <linux/bitfield.h> | ||
| 7 | #include <linux/dma-mapping.h> | ||
| 8 | #include <linux/module.h> | ||
| 9 | #include <linux/of_platform.h> | ||
| 10 | #include <linux/pagemap.h> | ||
| 11 | #include <linux/pm_runtime.h> | ||
| 12 | #include <drm/panfrost_drm.h> | ||
| 13 | #include <drm/drm_drv.h> | ||
| 14 | #include <drm/drm_ioctl.h> | ||
| 15 | #include <drm/drm_syncobj.h> | ||
| 16 | #include <drm/drm_utils.h> | ||
| 17 | |||
| 18 | #include "panfrost_device.h" | ||
| 19 | #include "panfrost_devfreq.h" | ||
| 20 | #include "panfrost_gem.h" | ||
| 21 | #include "panfrost_mmu.h" | ||
| 22 | #include "panfrost_job.h" | ||
| 23 | #include "panfrost_gpu.h" | ||
| 24 | |||
| 25 | static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct drm_file *file) | ||
| 26 | { | ||
| 27 | struct drm_panfrost_get_param *param = data; | ||
| 28 | struct panfrost_device *pfdev = ddev->dev_private; | ||
| 29 | |||
| 30 | if (param->pad != 0) | ||
| 31 | return -EINVAL; | ||
| 32 | |||
| 33 | switch (param->param) { | ||
| 34 | case DRM_PANFROST_PARAM_GPU_PROD_ID: | ||
| 35 | param->value = pfdev->features.id; | ||
| 36 | break; | ||
| 37 | default: | ||
| 38 | return -EINVAL; | ||
| 39 | } | ||
| 40 | |||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data, | ||
| 45 | struct drm_file *file) | ||
| 46 | { | ||
| 47 | int ret; | ||
| 48 | struct drm_gem_shmem_object *shmem; | ||
| 49 | struct drm_panfrost_create_bo *args = data; | ||
| 50 | |||
| 51 | if (!args->size || args->flags || args->pad) | ||
| 52 | return -EINVAL; | ||
| 53 | |||
| 54 | shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, | ||
| 55 | &args->handle); | ||
| 56 | if (IS_ERR(shmem)) | ||
| 57 | return PTR_ERR(shmem); | ||
| 58 | |||
| 59 | ret = panfrost_mmu_map(to_panfrost_bo(&shmem->base)); | ||
| 60 | if (ret) | ||
| 61 | goto err_free; | ||
| 62 | |||
| 63 | args->offset = to_panfrost_bo(&shmem->base)->node.start << PAGE_SHIFT; | ||
| 64 | |||
| 65 | return 0; | ||
| 66 | |||
| 67 | err_free: | ||
| 68 | drm_gem_object_put_unlocked(&shmem->base); | ||
| 69 | return ret; | ||
| 70 | } | ||
| 71 | |||
| 72 | /** | ||
| 73 | * panfrost_lookup_bos() - Sets up job->bo[] with the GEM objects | ||
| 74 | * referenced by the job. | ||
| 75 | * @dev: DRM device | ||
| 76 | * @file_priv: DRM file for this fd | ||
| 77 | * @args: IOCTL args | ||
| 78 | * @job: job being set up | ||
| 79 | * | ||
| 80 | * Resolve handles from userspace to BOs and attach them to job. | ||
| 81 | * | ||
| 82 | * Note that this function doesn't need to unreference the BOs on | ||
| 83 | * failure, because that will happen at panfrost_job_cleanup() time. | ||
| 84 | */ | ||
| 85 | static int | ||
| 86 | panfrost_lookup_bos(struct drm_device *dev, | ||
| 87 | struct drm_file *file_priv, | ||
| 88 | struct drm_panfrost_submit *args, | ||
| 89 | struct panfrost_job *job) | ||
| 90 | { | ||
| 91 | job->bo_count = args->bo_handle_count; | ||
| 92 | |||
| 93 | if (!job->bo_count) | ||
| 94 | return 0; | ||
| 95 | |||
| 96 | job->implicit_fences = kvmalloc_array(job->bo_count, | ||
| 97 | sizeof(struct dma_fence *), | ||
| 98 | GFP_KERNEL | __GFP_ZERO); | ||
| 99 | if (!job->implicit_fences) | ||
| 100 | return -ENOMEM; | ||
| 101 | |||
| 102 | return drm_gem_objects_lookup(file_priv, | ||
| 103 | (void __user *)(uintptr_t)args->bo_handles, | ||
| 104 | job->bo_count, &job->bos); | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * panfrost_copy_in_sync() - Sets up job->in_fences[] with the sync objects | ||
| 109 | * referenced by the job. | ||
| 110 | * @dev: DRM device | ||
| 111 | * @file_priv: DRM file for this fd | ||
| 112 | * @args: IOCTL args | ||
| 113 | * @job: job being set up | ||
| 114 | * | ||
| 115 | * Resolve syncobjs from userspace to fences and attach them to job. | ||
| 116 | * | ||
| 117 | * Note that this function doesn't need to unreference the fences on | ||
| 118 | * failure, because that will happen at panfrost_job_cleanup() time. | ||
| 119 | */ | ||
| 120 | static int | ||
| 121 | panfrost_copy_in_sync(struct drm_device *dev, | ||
| 122 | struct drm_file *file_priv, | ||
| 123 | struct drm_panfrost_submit *args, | ||
| 124 | struct panfrost_job *job) | ||
| 125 | { | ||
| 126 | u32 *handles; | ||
| 127 | int ret = 0; | ||
| 128 | int i; | ||
| 129 | |||
| 130 | job->in_fence_count = args->in_sync_count; | ||
| 131 | |||
| 132 | if (!job->in_fence_count) | ||
| 133 | return 0; | ||
| 134 | |||
| 135 | job->in_fences = kvmalloc_array(job->in_fence_count, | ||
| 136 | sizeof(struct dma_fence *), | ||
| 137 | GFP_KERNEL | __GFP_ZERO); | ||
| 138 | if (!job->in_fences) { | ||
| 139 | DRM_DEBUG("Failed to allocate job in fences\n"); | ||
| 140 | return -ENOMEM; | ||
| 141 | } | ||
| 142 | |||
| 143 | handles = kvmalloc_array(job->in_fence_count, sizeof(u32), GFP_KERNEL); | ||
| 144 | if (!handles) { | ||
| 145 | ret = -ENOMEM; | ||
| 146 | DRM_DEBUG("Failed to allocate incoming syncobj handles\n"); | ||
| 147 | goto fail; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (copy_from_user(handles, | ||
| 151 | (void __user *)(uintptr_t)args->in_syncs, | ||
| 152 | job->in_fence_count * sizeof(u32))) { | ||
| 153 | ret = -EFAULT; | ||
| 154 | DRM_DEBUG("Failed to copy in syncobj handles\n"); | ||
| 155 | goto fail; | ||
| 156 | } | ||
| 157 | |||
| 158 | for (i = 0; i < job->in_fence_count; i++) { | ||
| 159 | ret = drm_syncobj_find_fence(file_priv, handles[i], 0, 0, | ||
| 160 | &job->in_fences[i]); | ||
| 161 | if (ret == -EINVAL) | ||
| 162 | goto fail; | ||
| 163 | } | ||
| 164 | |||
| 165 | fail: | ||
| 166 | kvfree(handles); | ||
| 167 | return ret; | ||
| 168 | } | ||
| 169 | |||
| 170 | static int panfrost_ioctl_submit(struct drm_device *dev, void *data, | ||
| 171 | struct drm_file *file) | ||
| 172 | { | ||
| 173 | struct panfrost_device *pfdev = dev->dev_private; | ||
| 174 | struct drm_panfrost_submit *args = data; | ||
| 175 | struct drm_syncobj *sync_out; | ||
| 176 | struct panfrost_job *job; | ||
| 177 | int ret = 0; | ||
| 178 | |||
| 179 | job = kzalloc(sizeof(*job), GFP_KERNEL); | ||
| 180 | if (!job) | ||
| 181 | return -ENOMEM; | ||
| 182 | |||
| 183 | kref_init(&job->refcount); | ||
| 184 | |||
| 185 | job->pfdev = pfdev; | ||
| 186 | job->jc = args->jc; | ||
| 187 | job->requirements = args->requirements; | ||
| 188 | job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev); | ||
| 189 | job->file_priv = file->driver_priv; | ||
| 190 | |||
| 191 | ret = panfrost_copy_in_sync(dev, file, args, job); | ||
| 192 | if (ret) | ||
| 193 | goto fail; | ||
| 194 | |||
| 195 | ret = panfrost_lookup_bos(dev, file, args, job); | ||
| 196 | if (ret) | ||
| 197 | goto fail; | ||
| 198 | |||
| 199 | ret = panfrost_job_push(job); | ||
| 200 | if (ret) | ||
| 201 | goto fail; | ||
| 202 | |||
| 203 | /* Update the return sync object for the job */ | ||
| 204 | sync_out = drm_syncobj_find(file, args->out_sync); | ||
| 205 | if (sync_out) { | ||
| 206 | drm_syncobj_replace_fence(sync_out, job->render_done_fence); | ||
| 207 | drm_syncobj_put(sync_out); | ||
| 208 | } | ||
| 209 | |||
| 210 | fail: | ||
| 211 | panfrost_job_put(job); | ||
| 212 | |||
| 213 | return ret; | ||
| 214 | } | ||
| 215 | |||
| 216 | static int | ||
| 217 | panfrost_ioctl_wait_bo(struct drm_device *dev, void *data, | ||
| 218 | struct drm_file *file_priv) | ||
| 219 | { | ||
| 220 | long ret; | ||
| 221 | struct drm_panfrost_wait_bo *args = data; | ||
| 222 | struct drm_gem_object *gem_obj; | ||
| 223 | unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns); | ||
| 224 | |||
| 225 | if (args->pad) | ||
| 226 | return -EINVAL; | ||
| 227 | |||
| 228 | gem_obj = drm_gem_object_lookup(file_priv, args->handle); | ||
| 229 | if (!gem_obj) | ||
| 230 | return -ENOENT; | ||
| 231 | |||
| 232 | ret = reservation_object_wait_timeout_rcu(gem_obj->resv, true, | ||
| 233 | true, timeout); | ||
| 234 | if (!ret) | ||
| 235 | ret = timeout ? -ETIMEDOUT : -EBUSY; | ||
| 236 | |||
| 237 | drm_gem_object_put_unlocked(gem_obj); | ||
| 238 | |||
| 239 | return ret; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int panfrost_ioctl_mmap_bo(struct drm_device *dev, void *data, | ||
| 243 | struct drm_file *file_priv) | ||
| 244 | { | ||
| 245 | struct drm_panfrost_mmap_bo *args = data; | ||
| 246 | struct drm_gem_object *gem_obj; | ||
| 247 | int ret; | ||
| 248 | |||
| 249 | if (args->flags != 0) { | ||
| 250 | DRM_INFO("unknown mmap_bo flags: %d\n", args->flags); | ||
| 251 | return -EINVAL; | ||
| 252 | } | ||
| 253 | |||
| 254 | gem_obj = drm_gem_object_lookup(file_priv, args->handle); | ||
| 255 | if (!gem_obj) { | ||
| 256 | DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); | ||
| 257 | return -ENOENT; | ||
| 258 | } | ||
| 259 | |||
| 260 | ret = drm_gem_create_mmap_offset(gem_obj); | ||
| 261 | if (ret == 0) | ||
| 262 | args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); | ||
| 263 | drm_gem_object_put_unlocked(gem_obj); | ||
| 264 | |||
| 265 | return ret; | ||
| 266 | } | ||
| 267 | |||
| 268 | static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data, | ||
| 269 | struct drm_file *file_priv) | ||
| 270 | { | ||
| 271 | struct drm_panfrost_get_bo_offset *args = data; | ||
| 272 | struct drm_gem_object *gem_obj; | ||
| 273 | struct panfrost_gem_object *bo; | ||
| 274 | |||
| 275 | gem_obj = drm_gem_object_lookup(file_priv, args->handle); | ||
| 276 | if (!gem_obj) { | ||
| 277 | DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); | ||
| 278 | return -ENOENT; | ||
| 279 | } | ||
| 280 | bo = to_panfrost_bo(gem_obj); | ||
| 281 | |||
| 282 | args->offset = bo->node.start << PAGE_SHIFT; | ||
| 283 | |||
| 284 | drm_gem_object_put_unlocked(gem_obj); | ||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static int | ||
| 289 | panfrost_open(struct drm_device *dev, struct drm_file *file) | ||
| 290 | { | ||
| 291 | struct panfrost_device *pfdev = dev->dev_private; | ||
| 292 | struct panfrost_file_priv *panfrost_priv; | ||
| 293 | |||
| 294 | panfrost_priv = kzalloc(sizeof(*panfrost_priv), GFP_KERNEL); | ||
| 295 | if (!panfrost_priv) | ||
| 296 | return -ENOMEM; | ||
| 297 | |||
| 298 | panfrost_priv->pfdev = pfdev; | ||
| 299 | file->driver_priv = panfrost_priv; | ||
| 300 | |||
| 301 | return panfrost_job_open(panfrost_priv); | ||
| 302 | } | ||
| 303 | |||
| 304 | static void | ||
| 305 | panfrost_postclose(struct drm_device *dev, struct drm_file *file) | ||
| 306 | { | ||
| 307 | struct panfrost_file_priv *panfrost_priv = file->driver_priv; | ||
| 308 | |||
| 309 | panfrost_job_close(panfrost_priv); | ||
| 310 | |||
| 311 | kfree(panfrost_priv); | ||
| 312 | } | ||
| 313 | |||
| 314 | /* DRM_AUTH is required on SUBMIT for now, while all clients share a single | ||
| 315 | * address space. Note that render nodes would be able to submit jobs that | ||
| 316 | * could access BOs from clients authenticated with the master node. | ||
| 317 | */ | ||
| 318 | static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = { | ||
| 319 | #define PANFROST_IOCTL(n, func, flags) \ | ||
| 320 | DRM_IOCTL_DEF_DRV(PANFROST_##n, panfrost_ioctl_##func, flags) | ||
| 321 | |||
| 322 | PANFROST_IOCTL(SUBMIT, submit, DRM_RENDER_ALLOW | DRM_AUTH), | ||
| 323 | PANFROST_IOCTL(WAIT_BO, wait_bo, DRM_RENDER_ALLOW), | ||
| 324 | PANFROST_IOCTL(CREATE_BO, create_bo, DRM_RENDER_ALLOW), | ||
| 325 | PANFROST_IOCTL(MMAP_BO, mmap_bo, DRM_RENDER_ALLOW), | ||
| 326 | PANFROST_IOCTL(GET_PARAM, get_param, DRM_RENDER_ALLOW), | ||
| 327 | PANFROST_IOCTL(GET_BO_OFFSET, get_bo_offset, DRM_RENDER_ALLOW), | ||
| 328 | }; | ||
| 329 | |||
| 330 | DEFINE_DRM_GEM_SHMEM_FOPS(panfrost_drm_driver_fops); | ||
| 331 | |||
| 332 | static struct drm_driver panfrost_drm_driver = { | ||
| 333 | .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_PRIME | | ||
| 334 | DRIVER_SYNCOBJ, | ||
| 335 | .open = panfrost_open, | ||
| 336 | .postclose = panfrost_postclose, | ||
| 337 | .ioctls = panfrost_drm_driver_ioctls, | ||
| 338 | .num_ioctls = ARRAY_SIZE(panfrost_drm_driver_ioctls), | ||
| 339 | .fops = &panfrost_drm_driver_fops, | ||
| 340 | .name = "panfrost", | ||
| 341 | .desc = "panfrost DRM", | ||
| 342 | .date = "20180908", | ||
| 343 | .major = 1, | ||
| 344 | .minor = 0, | ||
| 345 | |||
| 346 | .gem_create_object = panfrost_gem_create_object, | ||
| 347 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
| 348 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
| 349 | .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, | ||
| 350 | .gem_prime_mmap = drm_gem_prime_mmap, | ||
| 351 | }; | ||
| 352 | |||
| 353 | static int panfrost_probe(struct platform_device *pdev) | ||
| 354 | { | ||
| 355 | struct panfrost_device *pfdev; | ||
| 356 | struct drm_device *ddev; | ||
| 357 | int err; | ||
| 358 | |||
| 359 | pfdev = devm_kzalloc(&pdev->dev, sizeof(*pfdev), GFP_KERNEL); | ||
| 360 | if (!pfdev) | ||
| 361 | return -ENOMEM; | ||
| 362 | |||
| 363 | pfdev->pdev = pdev; | ||
| 364 | pfdev->dev = &pdev->dev; | ||
| 365 | |||
| 366 | platform_set_drvdata(pdev, pfdev); | ||
| 367 | |||
| 368 | /* Allocate and initialze the DRM device. */ | ||
| 369 | ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev); | ||
| 370 | if (IS_ERR(ddev)) | ||
| 371 | return PTR_ERR(ddev); | ||
| 372 | |||
| 373 | ddev->dev_private = pfdev; | ||
| 374 | pfdev->ddev = ddev; | ||
| 375 | |||
| 376 | spin_lock_init(&pfdev->mm_lock); | ||
| 377 | |||
| 378 | /* 4G enough for now. can be 48-bit */ | ||
| 379 | drm_mm_init(&pfdev->mm, SZ_32M >> PAGE_SHIFT, (SZ_4G - SZ_32M) >> PAGE_SHIFT); | ||
| 380 | |||
| 381 | pm_runtime_use_autosuspend(pfdev->dev); | ||
| 382 | pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */ | ||
| 383 | pm_runtime_enable(pfdev->dev); | ||
| 384 | |||
| 385 | err = panfrost_device_init(pfdev); | ||
| 386 | if (err) { | ||
| 387 | dev_err(&pdev->dev, "Fatal error during GPU init\n"); | ||
| 388 | goto err_out0; | ||
| 389 | } | ||
| 390 | |||
| 391 | dma_set_mask_and_coherent(pfdev->dev, | ||
| 392 | DMA_BIT_MASK(FIELD_GET(0xff00, pfdev->features.mmu_features))); | ||
| 393 | |||
| 394 | err = panfrost_devfreq_init(pfdev); | ||
| 395 | if (err) { | ||
| 396 | dev_err(&pdev->dev, "Fatal error during devfreq init\n"); | ||
| 397 | goto err_out1; | ||
| 398 | } | ||
| 399 | |||
| 400 | /* | ||
| 401 | * Register the DRM device with the core and the connectors with | ||
| 402 | * sysfs | ||
| 403 | */ | ||
| 404 | err = drm_dev_register(ddev, 0); | ||
| 405 | if (err < 0) | ||
| 406 | goto err_out1; | ||
| 407 | |||
| 408 | return 0; | ||
| 409 | |||
| 410 | err_out1: | ||
| 411 | panfrost_device_fini(pfdev); | ||
| 412 | err_out0: | ||
| 413 | drm_dev_put(ddev); | ||
| 414 | return err; | ||
| 415 | } | ||
| 416 | |||
| 417 | static int panfrost_remove(struct platform_device *pdev) | ||
| 418 | { | ||
| 419 | struct panfrost_device *pfdev = platform_get_drvdata(pdev); | ||
| 420 | struct drm_device *ddev = pfdev->ddev; | ||
| 421 | |||
| 422 | drm_dev_unregister(ddev); | ||
| 423 | pm_runtime_get_sync(pfdev->dev); | ||
| 424 | pm_runtime_put_sync_autosuspend(pfdev->dev); | ||
| 425 | pm_runtime_disable(pfdev->dev); | ||
| 426 | panfrost_device_fini(pfdev); | ||
| 427 | drm_dev_put(ddev); | ||
| 428 | return 0; | ||
| 429 | } | ||
| 430 | |||
| 431 | static const struct of_device_id dt_match[] = { | ||
| 432 | { .compatible = "arm,mali-t604" }, | ||
| 433 | { .compatible = "arm,mali-t624" }, | ||
| 434 | { .compatible = "arm,mali-t628" }, | ||
| 435 | { .compatible = "arm,mali-t720" }, | ||
| 436 | { .compatible = "arm,mali-t760" }, | ||
| 437 | { .compatible = "arm,mali-t820" }, | ||
| 438 | { .compatible = "arm,mali-t830" }, | ||
| 439 | { .compatible = "arm,mali-t860" }, | ||
| 440 | { .compatible = "arm,mali-t880" }, | ||
| 441 | {} | ||
| 442 | }; | ||
| 443 | MODULE_DEVICE_TABLE(of, dt_match); | ||
| 444 | |||
| 445 | static const struct dev_pm_ops panfrost_pm_ops = { | ||
| 446 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) | ||
| 447 | SET_RUNTIME_PM_OPS(panfrost_device_suspend, panfrost_device_resume, NULL) | ||
| 448 | }; | ||
| 449 | |||
| 450 | static struct platform_driver panfrost_driver = { | ||
| 451 | .probe = panfrost_probe, | ||
| 452 | .remove = panfrost_remove, | ||
| 453 | .driver = { | ||
| 454 | .name = "panfrost", | ||
| 455 | .pm = &panfrost_pm_ops, | ||
| 456 | .of_match_table = dt_match, | ||
| 457 | }, | ||
| 458 | }; | ||
| 459 | module_platform_driver(panfrost_driver); | ||
| 460 | |||
| 461 | MODULE_AUTHOR("Panfrost Project Developers"); | ||
| 462 | MODULE_DESCRIPTION("Panfrost DRM Driver"); | ||
| 463 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_features.h b/drivers/gpu/drm/panfrost/panfrost_features.h new file mode 100644 index 000000000000..5056777c7744 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_features.h | |||
| @@ -0,0 +1,309 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* (C) COPYRIGHT 2014-2018 ARM Limited. All rights reserved. */ | ||
| 3 | /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */ | ||
| 4 | #ifndef __PANFROST_FEATURES_H__ | ||
| 5 | #define __PANFROST_FEATURES_H__ | ||
| 6 | |||
| 7 | #include <linux/bitops.h> | ||
| 8 | |||
| 9 | #include "panfrost_device.h" | ||
| 10 | |||
| 11 | enum panfrost_hw_feature { | ||
| 12 | HW_FEATURE_JOBCHAIN_DISAMBIGUATION, | ||
| 13 | HW_FEATURE_PWRON_DURING_PWROFF_TRANS, | ||
| 14 | HW_FEATURE_XAFFINITY, | ||
| 15 | HW_FEATURE_OUT_OF_ORDER_EXEC, | ||
| 16 | HW_FEATURE_MRT, | ||
| 17 | HW_FEATURE_BRNDOUT_CC, | ||
| 18 | HW_FEATURE_INTERPIPE_REG_ALIASING, | ||
| 19 | HW_FEATURE_LD_ST_TILEBUFFER, | ||
| 20 | HW_FEATURE_MSAA_16X, | ||
| 21 | HW_FEATURE_32_BIT_UNIFORM_ADDRESS, | ||
| 22 | HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL, | ||
| 23 | HW_FEATURE_OPTIMIZED_COVERAGE_MASK, | ||
| 24 | HW_FEATURE_T7XX_PAIRING_RULES, | ||
| 25 | HW_FEATURE_LD_ST_LEA_TEX, | ||
| 26 | HW_FEATURE_LINEAR_FILTER_FLOAT, | ||
| 27 | HW_FEATURE_WORKGROUP_ROUND_MULTIPLE_OF_4, | ||
| 28 | HW_FEATURE_IMAGES_IN_FRAGMENT_SHADERS, | ||
| 29 | HW_FEATURE_TEST4_DATUM_MODE, | ||
| 30 | HW_FEATURE_NEXT_INSTRUCTION_TYPE, | ||
| 31 | HW_FEATURE_BRNDOUT_KILL, | ||
| 32 | HW_FEATURE_WARPING, | ||
| 33 | HW_FEATURE_V4, | ||
| 34 | HW_FEATURE_FLUSH_REDUCTION, | ||
| 35 | HW_FEATURE_PROTECTED_MODE, | ||
| 36 | HW_FEATURE_COHERENCY_REG, | ||
| 37 | HW_FEATURE_PROTECTED_DEBUG_MODE, | ||
| 38 | HW_FEATURE_AARCH64_MMU, | ||
| 39 | HW_FEATURE_TLS_HASHING, | ||
| 40 | HW_FEATURE_THREAD_GROUP_SPLIT, | ||
| 41 | HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG, | ||
| 42 | }; | ||
| 43 | |||
| 44 | #define hw_features_t600 (\ | ||
| 45 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 46 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 47 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 48 | BIT_ULL(HW_FEATURE_V4)) | ||
| 49 | |||
| 50 | #define hw_features_t620 (\ | ||
| 51 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 52 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 53 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 54 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 55 | BIT_ULL(HW_FEATURE_V4)) | ||
| 56 | |||
| 57 | #define hw_features_t720 (\ | ||
| 58 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 59 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 60 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 61 | BIT_ULL(HW_FEATURE_OPTIMIZED_COVERAGE_MASK) | \ | ||
| 62 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 63 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 64 | BIT_ULL(HW_FEATURE_WORKGROUP_ROUND_MULTIPLE_OF_4) | \ | ||
| 65 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 66 | BIT_ULL(HW_FEATURE_V4)) | ||
| 67 | |||
| 68 | |||
| 69 | #define hw_features_t760 (\ | ||
| 70 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 71 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 72 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 73 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 74 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 75 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 76 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 77 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 78 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 79 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 80 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 81 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 82 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 83 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 84 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT)) | ||
| 85 | |||
| 86 | // T860 | ||
| 87 | #define hw_features_t860 (\ | ||
| 88 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 89 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 90 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 91 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 92 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 93 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 94 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 95 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 96 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 97 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 98 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 99 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 100 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 101 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 102 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 103 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 104 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT)) | ||
| 105 | |||
| 106 | #define hw_features_t880 hw_features_t860 | ||
| 107 | |||
| 108 | #define hw_features_t830 (\ | ||
| 109 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 110 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 111 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 112 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 113 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 114 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 115 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 116 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 117 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 118 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 119 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 120 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 121 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 122 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 123 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 124 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 125 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 126 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT)) | ||
| 127 | |||
| 128 | #define hw_features_t820 (\ | ||
| 129 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 130 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 131 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 132 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 133 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 134 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 135 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 136 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 137 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 138 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 139 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 140 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 141 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 142 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 143 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 144 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 145 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 146 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT)) | ||
| 147 | |||
| 148 | #define hw_features_g71 (\ | ||
| 149 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 150 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 151 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 152 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 153 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 154 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 155 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 156 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 157 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 158 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 159 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 160 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 161 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 162 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 163 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 164 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 165 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 166 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 167 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 168 | BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \ | ||
| 169 | BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \ | ||
| 170 | BIT_ULL(HW_FEATURE_COHERENCY_REG)) | ||
| 171 | |||
| 172 | #define hw_features_g72 (\ | ||
| 173 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 174 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 175 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 176 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 177 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 178 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 179 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 180 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 181 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 182 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 183 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 184 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 185 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 186 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 187 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 188 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 189 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 190 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 191 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 192 | BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \ | ||
| 193 | BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \ | ||
| 194 | BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \ | ||
| 195 | BIT_ULL(HW_FEATURE_COHERENCY_REG)) | ||
| 196 | |||
| 197 | #define hw_features_g51 (\ | ||
| 198 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 199 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 200 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 201 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 202 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 203 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 204 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 205 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 206 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 207 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 208 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 209 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 210 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 211 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 212 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 213 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 214 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 215 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 216 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 217 | BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \ | ||
| 218 | BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \ | ||
| 219 | BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \ | ||
| 220 | BIT_ULL(HW_FEATURE_COHERENCY_REG)) | ||
| 221 | |||
| 222 | #define hw_features_g52 (\ | ||
| 223 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 224 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 225 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 226 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 227 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 228 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 229 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 230 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 231 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 232 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 233 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 234 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 235 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 236 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 237 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 238 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 239 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 240 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 241 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 242 | BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \ | ||
| 243 | BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \ | ||
| 244 | BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \ | ||
| 245 | BIT_ULL(HW_FEATURE_COHERENCY_REG)) | ||
| 246 | |||
| 247 | #define hw_features_g76 (\ | ||
| 248 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 249 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 250 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 251 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 252 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 253 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 254 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 255 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 256 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 257 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 258 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 259 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 260 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 261 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 262 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 263 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 264 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 265 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 266 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 267 | BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \ | ||
| 268 | BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \ | ||
| 269 | BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \ | ||
| 270 | BIT_ULL(HW_FEATURE_COHERENCY_REG) | \ | ||
| 271 | BIT_ULL(HW_FEATURE_AARCH64_MMU) | \ | ||
| 272 | BIT_ULL(HW_FEATURE_TLS_HASHING) | \ | ||
| 273 | BIT_ULL(HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG)) | ||
| 274 | |||
| 275 | #define hw_features_g31 (\ | ||
| 276 | BIT_ULL(HW_FEATURE_JOBCHAIN_DISAMBIGUATION) | \ | ||
| 277 | BIT_ULL(HW_FEATURE_PWRON_DURING_PWROFF_TRANS) | \ | ||
| 278 | BIT_ULL(HW_FEATURE_XAFFINITY) | \ | ||
| 279 | BIT_ULL(HW_FEATURE_WARPING) | \ | ||
| 280 | BIT_ULL(HW_FEATURE_INTERPIPE_REG_ALIASING) | \ | ||
| 281 | BIT_ULL(HW_FEATURE_32_BIT_UNIFORM_ADDRESS) | \ | ||
| 282 | BIT_ULL(HW_FEATURE_ATTR_AUTO_TYPE_INFERRAL) | \ | ||
| 283 | BIT_ULL(HW_FEATURE_BRNDOUT_CC) | \ | ||
| 284 | BIT_ULL(HW_FEATURE_BRNDOUT_KILL) | \ | ||
| 285 | BIT_ULL(HW_FEATURE_LD_ST_LEA_TEX) | \ | ||
| 286 | BIT_ULL(HW_FEATURE_LD_ST_TILEBUFFER) | \ | ||
| 287 | BIT_ULL(HW_FEATURE_LINEAR_FILTER_FLOAT) | \ | ||
| 288 | BIT_ULL(HW_FEATURE_MRT) | \ | ||
| 289 | BIT_ULL(HW_FEATURE_MSAA_16X) | \ | ||
| 290 | BIT_ULL(HW_FEATURE_NEXT_INSTRUCTION_TYPE) | \ | ||
| 291 | BIT_ULL(HW_FEATURE_OUT_OF_ORDER_EXEC) | \ | ||
| 292 | BIT_ULL(HW_FEATURE_T7XX_PAIRING_RULES) | \ | ||
| 293 | BIT_ULL(HW_FEATURE_TEST4_DATUM_MODE) | \ | ||
| 294 | BIT_ULL(HW_FEATURE_THREAD_GROUP_SPLIT) | \ | ||
| 295 | BIT_ULL(HW_FEATURE_FLUSH_REDUCTION) | \ | ||
| 296 | BIT_ULL(HW_FEATURE_PROTECTED_MODE) | \ | ||
| 297 | BIT_ULL(HW_FEATURE_PROTECTED_DEBUG_MODE) | \ | ||
| 298 | BIT_ULL(HW_FEATURE_COHERENCY_REG) | \ | ||
| 299 | BIT_ULL(HW_FEATURE_AARCH64_MMU) | \ | ||
| 300 | BIT_ULL(HW_FEATURE_TLS_HASHING) | \ | ||
| 301 | BIT_ULL(HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG)) | ||
| 302 | |||
| 303 | static inline bool panfrost_has_hw_feature(struct panfrost_device *pfdev, | ||
| 304 | enum panfrost_hw_feature feat) | ||
| 305 | { | ||
| 306 | return test_bit(feat, pfdev->features.hw_features); | ||
| 307 | } | ||
| 308 | |||
| 309 | #endif | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c new file mode 100644 index 000000000000..8a0376283a21 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 3 | |||
| 4 | #include <linux/err.h> | ||
| 5 | #include <linux/slab.h> | ||
| 6 | #include <linux/dma-buf.h> | ||
| 7 | #include <linux/dma-mapping.h> | ||
| 8 | |||
| 9 | #include <drm/panfrost_drm.h> | ||
| 10 | #include "panfrost_device.h" | ||
| 11 | #include "panfrost_gem.h" | ||
| 12 | #include "panfrost_mmu.h" | ||
| 13 | |||
| 14 | /* Called DRM core on the last userspace/kernel unreference of the | ||
| 15 | * BO. | ||
| 16 | */ | ||
| 17 | void panfrost_gem_free_object(struct drm_gem_object *obj) | ||
| 18 | { | ||
| 19 | struct panfrost_gem_object *bo = to_panfrost_bo(obj); | ||
| 20 | struct panfrost_device *pfdev = obj->dev->dev_private; | ||
| 21 | |||
| 22 | panfrost_mmu_unmap(bo); | ||
| 23 | |||
| 24 | spin_lock(&pfdev->mm_lock); | ||
| 25 | drm_mm_remove_node(&bo->node); | ||
| 26 | spin_unlock(&pfdev->mm_lock); | ||
| 27 | |||
| 28 | drm_gem_shmem_free_object(obj); | ||
| 29 | } | ||
| 30 | |||
| 31 | static const struct drm_gem_object_funcs panfrost_gem_funcs = { | ||
| 32 | .free = panfrost_gem_free_object, | ||
| 33 | .print_info = drm_gem_shmem_print_info, | ||
| 34 | .pin = drm_gem_shmem_pin, | ||
| 35 | .unpin = drm_gem_shmem_unpin, | ||
| 36 | .get_sg_table = drm_gem_shmem_get_sg_table, | ||
| 37 | .vmap = drm_gem_shmem_vmap, | ||
| 38 | .vunmap = drm_gem_shmem_vunmap, | ||
| 39 | .vm_ops = &drm_gem_shmem_vm_ops, | ||
| 40 | }; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * panfrost_gem_create_object - Implementation of driver->gem_create_object. | ||
| 44 | * @dev: DRM device | ||
| 45 | * @size: Size in bytes of the memory the object will reference | ||
| 46 | * | ||
| 47 | * This lets the GEM helpers allocate object structs for us, and keep | ||
| 48 | * our BO stats correct. | ||
| 49 | */ | ||
| 50 | struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size) | ||
| 51 | { | ||
| 52 | int ret; | ||
| 53 | struct panfrost_device *pfdev = dev->dev_private; | ||
| 54 | struct panfrost_gem_object *obj; | ||
| 55 | |||
| 56 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); | ||
| 57 | if (!obj) | ||
| 58 | return NULL; | ||
| 59 | |||
| 60 | obj->base.base.funcs = &panfrost_gem_funcs; | ||
| 61 | |||
| 62 | spin_lock(&pfdev->mm_lock); | ||
| 63 | ret = drm_mm_insert_node(&pfdev->mm, &obj->node, | ||
| 64 | roundup(size, PAGE_SIZE) >> PAGE_SHIFT); | ||
| 65 | spin_unlock(&pfdev->mm_lock); | ||
| 66 | if (ret) | ||
| 67 | goto free_obj; | ||
| 68 | |||
| 69 | return &obj->base.base; | ||
| 70 | |||
| 71 | free_obj: | ||
| 72 | kfree(obj); | ||
| 73 | return ERR_PTR(ret); | ||
| 74 | } | ||
| 75 | |||
| 76 | struct drm_gem_object * | ||
| 77 | panfrost_gem_prime_import_sg_table(struct drm_device *dev, | ||
| 78 | struct dma_buf_attachment *attach, | ||
| 79 | struct sg_table *sgt) | ||
| 80 | { | ||
| 81 | struct drm_gem_object *obj; | ||
| 82 | struct panfrost_gem_object *pobj; | ||
| 83 | |||
| 84 | obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt); | ||
| 85 | if (IS_ERR(obj)) | ||
| 86 | return ERR_CAST(obj); | ||
| 87 | |||
| 88 | pobj = to_panfrost_bo(obj); | ||
| 89 | |||
| 90 | obj->resv = attach->dmabuf->resv; | ||
| 91 | |||
| 92 | panfrost_mmu_map(pobj); | ||
| 93 | |||
| 94 | return obj; | ||
| 95 | } | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h new file mode 100644 index 000000000000..045000eb5fcf --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_gem.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 3 | |||
| 4 | #ifndef __PANFROST_GEM_H__ | ||
| 5 | #define __PANFROST_GEM_H__ | ||
| 6 | |||
| 7 | #include <drm/drm_gem_shmem_helper.h> | ||
| 8 | #include <drm/drm_mm.h> | ||
| 9 | |||
| 10 | struct panfrost_gem_object { | ||
| 11 | struct drm_gem_shmem_object base; | ||
| 12 | |||
| 13 | struct drm_mm_node node; | ||
| 14 | }; | ||
| 15 | |||
| 16 | static inline | ||
| 17 | struct panfrost_gem_object *to_panfrost_bo(struct drm_gem_object *obj) | ||
| 18 | { | ||
| 19 | return container_of(to_drm_gem_shmem_obj(obj), struct panfrost_gem_object, base); | ||
| 20 | } | ||
| 21 | |||
| 22 | struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size); | ||
| 23 | |||
| 24 | struct drm_gem_object * | ||
| 25 | panfrost_gem_prime_import_sg_table(struct drm_device *dev, | ||
| 26 | struct dma_buf_attachment *attach, | ||
| 27 | struct sg_table *sgt); | ||
| 28 | |||
| 29 | #endif /* __PANFROST_GEM_H__ */ | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c new file mode 100644 index 000000000000..aceaf6e44a09 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c | |||
| @@ -0,0 +1,362 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ | ||
| 3 | /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */ | ||
| 4 | /* Copyright 2019 Collabora ltd. */ | ||
| 5 | #include <linux/bitmap.h> | ||
| 6 | #include <linux/delay.h> | ||
| 7 | #include <linux/interrupt.h> | ||
| 8 | #include <linux/io.h> | ||
| 9 | #include <linux/iopoll.h> | ||
| 10 | #include <linux/platform_device.h> | ||
| 11 | |||
| 12 | #include "panfrost_device.h" | ||
| 13 | #include "panfrost_features.h" | ||
| 14 | #include "panfrost_issues.h" | ||
| 15 | #include "panfrost_gpu.h" | ||
| 16 | #include "panfrost_regs.h" | ||
| 17 | |||
| 18 | #define gpu_write(dev, reg, data) writel(data, dev->iomem + reg) | ||
| 19 | #define gpu_read(dev, reg) readl(dev->iomem + reg) | ||
| 20 | |||
| 21 | static irqreturn_t panfrost_gpu_irq_handler(int irq, void *data) | ||
| 22 | { | ||
| 23 | struct panfrost_device *pfdev = data; | ||
| 24 | u32 state = gpu_read(pfdev, GPU_INT_STAT); | ||
| 25 | u32 fault_status = gpu_read(pfdev, GPU_FAULT_STATUS); | ||
| 26 | |||
| 27 | if (!state) | ||
| 28 | return IRQ_NONE; | ||
| 29 | |||
| 30 | if (state & GPU_IRQ_MASK_ERROR) { | ||
| 31 | u64 address = (u64) gpu_read(pfdev, GPU_FAULT_ADDRESS_HI) << 32; | ||
| 32 | address |= gpu_read(pfdev, GPU_FAULT_ADDRESS_LO); | ||
| 33 | |||
| 34 | dev_warn(pfdev->dev, "GPU Fault 0x%08x (%s) at 0x%016llx\n", | ||
| 35 | fault_status & 0xFF, panfrost_exception_name(pfdev, fault_status), | ||
| 36 | address); | ||
| 37 | |||
| 38 | if (state & GPU_IRQ_MULTIPLE_FAULT) | ||
| 39 | dev_warn(pfdev->dev, "There were multiple GPU faults - some have not been reported\n"); | ||
| 40 | |||
| 41 | gpu_write(pfdev, GPU_INT_MASK, 0); | ||
| 42 | } | ||
| 43 | |||
| 44 | gpu_write(pfdev, GPU_INT_CLEAR, state); | ||
| 45 | |||
| 46 | return IRQ_HANDLED; | ||
| 47 | } | ||
| 48 | |||
| 49 | int panfrost_gpu_soft_reset(struct panfrost_device *pfdev) | ||
| 50 | { | ||
| 51 | int ret; | ||
| 52 | u32 val; | ||
| 53 | |||
| 54 | gpu_write(pfdev, GPU_INT_MASK, 0); | ||
| 55 | gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_RESET_COMPLETED); | ||
| 56 | gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET); | ||
| 57 | |||
| 58 | ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, | ||
| 59 | val, val & GPU_IRQ_RESET_COMPLETED, 100, 10000); | ||
| 60 | |||
| 61 | if (ret) { | ||
| 62 | dev_err(pfdev->dev, "gpu soft reset timed out\n"); | ||
| 63 | return ret; | ||
| 64 | } | ||
| 65 | |||
| 66 | gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_MASK_ALL); | ||
| 67 | gpu_write(pfdev, GPU_INT_MASK, GPU_IRQ_MASK_ALL); | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev) | ||
| 73 | { | ||
| 74 | u32 quirks = 0; | ||
| 75 | |||
| 76 | if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8443) || | ||
| 77 | panfrost_has_hw_issue(pfdev, HW_ISSUE_11035)) | ||
| 78 | quirks |= SC_LS_PAUSEBUFFER_DISABLE; | ||
| 79 | |||
| 80 | if (panfrost_has_hw_issue(pfdev, HW_ISSUE_10327)) | ||
| 81 | quirks |= SC_SDC_DISABLE_OQ_DISCARD; | ||
| 82 | |||
| 83 | if (panfrost_has_hw_issue(pfdev, HW_ISSUE_10797)) | ||
| 84 | quirks |= SC_ENABLE_TEXGRD_FLAGS; | ||
| 85 | |||
| 86 | if (!panfrost_has_hw_issue(pfdev, GPUCORE_1619)) { | ||
| 87 | if (panfrost_model_cmp(pfdev, 0x750) < 0) /* T60x, T62x, T72x */ | ||
| 88 | quirks |= SC_LS_ATTR_CHECK_DISABLE; | ||
| 89 | else if (panfrost_model_cmp(pfdev, 0x880) <= 0) /* T76x, T8xx */ | ||
| 90 | quirks |= SC_LS_ALLOW_ATTR_TYPES; | ||
| 91 | } | ||
| 92 | |||
| 93 | if (panfrost_has_hw_feature(pfdev, HW_FEATURE_TLS_HASHING)) | ||
| 94 | quirks |= SC_TLS_HASH_ENABLE; | ||
| 95 | |||
| 96 | if (quirks) | ||
| 97 | gpu_write(pfdev, GPU_SHADER_CONFIG, quirks); | ||
| 98 | |||
| 99 | |||
| 100 | quirks = gpu_read(pfdev, GPU_TILER_CONFIG); | ||
| 101 | |||
| 102 | /* Set tiler clock gate override if required */ | ||
| 103 | if (panfrost_has_hw_issue(pfdev, HW_ISSUE_T76X_3953)) | ||
| 104 | quirks |= TC_CLOCK_GATE_OVERRIDE; | ||
| 105 | |||
| 106 | gpu_write(pfdev, GPU_TILER_CONFIG, quirks); | ||
| 107 | |||
| 108 | |||
| 109 | quirks = gpu_read(pfdev, GPU_L2_MMU_CONFIG); | ||
| 110 | |||
| 111 | /* Limit read & write ID width for AXI */ | ||
| 112 | if (panfrost_has_hw_feature(pfdev, HW_FEATURE_3BIT_EXT_RW_L2_MMU_CONFIG)) | ||
| 113 | quirks &= ~(L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_READS | | ||
| 114 | L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_WRITES); | ||
| 115 | else | ||
| 116 | quirks &= ~(L2_MMU_CONFIG_LIMIT_EXTERNAL_READS | | ||
| 117 | L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES); | ||
| 118 | |||
| 119 | gpu_write(pfdev, GPU_L2_MMU_CONFIG, quirks); | ||
| 120 | |||
| 121 | quirks = 0; | ||
| 122 | if ((panfrost_model_eq(pfdev, 0x860) || panfrost_model_eq(pfdev, 0x880)) && | ||
| 123 | pfdev->features.revision >= 0x2000) | ||
| 124 | quirks |= JM_MAX_JOB_THROTTLE_LIMIT << JM_JOB_THROTTLE_LIMIT_SHIFT; | ||
| 125 | else if (panfrost_model_eq(pfdev, 0x6000) && | ||
| 126 | pfdev->features.coherency_features == COHERENCY_ACE) | ||
| 127 | quirks |= (COHERENCY_ACE_LITE | COHERENCY_ACE) << | ||
| 128 | JM_FORCE_COHERENCY_FEATURES_SHIFT; | ||
| 129 | |||
| 130 | if (quirks) | ||
| 131 | gpu_write(pfdev, GPU_JM_CONFIG, quirks); | ||
| 132 | } | ||
| 133 | |||
| 134 | #define MAX_HW_REVS 6 | ||
| 135 | |||
| 136 | struct panfrost_model { | ||
| 137 | const char *name; | ||
| 138 | u32 id; | ||
| 139 | u32 id_mask; | ||
| 140 | u64 features; | ||
| 141 | u64 issues; | ||
| 142 | struct { | ||
| 143 | u32 revision; | ||
| 144 | u64 issues; | ||
| 145 | } revs[MAX_HW_REVS]; | ||
| 146 | }; | ||
| 147 | |||
| 148 | #define GPU_MODEL(_name, _id, ...) \ | ||
| 149 | {\ | ||
| 150 | .name = __stringify(_name), \ | ||
| 151 | .id = _id, \ | ||
| 152 | .features = hw_features_##_name, \ | ||
| 153 | .issues = hw_issues_##_name, \ | ||
| 154 | .revs = { __VA_ARGS__ }, \ | ||
| 155 | } | ||
| 156 | |||
| 157 | #define GPU_REV_EXT(name, _rev, _p, _s, stat) \ | ||
| 158 | {\ | ||
| 159 | .revision = (_rev) << 12 | (_p) << 4 | (_s), \ | ||
| 160 | .issues = hw_issues_##name##_r##_rev##p##_p##stat, \ | ||
| 161 | } | ||
| 162 | #define GPU_REV(name, r, p) GPU_REV_EXT(name, r, p, 0, ) | ||
| 163 | |||
| 164 | static const struct panfrost_model gpu_models[] = { | ||
| 165 | /* T60x has an oddball version */ | ||
| 166 | GPU_MODEL(t600, 0x600, | ||
| 167 | GPU_REV_EXT(t600, 0, 0, 1, _15dev0)), | ||
| 168 | GPU_MODEL(t620, 0x620, | ||
| 169 | GPU_REV(t620, 0, 1), GPU_REV(t620, 1, 0)), | ||
| 170 | GPU_MODEL(t720, 0x720), | ||
| 171 | GPU_MODEL(t760, 0x750, | ||
| 172 | GPU_REV(t760, 0, 0), GPU_REV(t760, 0, 1), | ||
| 173 | GPU_REV_EXT(t760, 0, 1, 0, _50rel0), | ||
| 174 | GPU_REV(t760, 0, 2), GPU_REV(t760, 0, 3)), | ||
| 175 | GPU_MODEL(t820, 0x820), | ||
| 176 | GPU_MODEL(t830, 0x830), | ||
| 177 | GPU_MODEL(t860, 0x860), | ||
| 178 | GPU_MODEL(t880, 0x880), | ||
| 179 | |||
| 180 | GPU_MODEL(g71, 0x6000, | ||
| 181 | GPU_REV_EXT(g71, 0, 0, 1, _05dev0)), | ||
| 182 | GPU_MODEL(g72, 0x6001), | ||
| 183 | GPU_MODEL(g51, 0x7000), | ||
| 184 | GPU_MODEL(g76, 0x7001), | ||
| 185 | GPU_MODEL(g52, 0x7002), | ||
| 186 | GPU_MODEL(g31, 0x7003, | ||
| 187 | GPU_REV(g31, 1, 0)), | ||
| 188 | }; | ||
| 189 | |||
| 190 | static void panfrost_gpu_init_features(struct panfrost_device *pfdev) | ||
| 191 | { | ||
| 192 | u32 gpu_id, num_js, major, minor, status, rev; | ||
| 193 | const char *name = "unknown"; | ||
| 194 | u64 hw_feat = 0; | ||
| 195 | u64 hw_issues = hw_issues_all; | ||
| 196 | const struct panfrost_model *model; | ||
| 197 | int i; | ||
| 198 | |||
| 199 | pfdev->features.l2_features = gpu_read(pfdev, GPU_L2_FEATURES); | ||
| 200 | pfdev->features.core_features = gpu_read(pfdev, GPU_CORE_FEATURES); | ||
| 201 | pfdev->features.tiler_features = gpu_read(pfdev, GPU_TILER_FEATURES); | ||
| 202 | pfdev->features.mem_features = gpu_read(pfdev, GPU_MEM_FEATURES); | ||
| 203 | pfdev->features.mmu_features = gpu_read(pfdev, GPU_MMU_FEATURES); | ||
| 204 | pfdev->features.thread_features = gpu_read(pfdev, GPU_THREAD_FEATURES); | ||
| 205 | pfdev->features.coherency_features = gpu_read(pfdev, GPU_COHERENCY_FEATURES); | ||
| 206 | for (i = 0; i < 4; i++) | ||
| 207 | pfdev->features.texture_features[i] = gpu_read(pfdev, GPU_TEXTURE_FEATURES(i)); | ||
| 208 | |||
| 209 | pfdev->features.as_present = gpu_read(pfdev, GPU_AS_PRESENT); | ||
| 210 | |||
| 211 | pfdev->features.js_present = gpu_read(pfdev, GPU_JS_PRESENT); | ||
| 212 | num_js = hweight32(pfdev->features.js_present); | ||
| 213 | for (i = 0; i < num_js; i++) | ||
| 214 | pfdev->features.js_features[i] = gpu_read(pfdev, GPU_JS_FEATURES(i)); | ||
| 215 | |||
| 216 | pfdev->features.shader_present = gpu_read(pfdev, GPU_SHADER_PRESENT_LO); | ||
| 217 | pfdev->features.shader_present |= (u64)gpu_read(pfdev, GPU_SHADER_PRESENT_HI) << 32; | ||
| 218 | |||
| 219 | pfdev->features.tiler_present = gpu_read(pfdev, GPU_TILER_PRESENT_LO); | ||
| 220 | pfdev->features.tiler_present |= (u64)gpu_read(pfdev, GPU_TILER_PRESENT_HI) << 32; | ||
| 221 | |||
| 222 | pfdev->features.l2_present = gpu_read(pfdev, GPU_L2_PRESENT_LO); | ||
| 223 | pfdev->features.l2_present |= (u64)gpu_read(pfdev, GPU_L2_PRESENT_HI) << 32; | ||
| 224 | pfdev->features.nr_core_groups = hweight64(pfdev->features.l2_present); | ||
| 225 | |||
| 226 | pfdev->features.stack_present = gpu_read(pfdev, GPU_STACK_PRESENT_LO); | ||
| 227 | pfdev->features.stack_present |= (u64)gpu_read(pfdev, GPU_STACK_PRESENT_HI) << 32; | ||
| 228 | |||
| 229 | gpu_id = gpu_read(pfdev, GPU_ID); | ||
| 230 | pfdev->features.revision = gpu_id & 0xffff; | ||
| 231 | pfdev->features.id = gpu_id >> 16; | ||
| 232 | |||
| 233 | /* The T60x has an oddball ID value. Fix it up to the standard Midgard | ||
| 234 | * format so we (and userspace) don't have to special case it. | ||
| 235 | */ | ||
| 236 | if (pfdev->features.id == 0x6956) | ||
| 237 | pfdev->features.id = 0x0600; | ||
| 238 | |||
| 239 | major = (pfdev->features.revision >> 12) & 0xf; | ||
| 240 | minor = (pfdev->features.revision >> 4) & 0xff; | ||
| 241 | status = pfdev->features.revision & 0xf; | ||
| 242 | rev = pfdev->features.revision; | ||
| 243 | |||
| 244 | gpu_id = pfdev->features.id; | ||
| 245 | |||
| 246 | for (model = gpu_models; model->name; model++) { | ||
| 247 | int best = -1; | ||
| 248 | |||
| 249 | if (!panfrost_model_eq(pfdev, model->id)) | ||
| 250 | continue; | ||
| 251 | |||
| 252 | name = model->name; | ||
| 253 | hw_feat = model->features; | ||
| 254 | hw_issues |= model->issues; | ||
| 255 | for (i = 0; i < MAX_HW_REVS; i++) { | ||
| 256 | if (model->revs[i].revision == rev) { | ||
| 257 | best = i; | ||
| 258 | break; | ||
| 259 | } else if (model->revs[i].revision == (rev & ~0xf)) | ||
| 260 | best = i; | ||
| 261 | } | ||
| 262 | |||
| 263 | if (best >= 0) | ||
| 264 | hw_issues |= model->revs[best].issues; | ||
| 265 | |||
| 266 | break; | ||
| 267 | } | ||
| 268 | |||
| 269 | bitmap_from_u64(pfdev->features.hw_features, hw_feat); | ||
| 270 | bitmap_from_u64(pfdev->features.hw_issues, hw_issues); | ||
| 271 | |||
| 272 | dev_info(pfdev->dev, "mali-%s id 0x%x major 0x%x minor 0x%x status 0x%x", | ||
| 273 | name, gpu_id, major, minor, status); | ||
| 274 | dev_info(pfdev->dev, "features: %64pb, issues: %64pb", | ||
| 275 | pfdev->features.hw_features, | ||
| 276 | pfdev->features.hw_issues); | ||
| 277 | |||
| 278 | dev_info(pfdev->dev, "Features: L2:0x%08x Shader:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x JS:0x%x", | ||
| 279 | gpu_read(pfdev, GPU_L2_FEATURES), | ||
| 280 | gpu_read(pfdev, GPU_CORE_FEATURES), | ||
| 281 | gpu_read(pfdev, GPU_TILER_FEATURES), | ||
| 282 | gpu_read(pfdev, GPU_MEM_FEATURES), | ||
| 283 | gpu_read(pfdev, GPU_MMU_FEATURES), | ||
| 284 | gpu_read(pfdev, GPU_AS_PRESENT), | ||
| 285 | gpu_read(pfdev, GPU_JS_PRESENT)); | ||
| 286 | |||
| 287 | dev_info(pfdev->dev, "shader_present=0x%0llx l2_present=0x%0llx", | ||
| 288 | pfdev->features.shader_present, pfdev->features.l2_present); | ||
| 289 | } | ||
| 290 | |||
| 291 | void panfrost_gpu_power_on(struct panfrost_device *pfdev) | ||
| 292 | { | ||
| 293 | int ret; | ||
| 294 | u32 val; | ||
| 295 | |||
| 296 | /* Just turn on everything for now */ | ||
| 297 | gpu_write(pfdev, L2_PWRON_LO, pfdev->features.l2_present); | ||
| 298 | ret = readl_relaxed_poll_timeout(pfdev->iomem + L2_READY_LO, | ||
| 299 | val, val == pfdev->features.l2_present, 100, 1000); | ||
| 300 | |||
| 301 | gpu_write(pfdev, STACK_PWRON_LO, pfdev->features.stack_present); | ||
| 302 | ret |= readl_relaxed_poll_timeout(pfdev->iomem + STACK_READY_LO, | ||
| 303 | val, val == pfdev->features.stack_present, 100, 1000); | ||
| 304 | |||
| 305 | gpu_write(pfdev, SHADER_PWRON_LO, pfdev->features.shader_present); | ||
| 306 | ret |= readl_relaxed_poll_timeout(pfdev->iomem + SHADER_READY_LO, | ||
| 307 | val, val == pfdev->features.shader_present, 100, 1000); | ||
| 308 | |||
| 309 | gpu_write(pfdev, TILER_PWRON_LO, pfdev->features.tiler_present); | ||
| 310 | ret |= readl_relaxed_poll_timeout(pfdev->iomem + TILER_READY_LO, | ||
| 311 | val, val == pfdev->features.tiler_present, 100, 1000); | ||
| 312 | |||
| 313 | if (ret) | ||
| 314 | dev_err(pfdev->dev, "error powering up gpu"); | ||
| 315 | } | ||
| 316 | |||
| 317 | void panfrost_gpu_power_off(struct panfrost_device *pfdev) | ||
| 318 | { | ||
| 319 | gpu_write(pfdev, TILER_PWROFF_LO, 0); | ||
| 320 | gpu_write(pfdev, SHADER_PWROFF_LO, 0); | ||
| 321 | gpu_write(pfdev, STACK_PWROFF_LO, 0); | ||
| 322 | gpu_write(pfdev, L2_PWROFF_LO, 0); | ||
| 323 | } | ||
| 324 | |||
| 325 | int panfrost_gpu_init(struct panfrost_device *pfdev) | ||
| 326 | { | ||
| 327 | int err, irq; | ||
| 328 | |||
| 329 | err = panfrost_gpu_soft_reset(pfdev); | ||
| 330 | if (err) | ||
| 331 | return err; | ||
| 332 | |||
| 333 | panfrost_gpu_init_features(pfdev); | ||
| 334 | |||
| 335 | irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu"); | ||
| 336 | if (irq <= 0) | ||
| 337 | return -ENODEV; | ||
| 338 | |||
| 339 | err = devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler, | ||
| 340 | IRQF_SHARED, "gpu", pfdev); | ||
| 341 | if (err) { | ||
| 342 | dev_err(pfdev->dev, "failed to request gpu irq"); | ||
| 343 | return err; | ||
| 344 | } | ||
| 345 | |||
| 346 | panfrost_gpu_init_quirks(pfdev); | ||
| 347 | panfrost_gpu_power_on(pfdev); | ||
| 348 | |||
| 349 | return 0; | ||
| 350 | } | ||
| 351 | |||
| 352 | void panfrost_gpu_fini(struct panfrost_device *pfdev) | ||
| 353 | { | ||
| 354 | panfrost_gpu_power_off(pfdev); | ||
| 355 | } | ||
| 356 | |||
| 357 | u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev) | ||
| 358 | { | ||
| 359 | if (panfrost_has_hw_feature(pfdev, HW_FEATURE_FLUSH_REDUCTION)) | ||
| 360 | return gpu_read(pfdev, GPU_LATEST_FLUSH_ID); | ||
| 361 | return 0; | ||
| 362 | } | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.h b/drivers/gpu/drm/panfrost/panfrost_gpu.h new file mode 100644 index 000000000000..4112412087b2 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ | ||
| 3 | /* Copyright 2019 Collabora ltd. */ | ||
| 4 | |||
| 5 | #ifndef __PANFROST_GPU_H__ | ||
| 6 | #define __PANFROST_GPU_H__ | ||
| 7 | |||
| 8 | struct panfrost_device; | ||
| 9 | |||
| 10 | int panfrost_gpu_init(struct panfrost_device *pfdev); | ||
| 11 | void panfrost_gpu_fini(struct panfrost_device *pfdev); | ||
| 12 | |||
| 13 | u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev); | ||
| 14 | |||
| 15 | int panfrost_gpu_soft_reset(struct panfrost_device *pfdev); | ||
| 16 | void panfrost_gpu_power_on(struct panfrost_device *pfdev); | ||
| 17 | void panfrost_gpu_power_off(struct panfrost_device *pfdev); | ||
| 18 | |||
| 19 | #endif | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_issues.h b/drivers/gpu/drm/panfrost/panfrost_issues.h new file mode 100644 index 000000000000..cec6dcdadb5c --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_issues.h | |||
| @@ -0,0 +1,176 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* (C) COPYRIGHT 2014-2018 ARM Limited. All rights reserved. */ | ||
| 3 | /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */ | ||
| 4 | #ifndef __PANFROST_ISSUES_H__ | ||
| 5 | #define __PANFROST_ISSUES_H__ | ||
| 6 | |||
| 7 | #include <linux/bitops.h> | ||
| 8 | |||
| 9 | #include "panfrost_device.h" | ||
| 10 | |||
| 11 | /* | ||
| 12 | * This is not a complete list of issues, but only the ones the driver needs | ||
| 13 | * to care about. | ||
| 14 | */ | ||
| 15 | enum panfrost_hw_issue { | ||
| 16 | HW_ISSUE_6367, | ||
| 17 | HW_ISSUE_6787, | ||
| 18 | HW_ISSUE_8186, | ||
| 19 | HW_ISSUE_8245, | ||
| 20 | HW_ISSUE_8316, | ||
| 21 | HW_ISSUE_8394, | ||
| 22 | HW_ISSUE_8401, | ||
| 23 | HW_ISSUE_8408, | ||
| 24 | HW_ISSUE_8443, | ||
| 25 | HW_ISSUE_8987, | ||
| 26 | HW_ISSUE_9435, | ||
| 27 | HW_ISSUE_9510, | ||
| 28 | HW_ISSUE_9630, | ||
| 29 | HW_ISSUE_10327, | ||
| 30 | HW_ISSUE_10649, | ||
| 31 | HW_ISSUE_10676, | ||
| 32 | HW_ISSUE_10797, | ||
| 33 | HW_ISSUE_10817, | ||
| 34 | HW_ISSUE_10883, | ||
| 35 | HW_ISSUE_10959, | ||
| 36 | HW_ISSUE_10969, | ||
| 37 | HW_ISSUE_11020, | ||
| 38 | HW_ISSUE_11024, | ||
| 39 | HW_ISSUE_11035, | ||
| 40 | HW_ISSUE_11056, | ||
| 41 | HW_ISSUE_T76X_3542, | ||
| 42 | HW_ISSUE_T76X_3953, | ||
| 43 | HW_ISSUE_TMIX_8463, | ||
| 44 | GPUCORE_1619, | ||
| 45 | HW_ISSUE_TMIX_8438, | ||
| 46 | HW_ISSUE_TGOX_R1_1234, | ||
| 47 | HW_ISSUE_END | ||
| 48 | }; | ||
| 49 | |||
| 50 | #define hw_issues_all (\ | ||
| 51 | BIT_ULL(HW_ISSUE_9435)) | ||
| 52 | |||
| 53 | #define hw_issues_t600 (\ | ||
| 54 | BIT_ULL(HW_ISSUE_6367) | \ | ||
| 55 | BIT_ULL(HW_ISSUE_6787) | \ | ||
| 56 | BIT_ULL(HW_ISSUE_8408) | \ | ||
| 57 | BIT_ULL(HW_ISSUE_9510) | \ | ||
| 58 | BIT_ULL(HW_ISSUE_10649) | \ | ||
| 59 | BIT_ULL(HW_ISSUE_10676) | \ | ||
| 60 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 61 | BIT_ULL(HW_ISSUE_11020) | \ | ||
| 62 | BIT_ULL(HW_ISSUE_11035) | \ | ||
| 63 | BIT_ULL(HW_ISSUE_11056) | \ | ||
| 64 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 65 | |||
| 66 | #define hw_issues_t600_r0p0_15dev0 (\ | ||
| 67 | BIT_ULL(HW_ISSUE_8186) | \ | ||
| 68 | BIT_ULL(HW_ISSUE_8245) | \ | ||
| 69 | BIT_ULL(HW_ISSUE_8316) | \ | ||
| 70 | BIT_ULL(HW_ISSUE_8394) | \ | ||
| 71 | BIT_ULL(HW_ISSUE_8401) | \ | ||
| 72 | BIT_ULL(HW_ISSUE_8443) | \ | ||
| 73 | BIT_ULL(HW_ISSUE_8987) | \ | ||
| 74 | BIT_ULL(HW_ISSUE_9630) | \ | ||
| 75 | BIT_ULL(HW_ISSUE_10969) | \ | ||
| 76 | BIT_ULL(GPUCORE_1619)) | ||
| 77 | |||
| 78 | #define hw_issues_t620 (\ | ||
| 79 | BIT_ULL(HW_ISSUE_10649) | \ | ||
| 80 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 81 | BIT_ULL(HW_ISSUE_10959) | \ | ||
| 82 | BIT_ULL(HW_ISSUE_11056) | \ | ||
| 83 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 84 | |||
| 85 | #define hw_issues_t620_r0p1 (\ | ||
| 86 | BIT_ULL(HW_ISSUE_10327) | \ | ||
| 87 | BIT_ULL(HW_ISSUE_10676) | \ | ||
| 88 | BIT_ULL(HW_ISSUE_10817) | \ | ||
| 89 | BIT_ULL(HW_ISSUE_11020) | \ | ||
| 90 | BIT_ULL(HW_ISSUE_11024) | \ | ||
| 91 | BIT_ULL(HW_ISSUE_11035)) | ||
| 92 | |||
| 93 | #define hw_issues_t620_r1p0 (\ | ||
| 94 | BIT_ULL(HW_ISSUE_11020) | \ | ||
| 95 | BIT_ULL(HW_ISSUE_11024)) | ||
| 96 | |||
| 97 | #define hw_issues_t720 (\ | ||
| 98 | BIT_ULL(HW_ISSUE_10649) | \ | ||
| 99 | BIT_ULL(HW_ISSUE_10797) | \ | ||
| 100 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 101 | BIT_ULL(HW_ISSUE_11056) | \ | ||
| 102 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 103 | |||
| 104 | #define hw_issues_t760 (\ | ||
| 105 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 106 | BIT_ULL(HW_ISSUE_T76X_3953) | \ | ||
| 107 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 108 | |||
| 109 | #define hw_issues_t760_r0p0 (\ | ||
| 110 | BIT_ULL(HW_ISSUE_11020) | \ | ||
| 111 | BIT_ULL(HW_ISSUE_11024) | \ | ||
| 112 | BIT_ULL(HW_ISSUE_T76X_3542)) | ||
| 113 | |||
| 114 | #define hw_issues_t760_r0p1 (\ | ||
| 115 | BIT_ULL(HW_ISSUE_11020) | \ | ||
| 116 | BIT_ULL(HW_ISSUE_11024) | \ | ||
| 117 | BIT_ULL(HW_ISSUE_T76X_3542)) | ||
| 118 | |||
| 119 | #define hw_issues_t760_r0p1_50rel0 (\ | ||
| 120 | BIT_ULL(HW_ISSUE_T76X_3542)) | ||
| 121 | |||
| 122 | #define hw_issues_t760_r0p2 (\ | ||
| 123 | BIT_ULL(HW_ISSUE_11020) | \ | ||
| 124 | BIT_ULL(HW_ISSUE_11024) | \ | ||
| 125 | BIT_ULL(HW_ISSUE_T76X_3542)) | ||
| 126 | |||
| 127 | #define hw_issues_t760_r0p3 (\ | ||
| 128 | BIT_ULL(HW_ISSUE_T76X_3542)) | ||
| 129 | |||
| 130 | #define hw_issues_t820 (\ | ||
| 131 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 132 | BIT_ULL(HW_ISSUE_T76X_3953) | \ | ||
| 133 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 134 | |||
| 135 | #define hw_issues_t830 (\ | ||
| 136 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 137 | BIT_ULL(HW_ISSUE_T76X_3953) | \ | ||
| 138 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 139 | |||
| 140 | #define hw_issues_t860 (\ | ||
| 141 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 142 | BIT_ULL(HW_ISSUE_T76X_3953) | \ | ||
| 143 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 144 | |||
| 145 | #define hw_issues_t880 (\ | ||
| 146 | BIT_ULL(HW_ISSUE_10883) | \ | ||
| 147 | BIT_ULL(HW_ISSUE_T76X_3953) | \ | ||
| 148 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 149 | |||
| 150 | #define hw_issues_g31 0 | ||
| 151 | |||
| 152 | #define hw_issues_g31_r1p0 (\ | ||
| 153 | BIT_ULL(HW_ISSUE_TGOX_R1_1234)) | ||
| 154 | |||
| 155 | #define hw_issues_g51 0 | ||
| 156 | |||
| 157 | #define hw_issues_g52 0 | ||
| 158 | |||
| 159 | #define hw_issues_g71 (\ | ||
| 160 | BIT_ULL(HW_ISSUE_TMIX_8463) | \ | ||
| 161 | BIT_ULL(HW_ISSUE_TMIX_8438)) | ||
| 162 | |||
| 163 | #define hw_issues_g71_r0p0_05dev0 (\ | ||
| 164 | BIT_ULL(HW_ISSUE_T76X_3953)) | ||
| 165 | |||
| 166 | #define hw_issues_g72 0 | ||
| 167 | |||
| 168 | #define hw_issues_g76 0 | ||
| 169 | |||
| 170 | static inline bool panfrost_has_hw_issue(struct panfrost_device *pfdev, | ||
| 171 | enum panfrost_hw_issue issue) | ||
| 172 | { | ||
| 173 | return test_bit(issue, pfdev->features.hw_issues); | ||
| 174 | } | ||
| 175 | |||
| 176 | #endif /* __PANFROST_ISSUES_H__ */ | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c new file mode 100644 index 000000000000..0a7ed04f7d52 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_job.c | |||
| @@ -0,0 +1,560 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 3 | /* Copyright 2019 Collabora ltd. */ | ||
| 4 | #include <linux/delay.h> | ||
| 5 | #include <linux/interrupt.h> | ||
| 6 | #include <linux/io.h> | ||
| 7 | #include <linux/platform_device.h> | ||
| 8 | #include <linux/pm_runtime.h> | ||
| 9 | #include <linux/reservation.h> | ||
| 10 | #include <drm/gpu_scheduler.h> | ||
| 11 | #include <drm/panfrost_drm.h> | ||
| 12 | |||
| 13 | #include "panfrost_device.h" | ||
| 14 | #include "panfrost_devfreq.h" | ||
| 15 | #include "panfrost_job.h" | ||
| 16 | #include "panfrost_features.h" | ||
| 17 | #include "panfrost_issues.h" | ||
| 18 | #include "panfrost_gem.h" | ||
| 19 | #include "panfrost_regs.h" | ||
| 20 | #include "panfrost_gpu.h" | ||
| 21 | #include "panfrost_mmu.h" | ||
| 22 | |||
| 23 | #define job_write(dev, reg, data) writel(data, dev->iomem + (reg)) | ||
| 24 | #define job_read(dev, reg) readl(dev->iomem + (reg)) | ||
| 25 | |||
| 26 | struct panfrost_queue_state { | ||
| 27 | struct drm_gpu_scheduler sched; | ||
| 28 | |||
| 29 | u64 fence_context; | ||
| 30 | u64 emit_seqno; | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct panfrost_job_slot { | ||
| 34 | struct panfrost_queue_state queue[NUM_JOB_SLOTS]; | ||
| 35 | spinlock_t job_lock; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static struct panfrost_job * | ||
| 39 | to_panfrost_job(struct drm_sched_job *sched_job) | ||
| 40 | { | ||
| 41 | return container_of(sched_job, struct panfrost_job, base); | ||
| 42 | } | ||
| 43 | |||
| 44 | struct panfrost_fence { | ||
| 45 | struct dma_fence base; | ||
| 46 | struct drm_device *dev; | ||
| 47 | /* panfrost seqno for signaled() test */ | ||
| 48 | u64 seqno; | ||
| 49 | int queue; | ||
| 50 | }; | ||
| 51 | |||
| 52 | static inline struct panfrost_fence * | ||
| 53 | to_panfrost_fence(struct dma_fence *fence) | ||
| 54 | { | ||
| 55 | return (struct panfrost_fence *)fence; | ||
| 56 | } | ||
| 57 | |||
| 58 | static const char *panfrost_fence_get_driver_name(struct dma_fence *fence) | ||
| 59 | { | ||
| 60 | return "panfrost"; | ||
| 61 | } | ||
| 62 | |||
| 63 | static const char *panfrost_fence_get_timeline_name(struct dma_fence *fence) | ||
| 64 | { | ||
| 65 | struct panfrost_fence *f = to_panfrost_fence(fence); | ||
| 66 | |||
| 67 | switch (f->queue) { | ||
| 68 | case 0: | ||
| 69 | return "panfrost-js-0"; | ||
| 70 | case 1: | ||
| 71 | return "panfrost-js-1"; | ||
| 72 | case 2: | ||
| 73 | return "panfrost-js-2"; | ||
| 74 | default: | ||
| 75 | return NULL; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | static const struct dma_fence_ops panfrost_fence_ops = { | ||
| 80 | .get_driver_name = panfrost_fence_get_driver_name, | ||
| 81 | .get_timeline_name = panfrost_fence_get_timeline_name, | ||
| 82 | }; | ||
| 83 | |||
| 84 | static struct dma_fence *panfrost_fence_create(struct panfrost_device *pfdev, int js_num) | ||
| 85 | { | ||
| 86 | struct panfrost_fence *fence; | ||
| 87 | struct panfrost_job_slot *js = pfdev->js; | ||
| 88 | |||
| 89 | fence = kzalloc(sizeof(*fence), GFP_KERNEL); | ||
| 90 | if (!fence) | ||
| 91 | return ERR_PTR(-ENOMEM); | ||
| 92 | |||
| 93 | fence->dev = pfdev->ddev; | ||
| 94 | fence->queue = js_num; | ||
| 95 | fence->seqno = ++js->queue[js_num].emit_seqno; | ||
| 96 | dma_fence_init(&fence->base, &panfrost_fence_ops, &js->job_lock, | ||
| 97 | js->queue[js_num].fence_context, fence->seqno); | ||
| 98 | |||
| 99 | return &fence->base; | ||
| 100 | } | ||
| 101 | |||
| 102 | static int panfrost_job_get_slot(struct panfrost_job *job) | ||
| 103 | { | ||
| 104 | /* JS0: fragment jobs. | ||
| 105 | * JS1: vertex/tiler jobs | ||
| 106 | * JS2: compute jobs | ||
| 107 | */ | ||
| 108 | if (job->requirements & PANFROST_JD_REQ_FS) | ||
| 109 | return 0; | ||
| 110 | |||
| 111 | /* Not exposed to userspace yet */ | ||
| 112 | #if 0 | ||
| 113 | if (job->requirements & PANFROST_JD_REQ_ONLY_COMPUTE) { | ||
| 114 | if ((job->requirements & PANFROST_JD_REQ_CORE_GRP_MASK) && | ||
| 115 | (job->pfdev->features.nr_core_groups == 2)) | ||
| 116 | return 2; | ||
| 117 | if (panfrost_has_hw_issue(job->pfdev, HW_ISSUE_8987)) | ||
| 118 | return 2; | ||
| 119 | } | ||
| 120 | #endif | ||
| 121 | return 1; | ||
| 122 | } | ||
| 123 | |||
| 124 | static void panfrost_job_write_affinity(struct panfrost_device *pfdev, | ||
| 125 | u32 requirements, | ||
| 126 | int js) | ||
| 127 | { | ||
| 128 | u64 affinity; | ||
| 129 | |||
| 130 | /* | ||
| 131 | * Use all cores for now. | ||
| 132 | * Eventually we may need to support tiler only jobs and h/w with | ||
| 133 | * multiple (2) coherent core groups | ||
| 134 | */ | ||
| 135 | affinity = pfdev->features.shader_present; | ||
| 136 | |||
| 137 | job_write(pfdev, JS_AFFINITY_NEXT_LO(js), affinity & 0xFFFFFFFF); | ||
| 138 | job_write(pfdev, JS_AFFINITY_NEXT_HI(js), affinity >> 32); | ||
| 139 | } | ||
| 140 | |||
| 141 | static void panfrost_job_hw_submit(struct panfrost_job *job, int js) | ||
| 142 | { | ||
| 143 | struct panfrost_device *pfdev = job->pfdev; | ||
| 144 | unsigned long flags; | ||
| 145 | u32 cfg; | ||
| 146 | u64 jc_head = job->jc; | ||
| 147 | int ret; | ||
| 148 | |||
| 149 | ret = pm_runtime_get_sync(pfdev->dev); | ||
| 150 | if (ret < 0) | ||
| 151 | return; | ||
| 152 | |||
| 153 | if (WARN_ON(job_read(pfdev, JS_COMMAND_NEXT(js)))) | ||
| 154 | goto end; | ||
| 155 | |||
| 156 | panfrost_devfreq_record_transition(pfdev, js); | ||
| 157 | spin_lock_irqsave(&pfdev->hwaccess_lock, flags); | ||
| 158 | |||
| 159 | job_write(pfdev, JS_HEAD_NEXT_LO(js), jc_head & 0xFFFFFFFF); | ||
| 160 | job_write(pfdev, JS_HEAD_NEXT_HI(js), jc_head >> 32); | ||
| 161 | |||
| 162 | panfrost_job_write_affinity(pfdev, job->requirements, js); | ||
| 163 | |||
| 164 | /* start MMU, medium priority, cache clean/flush on end, clean/flush on | ||
| 165 | * start */ | ||
| 166 | /* TODO: different address spaces */ | ||
| 167 | cfg = JS_CONFIG_THREAD_PRI(8) | | ||
| 168 | JS_CONFIG_START_FLUSH_CLEAN_INVALIDATE | | ||
| 169 | JS_CONFIG_END_FLUSH_CLEAN_INVALIDATE; | ||
| 170 | |||
| 171 | if (panfrost_has_hw_feature(pfdev, HW_FEATURE_FLUSH_REDUCTION)) | ||
| 172 | cfg |= JS_CONFIG_ENABLE_FLUSH_REDUCTION; | ||
| 173 | |||
| 174 | if (panfrost_has_hw_issue(pfdev, HW_ISSUE_10649)) | ||
| 175 | cfg |= JS_CONFIG_START_MMU; | ||
| 176 | |||
| 177 | job_write(pfdev, JS_CONFIG_NEXT(js), cfg); | ||
| 178 | |||
| 179 | if (panfrost_has_hw_feature(pfdev, HW_FEATURE_FLUSH_REDUCTION)) | ||
| 180 | job_write(pfdev, JS_FLUSH_ID_NEXT(js), job->flush_id); | ||
| 181 | |||
| 182 | /* GO ! */ | ||
| 183 | dev_dbg(pfdev->dev, "JS: Submitting atom %p to js[%d] with head=0x%llx", | ||
| 184 | job, js, jc_head); | ||
| 185 | |||
| 186 | job_write(pfdev, JS_COMMAND_NEXT(js), JS_COMMAND_START); | ||
| 187 | |||
| 188 | spin_unlock_irqrestore(&pfdev->hwaccess_lock, flags); | ||
| 189 | |||
| 190 | end: | ||
| 191 | pm_runtime_mark_last_busy(pfdev->dev); | ||
| 192 | pm_runtime_put_autosuspend(pfdev->dev); | ||
| 193 | } | ||
| 194 | |||
| 195 | static void panfrost_acquire_object_fences(struct drm_gem_object **bos, | ||
| 196 | int bo_count, | ||
| 197 | struct dma_fence **implicit_fences) | ||
| 198 | { | ||
| 199 | int i; | ||
| 200 | |||
| 201 | for (i = 0; i < bo_count; i++) | ||
| 202 | implicit_fences[i] = reservation_object_get_excl_rcu(bos[i]->resv); | ||
| 203 | } | ||
| 204 | |||
| 205 | static void panfrost_attach_object_fences(struct drm_gem_object **bos, | ||
| 206 | int bo_count, | ||
| 207 | struct dma_fence *fence) | ||
| 208 | { | ||
| 209 | int i; | ||
| 210 | |||
| 211 | for (i = 0; i < bo_count; i++) | ||
| 212 | reservation_object_add_excl_fence(bos[i]->resv, fence); | ||
| 213 | } | ||
| 214 | |||
| 215 | int panfrost_job_push(struct panfrost_job *job) | ||
| 216 | { | ||
| 217 | struct panfrost_device *pfdev = job->pfdev; | ||
| 218 | int slot = panfrost_job_get_slot(job); | ||
| 219 | struct drm_sched_entity *entity = &job->file_priv->sched_entity[slot]; | ||
| 220 | struct ww_acquire_ctx acquire_ctx; | ||
| 221 | int ret = 0; | ||
| 222 | |||
| 223 | mutex_lock(&pfdev->sched_lock); | ||
| 224 | |||
| 225 | ret = drm_gem_lock_reservations(job->bos, job->bo_count, | ||
| 226 | &acquire_ctx); | ||
| 227 | if (ret) { | ||
| 228 | mutex_unlock(&pfdev->sched_lock); | ||
| 229 | return ret; | ||
| 230 | } | ||
| 231 | |||
| 232 | ret = drm_sched_job_init(&job->base, entity, NULL); | ||
| 233 | if (ret) { | ||
| 234 | mutex_unlock(&pfdev->sched_lock); | ||
| 235 | goto unlock; | ||
| 236 | } | ||
| 237 | |||
| 238 | job->render_done_fence = dma_fence_get(&job->base.s_fence->finished); | ||
| 239 | |||
| 240 | kref_get(&job->refcount); /* put by scheduler job completion */ | ||
| 241 | |||
| 242 | panfrost_acquire_object_fences(job->bos, job->bo_count, | ||
| 243 | job->implicit_fences); | ||
| 244 | |||
| 245 | drm_sched_entity_push_job(&job->base, entity); | ||
| 246 | |||
| 247 | mutex_unlock(&pfdev->sched_lock); | ||
| 248 | |||
| 249 | panfrost_attach_object_fences(job->bos, job->bo_count, | ||
| 250 | job->render_done_fence); | ||
| 251 | |||
| 252 | unlock: | ||
| 253 | drm_gem_unlock_reservations(job->bos, job->bo_count, &acquire_ctx); | ||
| 254 | |||
| 255 | return ret; | ||
| 256 | } | ||
| 257 | |||
| 258 | static void panfrost_job_cleanup(struct kref *ref) | ||
| 259 | { | ||
| 260 | struct panfrost_job *job = container_of(ref, struct panfrost_job, | ||
| 261 | refcount); | ||
| 262 | unsigned int i; | ||
| 263 | |||
| 264 | if (job->in_fences) { | ||
| 265 | for (i = 0; i < job->in_fence_count; i++) | ||
| 266 | dma_fence_put(job->in_fences[i]); | ||
| 267 | kvfree(job->in_fences); | ||
| 268 | } | ||
| 269 | if (job->implicit_fences) { | ||
| 270 | for (i = 0; i < job->bo_count; i++) | ||
| 271 | dma_fence_put(job->implicit_fences[i]); | ||
| 272 | kvfree(job->implicit_fences); | ||
| 273 | } | ||
| 274 | dma_fence_put(job->done_fence); | ||
| 275 | dma_fence_put(job->render_done_fence); | ||
| 276 | |||
| 277 | if (job->bos) { | ||
| 278 | for (i = 0; i < job->bo_count; i++) | ||
| 279 | drm_gem_object_put_unlocked(job->bos[i]); | ||
| 280 | kvfree(job->bos); | ||
| 281 | } | ||
| 282 | |||
| 283 | kfree(job); | ||
| 284 | } | ||
| 285 | |||
| 286 | void panfrost_job_put(struct panfrost_job *job) | ||
| 287 | { | ||
| 288 | kref_put(&job->refcount, panfrost_job_cleanup); | ||
| 289 | } | ||
| 290 | |||
| 291 | static void panfrost_job_free(struct drm_sched_job *sched_job) | ||
| 292 | { | ||
| 293 | struct panfrost_job *job = to_panfrost_job(sched_job); | ||
| 294 | |||
| 295 | drm_sched_job_cleanup(sched_job); | ||
| 296 | |||
| 297 | panfrost_job_put(job); | ||
| 298 | } | ||
| 299 | |||
| 300 | static struct dma_fence *panfrost_job_dependency(struct drm_sched_job *sched_job, | ||
| 301 | struct drm_sched_entity *s_entity) | ||
| 302 | { | ||
| 303 | struct panfrost_job *job = to_panfrost_job(sched_job); | ||
| 304 | struct dma_fence *fence; | ||
| 305 | unsigned int i; | ||
| 306 | |||
| 307 | /* Explicit fences */ | ||
| 308 | for (i = 0; i < job->in_fence_count; i++) { | ||
| 309 | if (job->in_fences[i]) { | ||
| 310 | fence = job->in_fences[i]; | ||
| 311 | job->in_fences[i] = NULL; | ||
| 312 | return fence; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | /* Implicit fences, max. one per BO */ | ||
| 317 | for (i = 0; i < job->bo_count; i++) { | ||
| 318 | if (job->implicit_fences[i]) { | ||
| 319 | fence = job->implicit_fences[i]; | ||
| 320 | job->implicit_fences[i] = NULL; | ||
| 321 | return fence; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | return NULL; | ||
| 326 | } | ||
| 327 | |||
| 328 | static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job) | ||
| 329 | { | ||
| 330 | struct panfrost_job *job = to_panfrost_job(sched_job); | ||
| 331 | struct panfrost_device *pfdev = job->pfdev; | ||
| 332 | int slot = panfrost_job_get_slot(job); | ||
| 333 | struct dma_fence *fence = NULL; | ||
| 334 | |||
| 335 | if (unlikely(job->base.s_fence->finished.error)) | ||
| 336 | return NULL; | ||
| 337 | |||
| 338 | pfdev->jobs[slot] = job; | ||
| 339 | |||
| 340 | fence = panfrost_fence_create(pfdev, slot); | ||
| 341 | if (IS_ERR(fence)) | ||
| 342 | return NULL; | ||
| 343 | |||
| 344 | if (job->done_fence) | ||
| 345 | dma_fence_put(job->done_fence); | ||
| 346 | job->done_fence = dma_fence_get(fence); | ||
| 347 | |||
| 348 | panfrost_job_hw_submit(job, slot); | ||
| 349 | |||
| 350 | return fence; | ||
| 351 | } | ||
| 352 | |||
| 353 | void panfrost_job_enable_interrupts(struct panfrost_device *pfdev) | ||
| 354 | { | ||
| 355 | int j; | ||
| 356 | u32 irq_mask = 0; | ||
| 357 | |||
| 358 | for (j = 0; j < NUM_JOB_SLOTS; j++) { | ||
| 359 | irq_mask |= MK_JS_MASK(j); | ||
| 360 | } | ||
| 361 | |||
| 362 | job_write(pfdev, JOB_INT_CLEAR, irq_mask); | ||
| 363 | job_write(pfdev, JOB_INT_MASK, irq_mask); | ||
| 364 | } | ||
| 365 | |||
| 366 | static void panfrost_job_timedout(struct drm_sched_job *sched_job) | ||
| 367 | { | ||
| 368 | struct panfrost_job *job = to_panfrost_job(sched_job); | ||
| 369 | struct panfrost_device *pfdev = job->pfdev; | ||
| 370 | int js = panfrost_job_get_slot(job); | ||
| 371 | int i; | ||
| 372 | |||
| 373 | /* | ||
| 374 | * If the GPU managed to complete this jobs fence, the timeout is | ||
| 375 | * spurious. Bail out. | ||
| 376 | */ | ||
| 377 | if (dma_fence_is_signaled(job->done_fence)) | ||
| 378 | return; | ||
| 379 | |||
| 380 | dev_err(pfdev->dev, "gpu sched timeout, js=%d, status=0x%x, head=0x%x, tail=0x%x, sched_job=%p", | ||
| 381 | js, | ||
| 382 | job_read(pfdev, JS_STATUS(js)), | ||
| 383 | job_read(pfdev, JS_HEAD_LO(js)), | ||
| 384 | job_read(pfdev, JS_TAIL_LO(js)), | ||
| 385 | sched_job); | ||
| 386 | |||
| 387 | for (i = 0; i < NUM_JOB_SLOTS; i++) | ||
| 388 | drm_sched_stop(&pfdev->js->queue[i].sched); | ||
| 389 | |||
| 390 | if (sched_job) | ||
| 391 | drm_sched_increase_karma(sched_job); | ||
| 392 | |||
| 393 | /* panfrost_core_dump(pfdev); */ | ||
| 394 | |||
| 395 | panfrost_devfreq_record_transition(pfdev, js); | ||
| 396 | panfrost_gpu_soft_reset(pfdev); | ||
| 397 | |||
| 398 | /* TODO: Re-enable all other address spaces */ | ||
| 399 | panfrost_mmu_enable(pfdev, 0); | ||
| 400 | panfrost_gpu_power_on(pfdev); | ||
| 401 | panfrost_job_enable_interrupts(pfdev); | ||
| 402 | |||
| 403 | for (i = 0; i < NUM_JOB_SLOTS; i++) | ||
| 404 | drm_sched_resubmit_jobs(&pfdev->js->queue[i].sched); | ||
| 405 | |||
| 406 | /* restart scheduler after GPU is usable again */ | ||
| 407 | for (i = 0; i < NUM_JOB_SLOTS; i++) | ||
| 408 | drm_sched_start(&pfdev->js->queue[i].sched, true); | ||
| 409 | } | ||
| 410 | |||
| 411 | static const struct drm_sched_backend_ops panfrost_sched_ops = { | ||
| 412 | .dependency = panfrost_job_dependency, | ||
| 413 | .run_job = panfrost_job_run, | ||
| 414 | .timedout_job = panfrost_job_timedout, | ||
| 415 | .free_job = panfrost_job_free | ||
| 416 | }; | ||
| 417 | |||
| 418 | static irqreturn_t panfrost_job_irq_handler(int irq, void *data) | ||
| 419 | { | ||
| 420 | struct panfrost_device *pfdev = data; | ||
| 421 | u32 status = job_read(pfdev, JOB_INT_STAT); | ||
| 422 | int j; | ||
| 423 | |||
| 424 | dev_dbg(pfdev->dev, "jobslot irq status=%x\n", status); | ||
| 425 | |||
| 426 | if (!status) | ||
| 427 | return IRQ_NONE; | ||
| 428 | |||
| 429 | pm_runtime_mark_last_busy(pfdev->dev); | ||
| 430 | |||
| 431 | for (j = 0; status; j++) { | ||
| 432 | u32 mask = MK_JS_MASK(j); | ||
| 433 | |||
| 434 | if (!(status & mask)) | ||
| 435 | continue; | ||
| 436 | |||
| 437 | job_write(pfdev, JOB_INT_CLEAR, mask); | ||
| 438 | |||
| 439 | if (status & JOB_INT_MASK_ERR(j)) { | ||
| 440 | job_write(pfdev, JS_COMMAND_NEXT(j), JS_COMMAND_NOP); | ||
| 441 | |||
| 442 | dev_err(pfdev->dev, "js fault, js=%d, status=%s, head=0x%x, tail=0x%x", | ||
| 443 | j, | ||
| 444 | panfrost_exception_name(pfdev, job_read(pfdev, JS_STATUS(j))), | ||
| 445 | job_read(pfdev, JS_HEAD_LO(j)), | ||
| 446 | job_read(pfdev, JS_TAIL_LO(j))); | ||
| 447 | |||
| 448 | drm_sched_fault(&pfdev->js->queue[j].sched); | ||
| 449 | } | ||
| 450 | |||
| 451 | if (status & JOB_INT_MASK_DONE(j)) { | ||
| 452 | panfrost_devfreq_record_transition(pfdev, j); | ||
| 453 | dma_fence_signal(pfdev->jobs[j]->done_fence); | ||
| 454 | } | ||
| 455 | |||
| 456 | status &= ~mask; | ||
| 457 | } | ||
| 458 | |||
| 459 | return IRQ_HANDLED; | ||
| 460 | } | ||
| 461 | |||
| 462 | int panfrost_job_init(struct panfrost_device *pfdev) | ||
| 463 | { | ||
| 464 | struct panfrost_job_slot *js; | ||
| 465 | int ret, j, irq; | ||
| 466 | |||
| 467 | pfdev->js = js = devm_kzalloc(pfdev->dev, sizeof(*js), GFP_KERNEL); | ||
| 468 | if (!js) | ||
| 469 | return -ENOMEM; | ||
| 470 | |||
| 471 | spin_lock_init(&js->job_lock); | ||
| 472 | |||
| 473 | irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "job"); | ||
| 474 | if (irq <= 0) | ||
| 475 | return -ENODEV; | ||
| 476 | |||
| 477 | ret = devm_request_irq(pfdev->dev, irq, panfrost_job_irq_handler, | ||
| 478 | IRQF_SHARED, "job", pfdev); | ||
| 479 | if (ret) { | ||
| 480 | dev_err(pfdev->dev, "failed to request job irq"); | ||
| 481 | return ret; | ||
| 482 | } | ||
| 483 | |||
| 484 | for (j = 0; j < NUM_JOB_SLOTS; j++) { | ||
| 485 | js->queue[j].fence_context = dma_fence_context_alloc(1); | ||
| 486 | |||
| 487 | ret = drm_sched_init(&js->queue[j].sched, | ||
| 488 | &panfrost_sched_ops, | ||
| 489 | 1, 0, msecs_to_jiffies(500), | ||
| 490 | "pan_js"); | ||
| 491 | if (ret) { | ||
| 492 | dev_err(pfdev->dev, "Failed to create scheduler: %d.", ret); | ||
| 493 | goto err_sched; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | |||
| 497 | panfrost_job_enable_interrupts(pfdev); | ||
| 498 | |||
| 499 | return 0; | ||
| 500 | |||
| 501 | err_sched: | ||
| 502 | for (j--; j >= 0; j--) | ||
| 503 | drm_sched_fini(&js->queue[j].sched); | ||
| 504 | |||
| 505 | return ret; | ||
| 506 | } | ||
| 507 | |||
| 508 | void panfrost_job_fini(struct panfrost_device *pfdev) | ||
| 509 | { | ||
| 510 | struct panfrost_job_slot *js = pfdev->js; | ||
| 511 | int j; | ||
| 512 | |||
| 513 | job_write(pfdev, JOB_INT_MASK, 0); | ||
| 514 | |||
| 515 | for (j = 0; j < NUM_JOB_SLOTS; j++) | ||
| 516 | drm_sched_fini(&js->queue[j].sched); | ||
| 517 | |||
| 518 | } | ||
| 519 | |||
| 520 | int panfrost_job_open(struct panfrost_file_priv *panfrost_priv) | ||
| 521 | { | ||
| 522 | struct panfrost_device *pfdev = panfrost_priv->pfdev; | ||
| 523 | struct panfrost_job_slot *js = pfdev->js; | ||
| 524 | struct drm_sched_rq *rq; | ||
| 525 | int ret, i; | ||
| 526 | |||
| 527 | for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
| 528 | rq = &js->queue[i].sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL]; | ||
| 529 | ret = drm_sched_entity_init(&panfrost_priv->sched_entity[i], &rq, 1, NULL); | ||
| 530 | if (WARN_ON(ret)) | ||
| 531 | return ret; | ||
| 532 | } | ||
| 533 | return 0; | ||
| 534 | } | ||
| 535 | |||
| 536 | void panfrost_job_close(struct panfrost_file_priv *panfrost_priv) | ||
| 537 | { | ||
| 538 | int i; | ||
| 539 | |||
| 540 | for (i = 0; i < NUM_JOB_SLOTS; i++) | ||
| 541 | drm_sched_entity_destroy(&panfrost_priv->sched_entity[i]); | ||
| 542 | } | ||
| 543 | |||
| 544 | int panfrost_job_is_idle(struct panfrost_device *pfdev) | ||
| 545 | { | ||
| 546 | struct panfrost_job_slot *js = pfdev->js; | ||
| 547 | int i; | ||
| 548 | |||
| 549 | for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
| 550 | /* If there are any jobs in the HW queue, we're not idle */ | ||
| 551 | if (atomic_read(&js->queue[i].sched.hw_rq_count)) | ||
| 552 | return false; | ||
| 553 | |||
| 554 | /* Check whether the hardware is idle */ | ||
| 555 | if (pfdev->devfreq.slot[i].busy) | ||
| 556 | return false; | ||
| 557 | } | ||
| 558 | |||
| 559 | return true; | ||
| 560 | } | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h new file mode 100644 index 000000000000..62454128a792 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_job.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* Copyright 2019 Collabora ltd. */ | ||
| 3 | |||
| 4 | #ifndef __PANFROST_JOB_H__ | ||
| 5 | #define __PANFROST_JOB_H__ | ||
| 6 | |||
| 7 | #include <uapi/drm/panfrost_drm.h> | ||
| 8 | #include <drm/gpu_scheduler.h> | ||
| 9 | |||
| 10 | struct panfrost_device; | ||
| 11 | struct panfrost_gem_object; | ||
| 12 | struct panfrost_file_priv; | ||
| 13 | |||
| 14 | struct panfrost_job { | ||
| 15 | struct drm_sched_job base; | ||
| 16 | |||
| 17 | struct kref refcount; | ||
| 18 | |||
| 19 | struct panfrost_device *pfdev; | ||
| 20 | struct panfrost_file_priv *file_priv; | ||
| 21 | |||
| 22 | /* Optional fences userspace can pass in for the job to depend on. */ | ||
| 23 | struct dma_fence **in_fences; | ||
| 24 | u32 in_fence_count; | ||
| 25 | |||
| 26 | /* Fence to be signaled by IRQ handler when the job is complete. */ | ||
| 27 | struct dma_fence *done_fence; | ||
| 28 | |||
| 29 | __u64 jc; | ||
| 30 | __u32 requirements; | ||
| 31 | __u32 flush_id; | ||
| 32 | |||
| 33 | /* Exclusive fences we have taken from the BOs to wait for */ | ||
| 34 | struct dma_fence **implicit_fences; | ||
| 35 | struct drm_gem_object **bos; | ||
| 36 | u32 bo_count; | ||
| 37 | |||
| 38 | /* Fence to be signaled by drm-sched once its done with the job */ | ||
| 39 | struct dma_fence *render_done_fence; | ||
| 40 | }; | ||
| 41 | |||
| 42 | int panfrost_job_init(struct panfrost_device *pfdev); | ||
| 43 | void panfrost_job_fini(struct panfrost_device *pfdev); | ||
| 44 | int panfrost_job_open(struct panfrost_file_priv *panfrost_priv); | ||
| 45 | void panfrost_job_close(struct panfrost_file_priv *panfrost_priv); | ||
| 46 | int panfrost_job_push(struct panfrost_job *job); | ||
| 47 | void panfrost_job_put(struct panfrost_job *job); | ||
| 48 | void panfrost_job_enable_interrupts(struct panfrost_device *pfdev); | ||
| 49 | int panfrost_job_is_idle(struct panfrost_device *pfdev); | ||
| 50 | |||
| 51 | #endif | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c new file mode 100644 index 000000000000..762b1bd2a8c2 --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c | |||
| @@ -0,0 +1,386 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 3 | #include <linux/bitfield.h> | ||
| 4 | #include <linux/delay.h> | ||
| 5 | #include <linux/interrupt.h> | ||
| 6 | #include <linux/io.h> | ||
| 7 | #include <linux/iopoll.h> | ||
| 8 | #include <linux/io-pgtable.h> | ||
| 9 | #include <linux/iommu.h> | ||
| 10 | #include <linux/platform_device.h> | ||
| 11 | #include <linux/pm_runtime.h> | ||
| 12 | #include <linux/sizes.h> | ||
| 13 | |||
| 14 | #include "panfrost_device.h" | ||
| 15 | #include "panfrost_mmu.h" | ||
| 16 | #include "panfrost_gem.h" | ||
| 17 | #include "panfrost_features.h" | ||
| 18 | #include "panfrost_regs.h" | ||
| 19 | |||
| 20 | #define mmu_write(dev, reg, data) writel(data, dev->iomem + reg) | ||
| 21 | #define mmu_read(dev, reg) readl(dev->iomem + reg) | ||
| 22 | |||
| 23 | struct panfrost_mmu { | ||
| 24 | struct io_pgtable_cfg pgtbl_cfg; | ||
| 25 | struct io_pgtable_ops *pgtbl_ops; | ||
| 26 | struct mutex lock; | ||
| 27 | }; | ||
| 28 | |||
| 29 | static int wait_ready(struct panfrost_device *pfdev, u32 as_nr) | ||
| 30 | { | ||
| 31 | int ret; | ||
| 32 | u32 val; | ||
| 33 | |||
| 34 | /* Wait for the MMU status to indicate there is no active command, in | ||
| 35 | * case one is pending. */ | ||
| 36 | ret = readl_relaxed_poll_timeout_atomic(pfdev->iomem + AS_STATUS(as_nr), | ||
| 37 | val, !(val & AS_STATUS_AS_ACTIVE), 10, 1000); | ||
| 38 | |||
| 39 | if (ret) | ||
| 40 | dev_err(pfdev->dev, "AS_ACTIVE bit stuck\n"); | ||
| 41 | |||
| 42 | return ret; | ||
| 43 | } | ||
| 44 | |||
| 45 | static int write_cmd(struct panfrost_device *pfdev, u32 as_nr, u32 cmd) | ||
| 46 | { | ||
| 47 | int status; | ||
| 48 | |||
| 49 | /* write AS_COMMAND when MMU is ready to accept another command */ | ||
| 50 | status = wait_ready(pfdev, as_nr); | ||
| 51 | if (!status) | ||
| 52 | mmu_write(pfdev, AS_COMMAND(as_nr), cmd); | ||
| 53 | |||
| 54 | return status; | ||
| 55 | } | ||
| 56 | |||
| 57 | static void lock_region(struct panfrost_device *pfdev, u32 as_nr, | ||
| 58 | u64 iova, size_t size) | ||
| 59 | { | ||
| 60 | u8 region_width; | ||
| 61 | u64 region = iova & PAGE_MASK; | ||
| 62 | /* | ||
| 63 | * fls returns: | ||
| 64 | * 1 .. 32 | ||
| 65 | * | ||
| 66 | * 10 + fls(num_pages) | ||
| 67 | * results in the range (11 .. 42) | ||
| 68 | */ | ||
| 69 | |||
| 70 | size = round_up(size, PAGE_SIZE); | ||
| 71 | |||
| 72 | region_width = 10 + fls(size >> PAGE_SHIFT); | ||
| 73 | if ((size >> PAGE_SHIFT) != (1ul << (region_width - 11))) { | ||
| 74 | /* not pow2, so must go up to the next pow2 */ | ||
| 75 | region_width += 1; | ||
| 76 | } | ||
| 77 | region |= region_width; | ||
| 78 | |||
| 79 | /* Lock the region that needs to be updated */ | ||
| 80 | mmu_write(pfdev, AS_LOCKADDR_LO(as_nr), region & 0xFFFFFFFFUL); | ||
| 81 | mmu_write(pfdev, AS_LOCKADDR_HI(as_nr), (region >> 32) & 0xFFFFFFFFUL); | ||
| 82 | write_cmd(pfdev, as_nr, AS_COMMAND_LOCK); | ||
| 83 | } | ||
| 84 | |||
| 85 | |||
| 86 | static int mmu_hw_do_operation(struct panfrost_device *pfdev, u32 as_nr, | ||
| 87 | u64 iova, size_t size, u32 op) | ||
| 88 | { | ||
| 89 | unsigned long flags; | ||
| 90 | int ret; | ||
| 91 | |||
| 92 | spin_lock_irqsave(&pfdev->hwaccess_lock, flags); | ||
| 93 | |||
| 94 | if (op != AS_COMMAND_UNLOCK) | ||
| 95 | lock_region(pfdev, as_nr, iova, size); | ||
| 96 | |||
| 97 | /* Run the MMU operation */ | ||
| 98 | write_cmd(pfdev, as_nr, op); | ||
| 99 | |||
| 100 | /* Wait for the flush to complete */ | ||
| 101 | ret = wait_ready(pfdev, as_nr); | ||
| 102 | |||
| 103 | spin_unlock_irqrestore(&pfdev->hwaccess_lock, flags); | ||
| 104 | |||
| 105 | return ret; | ||
| 106 | } | ||
| 107 | |||
| 108 | void panfrost_mmu_enable(struct panfrost_device *pfdev, u32 as_nr) | ||
| 109 | { | ||
| 110 | struct io_pgtable_cfg *cfg = &pfdev->mmu->pgtbl_cfg; | ||
| 111 | u64 transtab = cfg->arm_mali_lpae_cfg.transtab; | ||
| 112 | u64 memattr = cfg->arm_mali_lpae_cfg.memattr; | ||
| 113 | |||
| 114 | mmu_write(pfdev, MMU_INT_CLEAR, ~0); | ||
| 115 | mmu_write(pfdev, MMU_INT_MASK, ~0); | ||
| 116 | |||
| 117 | mmu_write(pfdev, AS_TRANSTAB_LO(as_nr), transtab & 0xffffffffUL); | ||
| 118 | mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), transtab >> 32); | ||
| 119 | |||
| 120 | /* Need to revisit mem attrs. | ||
| 121 | * NC is the default, Mali driver is inner WT. | ||
| 122 | */ | ||
| 123 | mmu_write(pfdev, AS_MEMATTR_LO(as_nr), memattr & 0xffffffffUL); | ||
| 124 | mmu_write(pfdev, AS_MEMATTR_HI(as_nr), memattr >> 32); | ||
| 125 | |||
| 126 | write_cmd(pfdev, as_nr, AS_COMMAND_UPDATE); | ||
| 127 | } | ||
| 128 | |||
| 129 | static void mmu_disable(struct panfrost_device *pfdev, u32 as_nr) | ||
| 130 | { | ||
| 131 | mmu_write(pfdev, AS_TRANSTAB_LO(as_nr), 0); | ||
| 132 | mmu_write(pfdev, AS_TRANSTAB_HI(as_nr), 0); | ||
| 133 | |||
| 134 | mmu_write(pfdev, AS_MEMATTR_LO(as_nr), 0); | ||
| 135 | mmu_write(pfdev, AS_MEMATTR_HI(as_nr), 0); | ||
| 136 | |||
| 137 | write_cmd(pfdev, as_nr, AS_COMMAND_UPDATE); | ||
| 138 | } | ||
| 139 | |||
| 140 | static size_t get_pgsize(u64 addr, size_t size) | ||
| 141 | { | ||
| 142 | if (addr & (SZ_2M - 1) || size < SZ_2M) | ||
| 143 | return SZ_4K; | ||
| 144 | |||
| 145 | return SZ_2M; | ||
| 146 | } | ||
| 147 | |||
| 148 | int panfrost_mmu_map(struct panfrost_gem_object *bo) | ||
| 149 | { | ||
| 150 | struct drm_gem_object *obj = &bo->base.base; | ||
| 151 | struct panfrost_device *pfdev = to_panfrost_device(obj->dev); | ||
| 152 | struct io_pgtable_ops *ops = pfdev->mmu->pgtbl_ops; | ||
| 153 | u64 iova = bo->node.start << PAGE_SHIFT; | ||
| 154 | unsigned int count; | ||
| 155 | struct scatterlist *sgl; | ||
| 156 | struct sg_table *sgt; | ||
| 157 | int ret; | ||
| 158 | |||
| 159 | sgt = drm_gem_shmem_get_pages_sgt(obj); | ||
| 160 | if (WARN_ON(IS_ERR(sgt))) | ||
| 161 | return PTR_ERR(sgt); | ||
| 162 | |||
| 163 | ret = pm_runtime_get_sync(pfdev->dev); | ||
| 164 | if (ret < 0) | ||
| 165 | return ret; | ||
| 166 | |||
| 167 | mutex_lock(&pfdev->mmu->lock); | ||
| 168 | |||
| 169 | for_each_sg(sgt->sgl, sgl, sgt->nents, count) { | ||
| 170 | unsigned long paddr = sg_dma_address(sgl); | ||
| 171 | size_t len = sg_dma_len(sgl); | ||
| 172 | |||
| 173 | dev_dbg(pfdev->dev, "map: iova=%llx, paddr=%lx, len=%zx", iova, paddr, len); | ||
| 174 | |||
| 175 | while (len) { | ||
| 176 | size_t pgsize = get_pgsize(iova | paddr, len); | ||
| 177 | |||
| 178 | ops->map(ops, iova, paddr, pgsize, IOMMU_WRITE | IOMMU_READ); | ||
| 179 | iova += pgsize; | ||
| 180 | paddr += pgsize; | ||
| 181 | len -= pgsize; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | mmu_hw_do_operation(pfdev, 0, bo->node.start << PAGE_SHIFT, | ||
| 186 | bo->node.size << PAGE_SHIFT, AS_COMMAND_FLUSH_PT); | ||
| 187 | |||
| 188 | mutex_unlock(&pfdev->mmu->lock); | ||
| 189 | |||
| 190 | pm_runtime_mark_last_busy(pfdev->dev); | ||
| 191 | pm_runtime_put_autosuspend(pfdev->dev); | ||
| 192 | |||
| 193 | return 0; | ||
| 194 | } | ||
| 195 | |||
| 196 | void panfrost_mmu_unmap(struct panfrost_gem_object *bo) | ||
| 197 | { | ||
| 198 | struct drm_gem_object *obj = &bo->base.base; | ||
| 199 | struct panfrost_device *pfdev = to_panfrost_device(obj->dev); | ||
| 200 | struct io_pgtable_ops *ops = pfdev->mmu->pgtbl_ops; | ||
| 201 | u64 iova = bo->node.start << PAGE_SHIFT; | ||
| 202 | size_t len = bo->node.size << PAGE_SHIFT; | ||
| 203 | size_t unmapped_len = 0; | ||
| 204 | int ret; | ||
| 205 | |||
| 206 | dev_dbg(pfdev->dev, "unmap: iova=%llx, len=%zx", iova, len); | ||
| 207 | |||
| 208 | ret = pm_runtime_get_sync(pfdev->dev); | ||
| 209 | if (ret < 0) | ||
| 210 | return; | ||
| 211 | |||
| 212 | mutex_lock(&pfdev->mmu->lock); | ||
| 213 | |||
| 214 | while (unmapped_len < len) { | ||
| 215 | size_t unmapped_page; | ||
| 216 | size_t pgsize = get_pgsize(iova, len - unmapped_len); | ||
| 217 | |||
| 218 | unmapped_page = ops->unmap(ops, iova, pgsize); | ||
| 219 | if (!unmapped_page) | ||
| 220 | break; | ||
| 221 | |||
| 222 | iova += unmapped_page; | ||
| 223 | unmapped_len += unmapped_page; | ||
| 224 | } | ||
| 225 | |||
| 226 | mmu_hw_do_operation(pfdev, 0, bo->node.start << PAGE_SHIFT, | ||
| 227 | bo->node.size << PAGE_SHIFT, AS_COMMAND_FLUSH_PT); | ||
| 228 | |||
| 229 | mutex_unlock(&pfdev->mmu->lock); | ||
| 230 | |||
| 231 | pm_runtime_mark_last_busy(pfdev->dev); | ||
| 232 | pm_runtime_put_autosuspend(pfdev->dev); | ||
| 233 | } | ||
| 234 | |||
| 235 | static void mmu_tlb_inv_context_s1(void *cookie) | ||
| 236 | { | ||
| 237 | struct panfrost_device *pfdev = cookie; | ||
| 238 | |||
| 239 | mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM); | ||
| 240 | } | ||
| 241 | |||
| 242 | static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size, | ||
| 243 | size_t granule, bool leaf, void *cookie) | ||
| 244 | {} | ||
| 245 | |||
| 246 | static void mmu_tlb_sync_context(void *cookie) | ||
| 247 | { | ||
| 248 | //struct panfrost_device *pfdev = cookie; | ||
| 249 | // TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X | ||
| 250 | } | ||
| 251 | |||
| 252 | static const struct iommu_gather_ops mmu_tlb_ops = { | ||
| 253 | .tlb_flush_all = mmu_tlb_inv_context_s1, | ||
| 254 | .tlb_add_flush = mmu_tlb_inv_range_nosync, | ||
| 255 | .tlb_sync = mmu_tlb_sync_context, | ||
| 256 | }; | ||
| 257 | |||
| 258 | static const char *access_type_name(struct panfrost_device *pfdev, | ||
| 259 | u32 fault_status) | ||
| 260 | { | ||
| 261 | switch (fault_status & AS_FAULTSTATUS_ACCESS_TYPE_MASK) { | ||
| 262 | case AS_FAULTSTATUS_ACCESS_TYPE_ATOMIC: | ||
| 263 | if (panfrost_has_hw_feature(pfdev, HW_FEATURE_AARCH64_MMU)) | ||
| 264 | return "ATOMIC"; | ||
| 265 | else | ||
| 266 | return "UNKNOWN"; | ||
| 267 | case AS_FAULTSTATUS_ACCESS_TYPE_READ: | ||
| 268 | return "READ"; | ||
| 269 | case AS_FAULTSTATUS_ACCESS_TYPE_WRITE: | ||
| 270 | return "WRITE"; | ||
| 271 | case AS_FAULTSTATUS_ACCESS_TYPE_EX: | ||
| 272 | return "EXECUTE"; | ||
| 273 | default: | ||
| 274 | WARN_ON(1); | ||
| 275 | return NULL; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | static irqreturn_t panfrost_mmu_irq_handler(int irq, void *data) | ||
| 280 | { | ||
| 281 | struct panfrost_device *pfdev = data; | ||
| 282 | u32 status = mmu_read(pfdev, MMU_INT_STAT); | ||
| 283 | int i; | ||
| 284 | |||
| 285 | if (!status) | ||
| 286 | return IRQ_NONE; | ||
| 287 | |||
| 288 | dev_err(pfdev->dev, "mmu irq status=%x\n", status); | ||
| 289 | |||
| 290 | for (i = 0; status; i++) { | ||
| 291 | u32 mask = BIT(i) | BIT(i + 16); | ||
| 292 | u64 addr; | ||
| 293 | u32 fault_status; | ||
| 294 | u32 exception_type; | ||
| 295 | u32 access_type; | ||
| 296 | u32 source_id; | ||
| 297 | |||
| 298 | if (!(status & mask)) | ||
| 299 | continue; | ||
| 300 | |||
| 301 | fault_status = mmu_read(pfdev, AS_FAULTSTATUS(i)); | ||
| 302 | addr = mmu_read(pfdev, AS_FAULTADDRESS_LO(i)); | ||
| 303 | addr |= (u64)mmu_read(pfdev, AS_FAULTADDRESS_HI(i)) << 32; | ||
| 304 | |||
| 305 | /* decode the fault status */ | ||
| 306 | exception_type = fault_status & 0xFF; | ||
| 307 | access_type = (fault_status >> 8) & 0x3; | ||
| 308 | source_id = (fault_status >> 16); | ||
| 309 | |||
| 310 | /* terminal fault, print info about the fault */ | ||
| 311 | dev_err(pfdev->dev, | ||
| 312 | "Unhandled Page fault in AS%d at VA 0x%016llX\n" | ||
| 313 | "Reason: %s\n" | ||
| 314 | "raw fault status: 0x%X\n" | ||
| 315 | "decoded fault status: %s\n" | ||
| 316 | "exception type 0x%X: %s\n" | ||
| 317 | "access type 0x%X: %s\n" | ||
| 318 | "source id 0x%X\n", | ||
| 319 | i, addr, | ||
| 320 | "TODO", | ||
| 321 | fault_status, | ||
| 322 | (fault_status & (1 << 10) ? "DECODER FAULT" : "SLAVE FAULT"), | ||
| 323 | exception_type, panfrost_exception_name(pfdev, exception_type), | ||
| 324 | access_type, access_type_name(pfdev, fault_status), | ||
| 325 | source_id); | ||
| 326 | |||
| 327 | mmu_write(pfdev, MMU_INT_CLEAR, mask); | ||
| 328 | |||
| 329 | status &= ~mask; | ||
| 330 | } | ||
| 331 | |||
| 332 | return IRQ_HANDLED; | ||
| 333 | }; | ||
| 334 | |||
| 335 | int panfrost_mmu_init(struct panfrost_device *pfdev) | ||
| 336 | { | ||
| 337 | struct io_pgtable_ops *pgtbl_ops; | ||
| 338 | int err, irq; | ||
| 339 | |||
| 340 | pfdev->mmu = devm_kzalloc(pfdev->dev, sizeof(*pfdev->mmu), GFP_KERNEL); | ||
| 341 | if (!pfdev->mmu) | ||
| 342 | return -ENOMEM; | ||
| 343 | |||
| 344 | mutex_init(&pfdev->mmu->lock); | ||
| 345 | |||
| 346 | irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu"); | ||
| 347 | if (irq <= 0) | ||
| 348 | return -ENODEV; | ||
| 349 | |||
| 350 | err = devm_request_irq(pfdev->dev, irq, panfrost_mmu_irq_handler, | ||
| 351 | IRQF_SHARED, "mmu", pfdev); | ||
| 352 | |||
| 353 | if (err) { | ||
| 354 | dev_err(pfdev->dev, "failed to request mmu irq"); | ||
| 355 | return err; | ||
| 356 | } | ||
| 357 | mmu_write(pfdev, MMU_INT_CLEAR, ~0); | ||
| 358 | mmu_write(pfdev, MMU_INT_MASK, ~0); | ||
| 359 | |||
| 360 | pfdev->mmu->pgtbl_cfg = (struct io_pgtable_cfg) { | ||
| 361 | .pgsize_bitmap = SZ_4K | SZ_2M, | ||
| 362 | .ias = FIELD_GET(0xff, pfdev->features.mmu_features), | ||
| 363 | .oas = FIELD_GET(0xff00, pfdev->features.mmu_features), | ||
| 364 | .tlb = &mmu_tlb_ops, | ||
| 365 | .iommu_dev = pfdev->dev, | ||
| 366 | }; | ||
| 367 | |||
| 368 | pgtbl_ops = alloc_io_pgtable_ops(ARM_MALI_LPAE, &pfdev->mmu->pgtbl_cfg, | ||
| 369 | pfdev); | ||
| 370 | if (!pgtbl_ops) | ||
| 371 | return -ENOMEM; | ||
| 372 | |||
| 373 | pfdev->mmu->pgtbl_ops = pgtbl_ops; | ||
| 374 | |||
| 375 | panfrost_mmu_enable(pfdev, 0); | ||
| 376 | |||
| 377 | return 0; | ||
| 378 | } | ||
| 379 | |||
| 380 | void panfrost_mmu_fini(struct panfrost_device *pfdev) | ||
| 381 | { | ||
| 382 | mmu_write(pfdev, MMU_INT_MASK, 0); | ||
| 383 | mmu_disable(pfdev, 0); | ||
| 384 | |||
| 385 | free_io_pgtable_ops(pfdev->mmu->pgtbl_ops); | ||
| 386 | } | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.h b/drivers/gpu/drm/panfrost/panfrost_mmu.h new file mode 100644 index 000000000000..f5878d86a5ce --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 3 | |||
| 4 | #ifndef __PANFROST_MMU_H__ | ||
| 5 | #define __PANFROST_MMU_H__ | ||
| 6 | |||
| 7 | struct panfrost_gem_object; | ||
| 8 | |||
| 9 | int panfrost_mmu_map(struct panfrost_gem_object *bo); | ||
| 10 | void panfrost_mmu_unmap(struct panfrost_gem_object *bo); | ||
| 11 | |||
| 12 | int panfrost_mmu_init(struct panfrost_device *pfdev); | ||
| 13 | void panfrost_mmu_fini(struct panfrost_device *pfdev); | ||
| 14 | |||
| 15 | void panfrost_mmu_enable(struct panfrost_device *pfdev, u32 as_nr); | ||
| 16 | |||
| 17 | #endif | ||
diff --git a/drivers/gpu/drm/panfrost/panfrost_regs.h b/drivers/gpu/drm/panfrost/panfrost_regs.h new file mode 100644 index 000000000000..578c5fc2188b --- /dev/null +++ b/drivers/gpu/drm/panfrost/panfrost_regs.h | |||
| @@ -0,0 +1,298 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ | ||
| 3 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ | ||
| 4 | /* | ||
| 5 | * Register definitions based on mali_midg_regmap.h | ||
| 6 | * (C) COPYRIGHT 2010-2018 ARM Limited. All rights reserved. | ||
| 7 | */ | ||
| 8 | #ifndef __PANFROST_REGS_H__ | ||
| 9 | #define __PANFROST_REGS_H__ | ||
| 10 | |||
| 11 | #define GPU_ID 0x00 | ||
| 12 | #define GPU_L2_FEATURES 0x004 /* (RO) Level 2 cache features */ | ||
| 13 | #define GPU_CORE_FEATURES 0x008 /* (RO) Shader Core Features */ | ||
| 14 | #define GPU_TILER_FEATURES 0x00C /* (RO) Tiler Features */ | ||
| 15 | #define GPU_MEM_FEATURES 0x010 /* (RO) Memory system features */ | ||
| 16 | #define GROUPS_L2_COHERENT BIT(0) /* Cores groups are l2 coherent */ | ||
| 17 | |||
| 18 | #define GPU_MMU_FEATURES 0x014 /* (RO) MMU features */ | ||
| 19 | #define GPU_AS_PRESENT 0x018 /* (RO) Address space slots present */ | ||
| 20 | #define GPU_JS_PRESENT 0x01C /* (RO) Job slots present */ | ||
| 21 | |||
| 22 | #define GPU_INT_RAWSTAT 0x20 | ||
| 23 | #define GPU_INT_CLEAR 0x24 | ||
| 24 | #define GPU_INT_MASK 0x28 | ||
| 25 | #define GPU_INT_STAT 0x2c | ||
| 26 | #define GPU_IRQ_FAULT BIT(0) | ||
| 27 | #define GPU_IRQ_MULTIPLE_FAULT BIT(7) | ||
| 28 | #define GPU_IRQ_RESET_COMPLETED BIT(8) | ||
| 29 | #define GPU_IRQ_POWER_CHANGED BIT(9) | ||
| 30 | #define GPU_IRQ_POWER_CHANGED_ALL BIT(10) | ||
| 31 | #define GPU_IRQ_PERFCNT_SAMPLE_COMPLETED BIT(16) | ||
| 32 | #define GPU_IRQ_CLEAN_CACHES_COMPLETED BIT(17) | ||
| 33 | #define GPU_IRQ_MASK_ALL \ | ||
| 34 | (GPU_IRQ_FAULT |\ | ||
| 35 | GPU_IRQ_MULTIPLE_FAULT |\ | ||
| 36 | GPU_IRQ_RESET_COMPLETED |\ | ||
| 37 | GPU_IRQ_POWER_CHANGED |\ | ||
| 38 | GPU_IRQ_POWER_CHANGED_ALL |\ | ||
| 39 | GPU_IRQ_PERFCNT_SAMPLE_COMPLETED |\ | ||
| 40 | GPU_IRQ_CLEAN_CACHES_COMPLETED) | ||
| 41 | #define GPU_IRQ_MASK_ERROR \ | ||
| 42 | ( \ | ||
| 43 | GPU_IRQ_FAULT |\ | ||
| 44 | GPU_IRQ_MULTIPLE_FAULT) | ||
| 45 | #define GPU_CMD 0x30 | ||
| 46 | #define GPU_CMD_SOFT_RESET 0x01 | ||
| 47 | #define GPU_STATUS 0x34 | ||
| 48 | #define GPU_LATEST_FLUSH_ID 0x38 | ||
| 49 | #define GPU_FAULT_STATUS 0x3C | ||
| 50 | #define GPU_FAULT_ADDRESS_LO 0x40 | ||
| 51 | #define GPU_FAULT_ADDRESS_HI 0x44 | ||
| 52 | |||
| 53 | #define GPU_THREAD_MAX_THREADS 0x0A0 /* (RO) Maximum number of threads per core */ | ||
| 54 | #define GPU_THREAD_MAX_WORKGROUP_SIZE 0x0A4 /* (RO) Maximum workgroup size */ | ||
| 55 | #define GPU_THREAD_MAX_BARRIER_SIZE 0x0A8 /* (RO) Maximum threads waiting at a barrier */ | ||
| 56 | #define GPU_THREAD_FEATURES 0x0AC /* (RO) Thread features */ | ||
| 57 | #define GPU_THREAD_TLS_ALLOC 0x310 /* (RO) Number of threads per core that | ||
| 58 | * TLS must be allocated for */ | ||
| 59 | |||
| 60 | #define GPU_TEXTURE_FEATURES(n) (0x0B0 + ((n) * 4)) | ||
| 61 | #define GPU_JS_FEATURES(n) (0x0C0 + ((n) * 4)) | ||
| 62 | |||
| 63 | #define GPU_SHADER_PRESENT_LO 0x100 /* (RO) Shader core present bitmap, low word */ | ||
| 64 | #define GPU_SHADER_PRESENT_HI 0x104 /* (RO) Shader core present bitmap, high word */ | ||
| 65 | #define GPU_TILER_PRESENT_LO 0x110 /* (RO) Tiler core present bitmap, low word */ | ||
| 66 | #define GPU_TILER_PRESENT_HI 0x114 /* (RO) Tiler core present bitmap, high word */ | ||
| 67 | |||
| 68 | #define GPU_L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */ | ||
| 69 | #define GPU_L2_PRESENT_HI 0x124 /* (RO) Level 2 cache present bitmap, high word */ | ||
| 70 | |||
| 71 | #define GPU_COHERENCY_FEATURES 0x300 /* (RO) Coherency features present */ | ||
| 72 | #define COHERENCY_ACE_LITE BIT(0) | ||
| 73 | #define COHERENCY_ACE BIT(1) | ||
| 74 | |||
| 75 | #define GPU_STACK_PRESENT_LO 0xE00 /* (RO) Core stack present bitmap, low word */ | ||
| 76 | #define GPU_STACK_PRESENT_HI 0xE04 /* (RO) Core stack present bitmap, high word */ | ||
| 77 | |||
| 78 | #define SHADER_READY_LO 0x140 /* (RO) Shader core ready bitmap, low word */ | ||
| 79 | #define SHADER_READY_HI 0x144 /* (RO) Shader core ready bitmap, high word */ | ||
| 80 | |||
| 81 | #define TILER_READY_LO 0x150 /* (RO) Tiler core ready bitmap, low word */ | ||
| 82 | #define TILER_READY_HI 0x154 /* (RO) Tiler core ready bitmap, high word */ | ||
| 83 | |||
| 84 | #define L2_READY_LO 0x160 /* (RO) Level 2 cache ready bitmap, low word */ | ||
| 85 | #define L2_READY_HI 0x164 /* (RO) Level 2 cache ready bitmap, high word */ | ||
| 86 | |||
| 87 | #define STACK_READY_LO 0xE10 /* (RO) Core stack ready bitmap, low word */ | ||
| 88 | #define STACK_READY_HI 0xE14 /* (RO) Core stack ready bitmap, high word */ | ||
| 89 | |||
| 90 | |||
| 91 | #define SHADER_PWRON_LO 0x180 /* (WO) Shader core power on bitmap, low word */ | ||
| 92 | #define SHADER_PWRON_HI 0x184 /* (WO) Shader core power on bitmap, high word */ | ||
| 93 | |||
| 94 | #define TILER_PWRON_LO 0x190 /* (WO) Tiler core power on bitmap, low word */ | ||
| 95 | #define TILER_PWRON_HI 0x194 /* (WO) Tiler core power on bitmap, high word */ | ||
| 96 | |||
| 97 | #define L2_PWRON_LO 0x1A0 /* (WO) Level 2 cache power on bitmap, low word */ | ||
| 98 | #define L2_PWRON_HI 0x1A4 /* (WO) Level 2 cache power on bitmap, high word */ | ||
| 99 | |||
| 100 | #define STACK_PWRON_LO 0xE20 /* (RO) Core stack power on bitmap, low word */ | ||
| 101 | #define STACK_PWRON_HI 0xE24 /* (RO) Core stack power on bitmap, high word */ | ||
| 102 | |||
| 103 | |||
| 104 | #define SHADER_PWROFF_LO 0x1C0 /* (WO) Shader core power off bitmap, low word */ | ||
| 105 | #define SHADER_PWROFF_HI 0x1C4 /* (WO) Shader core power off bitmap, high word */ | ||
| 106 | |||
| 107 | #define TILER_PWROFF_LO 0x1D0 /* (WO) Tiler core power off bitmap, low word */ | ||
| 108 | #define TILER_PWROFF_HI 0x1D4 /* (WO) Tiler core power off bitmap, high word */ | ||
| 109 | |||
| 110 | #define L2_PWROFF_LO 0x1E0 /* (WO) Level 2 cache power off bitmap, low word */ | ||
| 111 | #define L2_PWROFF_HI 0x1E4 /* (WO) Level 2 cache power off bitmap, high word */ | ||
| 112 | |||
| 113 | #define STACK_PWROFF_LO 0xE30 /* (RO) Core stack power off bitmap, low word */ | ||
| 114 | #define STACK_PWROFF_HI 0xE34 /* (RO) Core stack power off bitmap, high word */ | ||
| 115 | |||
| 116 | |||
| 117 | #define SHADER_PWRTRANS_LO 0x200 /* (RO) Shader core power transition bitmap, low word */ | ||
| 118 | #define SHADER_PWRTRANS_HI 0x204 /* (RO) Shader core power transition bitmap, high word */ | ||
| 119 | |||
| 120 | #define TILER_PWRTRANS_LO 0x210 /* (RO) Tiler core power transition bitmap, low word */ | ||
| 121 | #define TILER_PWRTRANS_HI 0x214 /* (RO) Tiler core power transition bitmap, high word */ | ||
| 122 | |||
| 123 | #define L2_PWRTRANS_LO 0x220 /* (RO) Level 2 cache power transition bitmap, low word */ | ||
| 124 | #define L2_PWRTRANS_HI 0x224 /* (RO) Level 2 cache power transition bitmap, high word */ | ||
| 125 | |||
| 126 | #define STACK_PWRTRANS_LO 0xE40 /* (RO) Core stack power transition bitmap, low word */ | ||
| 127 | #define STACK_PWRTRANS_HI 0xE44 /* (RO) Core stack power transition bitmap, high word */ | ||
| 128 | |||
| 129 | |||
| 130 | #define SHADER_PWRACTIVE_LO 0x240 /* (RO) Shader core active bitmap, low word */ | ||
| 131 | #define SHADER_PWRACTIVE_HI 0x244 /* (RO) Shader core active bitmap, high word */ | ||
| 132 | |||
| 133 | #define TILER_PWRACTIVE_LO 0x250 /* (RO) Tiler core active bitmap, low word */ | ||
| 134 | #define TILER_PWRACTIVE_HI 0x254 /* (RO) Tiler core active bitmap, high word */ | ||
| 135 | |||
| 136 | #define L2_PWRACTIVE_LO 0x260 /* (RO) Level 2 cache active bitmap, low word */ | ||
| 137 | #define L2_PWRACTIVE_HI 0x264 /* (RO) Level 2 cache active bitmap, high word */ | ||
| 138 | |||
| 139 | #define GPU_JM_CONFIG 0xF00 /* (RW) Job Manager configuration register (Implementation specific register) */ | ||
| 140 | #define GPU_SHADER_CONFIG 0xF04 /* (RW) Shader core configuration settings (Implementation specific register) */ | ||
| 141 | #define GPU_TILER_CONFIG 0xF08 /* (RW) Tiler core configuration settings (Implementation specific register) */ | ||
| 142 | #define GPU_L2_MMU_CONFIG 0xF0C /* (RW) Configuration of the L2 cache and MMU (Implementation specific register) */ | ||
| 143 | |||
| 144 | /* L2_MMU_CONFIG register */ | ||
| 145 | #define L2_MMU_CONFIG_ALLOW_SNOOP_DISPARITY_SHIFT 23 | ||
| 146 | #define L2_MMU_CONFIG_ALLOW_SNOOP_DISPARITY (0x1 << L2_MMU_CONFIG_ALLOW_SNOOP_DISPARITY_SHIFT) | ||
| 147 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_SHIFT 24 | ||
| 148 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_READS (0x3 << L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_SHIFT) | ||
| 149 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_OCTANT (0x1 << L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_SHIFT) | ||
| 150 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_QUARTER (0x2 << L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_SHIFT) | ||
| 151 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_HALF (0x3 << L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_SHIFT) | ||
| 152 | |||
| 153 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_SHIFT 26 | ||
| 154 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES (0x3 << L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_SHIFT) | ||
| 155 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_OCTANT (0x1 << L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_SHIFT) | ||
| 156 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_QUARTER (0x2 << L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_SHIFT) | ||
| 157 | #define L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_HALF (0x3 << L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_SHIFT) | ||
| 158 | |||
| 159 | #define L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_READS_SHIFT 12 | ||
| 160 | #define L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_READS (0x7 << L2_MMU_CONFIG_LIMIT_EXTERNAL_READS_SHIFT) | ||
| 161 | |||
| 162 | #define L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_WRITES_SHIFT 15 | ||
| 163 | #define L2_MMU_CONFIG_3BIT_LIMIT_EXTERNAL_WRITES (0x7 << L2_MMU_CONFIG_LIMIT_EXTERNAL_WRITES_SHIFT) | ||
| 164 | |||
| 165 | /* SHADER_CONFIG register */ | ||
| 166 | #define SC_ALT_COUNTERS BIT(3) | ||
| 167 | #define SC_OVERRIDE_FWD_PIXEL_KILL BIT(4) | ||
| 168 | #define SC_SDC_DISABLE_OQ_DISCARD BIT(6) | ||
| 169 | #define SC_LS_ALLOW_ATTR_TYPES BIT(16) | ||
| 170 | #define SC_LS_PAUSEBUFFER_DISABLE BIT(16) | ||
| 171 | #define SC_TLS_HASH_ENABLE BIT(17) | ||
| 172 | #define SC_LS_ATTR_CHECK_DISABLE BIT(18) | ||
| 173 | #define SC_ENABLE_TEXGRD_FLAGS BIT(25) | ||
| 174 | /* End SHADER_CONFIG register */ | ||
| 175 | |||
| 176 | /* TILER_CONFIG register */ | ||
| 177 | #define TC_CLOCK_GATE_OVERRIDE BIT(0) | ||
| 178 | |||
| 179 | /* JM_CONFIG register */ | ||
| 180 | #define JM_TIMESTAMP_OVERRIDE BIT(0) | ||
| 181 | #define JM_CLOCK_GATE_OVERRIDE BIT(1) | ||
| 182 | #define JM_JOB_THROTTLE_ENABLE BIT(2) | ||
| 183 | #define JM_JOB_THROTTLE_LIMIT_SHIFT 3 | ||
| 184 | #define JM_MAX_JOB_THROTTLE_LIMIT 0x3F | ||
| 185 | #define JM_FORCE_COHERENCY_FEATURES_SHIFT 2 | ||
| 186 | #define JM_IDVS_GROUP_SIZE_SHIFT 16 | ||
| 187 | #define JM_MAX_IDVS_GROUP_SIZE 0x3F | ||
| 188 | |||
| 189 | |||
| 190 | /* Job Control regs */ | ||
| 191 | #define JOB_INT_RAWSTAT 0x1000 | ||
| 192 | #define JOB_INT_CLEAR 0x1004 | ||
| 193 | #define JOB_INT_MASK 0x1008 | ||
| 194 | #define JOB_INT_STAT 0x100c | ||
| 195 | #define JOB_INT_JS_STATE 0x1010 | ||
| 196 | #define JOB_INT_THROTTLE 0x1014 | ||
| 197 | |||
| 198 | #define MK_JS_MASK(j) (0x10001 << (j)) | ||
| 199 | #define JOB_INT_MASK_ERR(j) BIT((j) + 16) | ||
| 200 | #define JOB_INT_MASK_DONE(j) BIT(j) | ||
| 201 | |||
| 202 | #define JS_BASE 0x1800 | ||
| 203 | #define JS_HEAD_LO(n) (JS_BASE + ((n) * 0x80) + 0x00) | ||
| 204 | #define JS_HEAD_HI(n) (JS_BASE + ((n) * 0x80) + 0x04) | ||
| 205 | #define JS_TAIL_LO(n) (JS_BASE + ((n) * 0x80) + 0x08) | ||
| 206 | #define JS_TAIL_HI(n) (JS_BASE + ((n) * 0x80) + 0x0c) | ||
| 207 | #define JS_AFFINITY_LO(n) (JS_BASE + ((n) * 0x80) + 0x10) | ||
| 208 | #define JS_AFFINITY_HI(n) (JS_BASE + ((n) * 0x80) + 0x14) | ||
| 209 | #define JS_CONFIG(n) (JS_BASE + ((n) * 0x80) + 0x18) | ||
| 210 | #define JS_XAFFINITY(n) (JS_BASE + ((n) * 0x80) + 0x1c) | ||
| 211 | #define JS_COMMAND(n) (JS_BASE + ((n) * 0x80) + 0x20) | ||
| 212 | #define JS_STATUS(n) (JS_BASE + ((n) * 0x80) + 0x24) | ||
| 213 | #define JS_HEAD_NEXT_LO(n) (JS_BASE + ((n) * 0x80) + 0x40) | ||
| 214 | #define JS_HEAD_NEXT_HI(n) (JS_BASE + ((n) * 0x80) + 0x44) | ||
| 215 | #define JS_AFFINITY_NEXT_LO(n) (JS_BASE + ((n) * 0x80) + 0x50) | ||
| 216 | #define JS_AFFINITY_NEXT_HI(n) (JS_BASE + ((n) * 0x80) + 0x54) | ||
| 217 | #define JS_CONFIG_NEXT(n) (JS_BASE + ((n) * 0x80) + 0x58) | ||
| 218 | #define JS_COMMAND_NEXT(n) (JS_BASE + ((n) * 0x80) + 0x60) | ||
| 219 | #define JS_FLUSH_ID_NEXT(n) (JS_BASE + ((n) * 0x80) + 0x70) | ||
| 220 | |||
| 221 | /* Possible values of JS_CONFIG and JS_CONFIG_NEXT registers */ | ||
| 222 | #define JS_CONFIG_START_FLUSH_CLEAN BIT(8) | ||
| 223 | #define JS_CONFIG_START_FLUSH_CLEAN_INVALIDATE (3u << 8) | ||
| 224 | #define JS_CONFIG_START_MMU BIT(10) | ||
| 225 | #define JS_CONFIG_JOB_CHAIN_FLAG BIT(11) | ||
| 226 | #define JS_CONFIG_END_FLUSH_CLEAN BIT(12) | ||
| 227 | #define JS_CONFIG_END_FLUSH_CLEAN_INVALIDATE (3u << 12) | ||
| 228 | #define JS_CONFIG_ENABLE_FLUSH_REDUCTION BIT(14) | ||
| 229 | #define JS_CONFIG_DISABLE_DESCRIPTOR_WR_BK BIT(15) | ||
| 230 | #define JS_CONFIG_THREAD_PRI(n) ((n) << 16) | ||
| 231 | |||
| 232 | #define JS_COMMAND_NOP 0x00 | ||
| 233 | #define JS_COMMAND_START 0x01 | ||
| 234 | #define JS_COMMAND_SOFT_STOP 0x02 /* Gently stop processing a job chain */ | ||
| 235 | #define JS_COMMAND_HARD_STOP 0x03 /* Rudely stop processing a job chain */ | ||
| 236 | #define JS_COMMAND_SOFT_STOP_0 0x04 /* Execute SOFT_STOP if JOB_CHAIN_FLAG is 0 */ | ||
| 237 | #define JS_COMMAND_HARD_STOP_0 0x05 /* Execute HARD_STOP if JOB_CHAIN_FLAG is 0 */ | ||
| 238 | #define JS_COMMAND_SOFT_STOP_1 0x06 /* Execute SOFT_STOP if JOB_CHAIN_FLAG is 1 */ | ||
| 239 | #define JS_COMMAND_HARD_STOP_1 0x07 /* Execute HARD_STOP if JOB_CHAIN_FLAG is 1 */ | ||
| 240 | |||
| 241 | #define JS_STATUS_EVENT_ACTIVE 0x08 | ||
| 242 | |||
| 243 | |||
| 244 | /* MMU regs */ | ||
| 245 | #define MMU_INT_RAWSTAT 0x2000 | ||
| 246 | #define MMU_INT_CLEAR 0x2004 | ||
| 247 | #define MMU_INT_MASK 0x2008 | ||
| 248 | #define MMU_INT_STAT 0x200c | ||
| 249 | |||
| 250 | /* AS_COMMAND register commands */ | ||
| 251 | #define AS_COMMAND_NOP 0x00 /* NOP Operation */ | ||
| 252 | #define AS_COMMAND_UPDATE 0x01 /* Broadcasts the values in AS_TRANSTAB and ASn_MEMATTR to all MMUs */ | ||
| 253 | #define AS_COMMAND_LOCK 0x02 /* Issue a lock region command to all MMUs */ | ||
| 254 | #define AS_COMMAND_UNLOCK 0x03 /* Issue a flush region command to all MMUs */ | ||
| 255 | #define AS_COMMAND_FLUSH 0x04 /* Flush all L2 caches then issue a flush region command to all MMUs | ||
| 256 | (deprecated - only for use with T60x) */ | ||
| 257 | #define AS_COMMAND_FLUSH_PT 0x04 /* Flush all L2 caches then issue a flush region command to all MMUs */ | ||
| 258 | #define AS_COMMAND_FLUSH_MEM 0x05 /* Wait for memory accesses to complete, flush all the L1s cache then | ||
| 259 | flush all L2 caches then issue a flush region command to all MMUs */ | ||
| 260 | |||
| 261 | #define MMU_AS(as) (0x2400 + ((as) << 6)) | ||
| 262 | |||
| 263 | #define AS_TRANSTAB_LO(as) (MMU_AS(as) + 0x00) /* (RW) Translation Table Base Address for address space n, low word */ | ||
| 264 | #define AS_TRANSTAB_HI(as) (MMU_AS(as) + 0x04) /* (RW) Translation Table Base Address for address space n, high word */ | ||
| 265 | #define AS_MEMATTR_LO(as) (MMU_AS(as) + 0x08) /* (RW) Memory attributes for address space n, low word. */ | ||
| 266 | #define AS_MEMATTR_HI(as) (MMU_AS(as) + 0x0C) /* (RW) Memory attributes for address space n, high word. */ | ||
| 267 | #define AS_LOCKADDR_LO(as) (MMU_AS(as) + 0x10) /* (RW) Lock region address for address space n, low word */ | ||
| 268 | #define AS_LOCKADDR_HI(as) (MMU_AS(as) + 0x14) /* (RW) Lock region address for address space n, high word */ | ||
| 269 | #define AS_COMMAND(as) (MMU_AS(as) + 0x18) /* (WO) MMU command register for address space n */ | ||
| 270 | #define AS_FAULTSTATUS(as) (MMU_AS(as) + 0x1C) /* (RO) MMU fault status register for address space n */ | ||
| 271 | #define AS_FAULTADDRESS_LO(as) (MMU_AS(as) + 0x20) /* (RO) Fault Address for address space n, low word */ | ||
| 272 | #define AS_FAULTADDRESS_HI(as) (MMU_AS(as) + 0x24) /* (RO) Fault Address for address space n, high word */ | ||
| 273 | #define AS_STATUS(as) (MMU_AS(as) + 0x28) /* (RO) Status flags for address space n */ | ||
| 274 | /* Additional Bifrost AS regsiters */ | ||
| 275 | #define AS_TRANSCFG_LO(as) (MMU_AS(as) + 0x30) /* (RW) Translation table configuration for address space n, low word */ | ||
| 276 | #define AS_TRANSCFG_HI(as) (MMU_AS(as) + 0x34) /* (RW) Translation table configuration for address space n, high word */ | ||
| 277 | #define AS_FAULTEXTRA_LO(as) (MMU_AS(as) + 0x38) /* (RO) Secondary fault address for address space n, low word */ | ||
| 278 | #define AS_FAULTEXTRA_HI(as) (MMU_AS(as) + 0x3C) /* (RO) Secondary fault address for address space n, high word */ | ||
| 279 | |||
| 280 | /* | ||
| 281 | * Begin LPAE MMU TRANSTAB register values | ||
| 282 | */ | ||
| 283 | #define AS_TRANSTAB_LPAE_ADDR_SPACE_MASK 0xfffffffffffff000 | ||
| 284 | #define AS_TRANSTAB_LPAE_ADRMODE_IDENTITY 0x2 | ||
| 285 | #define AS_TRANSTAB_LPAE_ADRMODE_TABLE 0x3 | ||
| 286 | #define AS_TRANSTAB_LPAE_ADRMODE_MASK 0x3 | ||
| 287 | #define AS_TRANSTAB_LPAE_READ_INNER BIT(2) | ||
| 288 | #define AS_TRANSTAB_LPAE_SHARE_OUTER BIT(4) | ||
| 289 | |||
| 290 | #define AS_STATUS_AS_ACTIVE 0x01 | ||
| 291 | |||
| 292 | #define AS_FAULTSTATUS_ACCESS_TYPE_MASK (0x3 << 8) | ||
| 293 | #define AS_FAULTSTATUS_ACCESS_TYPE_ATOMIC (0x0 << 8) | ||
| 294 | #define AS_FAULTSTATUS_ACCESS_TYPE_EX (0x1 << 8) | ||
| 295 | #define AS_FAULTSTATUS_ACCESS_TYPE_READ (0x2 << 8) | ||
| 296 | #define AS_FAULTSTATUS_ACCESS_TYPE_WRITE (0x3 << 8) | ||
| 297 | |||
| 298 | #endif | ||
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index ee59da4a0172..4e5922c89d7b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c | |||
| @@ -361,13 +361,6 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | |||
| 361 | paddr = drm_fb_cma_get_gem_addr(fb, state, 0); | 361 | paddr = drm_fb_cma_get_gem_addr(fb, state, 0); |
| 362 | DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); | 362 | DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); |
| 363 | 363 | ||
| 364 | /* | ||
| 365 | * backend DMA accesses DRAM directly, bypassing the system | ||
| 366 | * bus. As such, the address range is different and the buffer | ||
| 367 | * address needs to be corrected. | ||
| 368 | */ | ||
| 369 | paddr -= PHYS_OFFSET; | ||
| 370 | |||
| 371 | if (fb->format->is_yuv) | 364 | if (fb->format->is_yuv) |
| 372 | return sun4i_backend_update_yuv_buffer(backend, fb, paddr); | 365 | return sun4i_backend_update_yuv_buffer(backend, fb, paddr); |
| 373 | 366 | ||
| @@ -803,6 +796,27 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, | |||
| 803 | dev_set_drvdata(dev, backend); | 796 | dev_set_drvdata(dev, backend); |
| 804 | spin_lock_init(&backend->frontend_lock); | 797 | spin_lock_init(&backend->frontend_lock); |
| 805 | 798 | ||
| 799 | if (of_find_property(dev->of_node, "interconnects", NULL)) { | ||
| 800 | /* | ||
| 801 | * This assume we have the same DMA constraints for all our the | ||
| 802 | * devices in our pipeline (all the backends, but also the | ||
| 803 | * frontends). This sounds bad, but it has always been the case | ||
| 804 | * for us, and DRM doesn't do per-device allocation either, so | ||
| 805 | * we would need to fix DRM first... | ||
| 806 | */ | ||
| 807 | ret = of_dma_configure(drm->dev, dev->of_node, true); | ||
| 808 | if (ret) | ||
| 809 | return ret; | ||
| 810 | } else { | ||
| 811 | /* | ||
| 812 | * If we don't have the interconnect property, most likely | ||
| 813 | * because of an old DT, we need to set the DMA offset by hand | ||
| 814 | * on our device since the RAM mapping is at 0 for the DMA bus, | ||
| 815 | * unlike the CPU. | ||
| 816 | */ | ||
| 817 | drm->dev->dma_pfn_offset = PHYS_PFN_OFFSET; | ||
| 818 | } | ||
| 819 | |||
| 806 | backend->engine.node = dev->of_node; | 820 | backend->engine.node = dev->of_node; |
| 807 | backend->engine.ops = &sun4i_backend_engine_ops; | 821 | backend->engine.ops = &sun4i_backend_engine_ops; |
| 808 | backend->engine.id = sun4i_backend_of_get_id(dev->of_node); | 822 | backend->engine.id = sun4i_backend_of_get_id(dev->of_node); |
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index fa92e992a282..9d8d8124b1f6 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c | |||
| @@ -236,8 +236,8 @@ static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm) | |||
| 236 | return NULL; | 236 | return NULL; |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, | 239 | static void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, |
| 240 | const struct drm_encoder *encoder) | 240 | const struct drm_encoder *encoder) |
| 241 | { | 241 | { |
| 242 | int ret = -ENOTSUPP; | 242 | int ret = -ENOTSUPP; |
| 243 | 243 | ||
diff --git a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c index b1e7c76e9c17..3267d0f9b9b2 100644 --- a/drivers/gpu/drm/sun4i/sun8i_tcon_top.c +++ b/drivers/gpu/drm/sun4i/sun8i_tcon_top.c | |||
| @@ -269,12 +269,12 @@ static int sun8i_tcon_top_remove(struct platform_device *pdev) | |||
| 269 | return 0; | 269 | return 0; |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | const struct sun8i_tcon_top_quirks sun8i_r40_tcon_top_quirks = { | 272 | static const struct sun8i_tcon_top_quirks sun8i_r40_tcon_top_quirks = { |
| 273 | .has_tcon_tv1 = true, | 273 | .has_tcon_tv1 = true, |
| 274 | .has_dsi = true, | 274 | .has_dsi = true, |
| 275 | }; | 275 | }; |
| 276 | 276 | ||
| 277 | const struct sun8i_tcon_top_quirks sun50i_h6_tcon_top_quirks = { | 277 | static const struct sun8i_tcon_top_quirks sun50i_h6_tcon_top_quirks = { |
| 278 | /* Nothing special */ | 278 | /* Nothing special */ |
| 279 | }; | 279 | }; |
| 280 | 280 | ||
diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c index fab961dded87..5773d0fb6ca1 100644 --- a/drivers/gpu/drm/tinydrm/hx8357d.c +++ b/drivers/gpu/drm/tinydrm/hx8357d.c | |||
| @@ -267,7 +267,7 @@ static int hx8357d_probe(struct spi_device *spi) | |||
| 267 | 267 | ||
| 268 | spi_set_drvdata(spi, drm); | 268 | spi_set_drvdata(spi, drm); |
| 269 | 269 | ||
| 270 | drm_fbdev_generic_setup(drm, 32); | 270 | drm_fbdev_generic_setup(drm, 0); |
| 271 | 271 | ||
| 272 | return 0; | 272 | return 0; |
| 273 | } | 273 | } |
diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c index e9116ef4b5bc..4b1a587c0134 100644 --- a/drivers/gpu/drm/tinydrm/ili9225.c +++ b/drivers/gpu/drm/tinydrm/ili9225.c | |||
| @@ -433,7 +433,7 @@ static int ili9225_probe(struct spi_device *spi) | |||
| 433 | 433 | ||
| 434 | spi_set_drvdata(spi, drm); | 434 | spi_set_drvdata(spi, drm); |
| 435 | 435 | ||
| 436 | drm_fbdev_generic_setup(drm, 32); | 436 | drm_fbdev_generic_setup(drm, 0); |
| 437 | 437 | ||
| 438 | return 0; | 438 | return 0; |
| 439 | } | 439 | } |
diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c index d15f85e837ae..4ade9e4b924f 100644 --- a/drivers/gpu/drm/tinydrm/ili9341.c +++ b/drivers/gpu/drm/tinydrm/ili9341.c | |||
| @@ -229,7 +229,7 @@ static int ili9341_probe(struct spi_device *spi) | |||
| 229 | 229 | ||
| 230 | spi_set_drvdata(spi, drm); | 230 | spi_set_drvdata(spi, drm); |
| 231 | 231 | ||
| 232 | drm_fbdev_generic_setup(drm, 32); | 232 | drm_fbdev_generic_setup(drm, 0); |
| 233 | 233 | ||
| 234 | return 0; | 234 | return 0; |
| 235 | } | 235 | } |
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c index c6dc31084a4e..8e169846fbd8 100644 --- a/drivers/gpu/drm/tinydrm/mi0283qt.c +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c | |||
| @@ -242,7 +242,7 @@ static int mi0283qt_probe(struct spi_device *spi) | |||
| 242 | 242 | ||
| 243 | spi_set_drvdata(spi, drm); | 243 | spi_set_drvdata(spi, drm); |
| 244 | 244 | ||
| 245 | drm_fbdev_generic_setup(drm, 32); | 245 | drm_fbdev_generic_setup(drm, 0); |
| 246 | 246 | ||
| 247 | return 0; | 247 | return 0; |
| 248 | } | 248 | } |
diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c index a29b8278324b..370629e2de94 100644 --- a/drivers/gpu/drm/tinydrm/repaper.c +++ b/drivers/gpu/drm/tinydrm/repaper.c | |||
| @@ -1131,7 +1131,7 @@ static int repaper_probe(struct spi_device *spi) | |||
| 1131 | 1131 | ||
| 1132 | DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000); | 1132 | DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000); |
| 1133 | 1133 | ||
| 1134 | drm_fbdev_generic_setup(drm, 32); | 1134 | drm_fbdev_generic_setup(drm, 0); |
| 1135 | 1135 | ||
| 1136 | return 0; | 1136 | return 0; |
| 1137 | } | 1137 | } |
diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c index 560d7ac0cadc..36bb16a15f7e 100644 --- a/drivers/gpu/drm/tinydrm/st7586.c +++ b/drivers/gpu/drm/tinydrm/st7586.c | |||
| @@ -408,7 +408,7 @@ static int st7586_probe(struct spi_device *spi) | |||
| 408 | DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n", | 408 | DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n", |
| 409 | drm->mode_config.preferred_depth, rotation); | 409 | drm->mode_config.preferred_depth, rotation); |
| 410 | 410 | ||
| 411 | drm_fbdev_generic_setup(drm, 32); | 411 | drm_fbdev_generic_setup(drm, 0); |
| 412 | 412 | ||
| 413 | return 0; | 413 | return 0; |
| 414 | } | 414 | } |
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c index 022e9849b95b..ce9109e613e0 100644 --- a/drivers/gpu/drm/tinydrm/st7735r.c +++ b/drivers/gpu/drm/tinydrm/st7735r.c | |||
| @@ -207,7 +207,7 @@ static int st7735r_probe(struct spi_device *spi) | |||
| 207 | 207 | ||
| 208 | spi_set_drvdata(spi, drm); | 208 | spi_set_drvdata(spi, drm); |
| 209 | 209 | ||
| 210 | drm_fbdev_generic_setup(drm, 32); | 210 | drm_fbdev_generic_setup(drm, 0); |
| 211 | 211 | ||
| 212 | return 0; | 212 | return 0; |
| 213 | } | 213 | } |
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index d3700ec15cbd..4e21efbc4459 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c | |||
| @@ -172,6 +172,10 @@ | |||
| 172 | #define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 | 172 | #define ARM_LPAE_MAIR_ATTR_IDX_CACHE 1 |
| 173 | #define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 | 173 | #define ARM_LPAE_MAIR_ATTR_IDX_DEV 2 |
| 174 | 174 | ||
| 175 | #define ARM_MALI_LPAE_TTBR_ADRMODE_TABLE (3u << 0) | ||
| 176 | #define ARM_MALI_LPAE_TTBR_READ_INNER BIT(2) | ||
| 177 | #define ARM_MALI_LPAE_TTBR_SHARE_OUTER BIT(4) | ||
| 178 | |||
| 175 | /* IOPTE accessors */ | 179 | /* IOPTE accessors */ |
| 176 | #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d)) | 180 | #define iopte_deref(pte,d) __va(iopte_to_paddr(pte, d)) |
| 177 | 181 | ||
| @@ -180,11 +184,6 @@ | |||
| 180 | 184 | ||
| 181 | #define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK) | 185 | #define iopte_prot(pte) ((pte) & ARM_LPAE_PTE_ATTR_MASK) |
| 182 | 186 | ||
| 183 | #define iopte_leaf(pte,l) \ | ||
| 184 | (l == (ARM_LPAE_MAX_LEVELS - 1) ? \ | ||
| 185 | (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_PAGE) : \ | ||
| 186 | (iopte_type(pte,l) == ARM_LPAE_PTE_TYPE_BLOCK)) | ||
| 187 | |||
| 188 | struct arm_lpae_io_pgtable { | 187 | struct arm_lpae_io_pgtable { |
| 189 | struct io_pgtable iop; | 188 | struct io_pgtable iop; |
| 190 | 189 | ||
| @@ -198,6 +197,15 @@ struct arm_lpae_io_pgtable { | |||
| 198 | 197 | ||
| 199 | typedef u64 arm_lpae_iopte; | 198 | typedef u64 arm_lpae_iopte; |
| 200 | 199 | ||
| 200 | static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl, | ||
| 201 | enum io_pgtable_fmt fmt) | ||
| 202 | { | ||
| 203 | if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE) | ||
| 204 | return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_PAGE; | ||
| 205 | |||
| 206 | return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_BLOCK; | ||
| 207 | } | ||
| 208 | |||
| 201 | static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr, | 209 | static arm_lpae_iopte paddr_to_iopte(phys_addr_t paddr, |
| 202 | struct arm_lpae_io_pgtable *data) | 210 | struct arm_lpae_io_pgtable *data) |
| 203 | { | 211 | { |
| @@ -303,12 +311,14 @@ static void __arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, | |||
| 303 | if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) | 311 | if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) |
| 304 | pte |= ARM_LPAE_PTE_NS; | 312 | pte |= ARM_LPAE_PTE_NS; |
| 305 | 313 | ||
| 306 | if (lvl == ARM_LPAE_MAX_LEVELS - 1) | 314 | if (data->iop.fmt != ARM_MALI_LPAE && lvl == ARM_LPAE_MAX_LEVELS - 1) |
| 307 | pte |= ARM_LPAE_PTE_TYPE_PAGE; | 315 | pte |= ARM_LPAE_PTE_TYPE_PAGE; |
| 308 | else | 316 | else |
| 309 | pte |= ARM_LPAE_PTE_TYPE_BLOCK; | 317 | pte |= ARM_LPAE_PTE_TYPE_BLOCK; |
| 310 | 318 | ||
| 311 | pte |= ARM_LPAE_PTE_AF | ARM_LPAE_PTE_SH_IS; | 319 | if (data->iop.fmt != ARM_MALI_LPAE) |
| 320 | pte |= ARM_LPAE_PTE_AF; | ||
| 321 | pte |= ARM_LPAE_PTE_SH_IS; | ||
| 312 | pte |= paddr_to_iopte(paddr, data); | 322 | pte |= paddr_to_iopte(paddr, data); |
| 313 | 323 | ||
| 314 | __arm_lpae_set_pte(ptep, pte, &data->iop.cfg); | 324 | __arm_lpae_set_pte(ptep, pte, &data->iop.cfg); |
| @@ -321,7 +331,7 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, | |||
| 321 | { | 331 | { |
| 322 | arm_lpae_iopte pte = *ptep; | 332 | arm_lpae_iopte pte = *ptep; |
| 323 | 333 | ||
| 324 | if (iopte_leaf(pte, lvl)) { | 334 | if (iopte_leaf(pte, lvl, data->iop.fmt)) { |
| 325 | /* We require an unmap first */ | 335 | /* We require an unmap first */ |
| 326 | WARN_ON(!selftest_running); | 336 | WARN_ON(!selftest_running); |
| 327 | return -EEXIST; | 337 | return -EEXIST; |
| @@ -409,7 +419,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova, | |||
| 409 | __arm_lpae_sync_pte(ptep, cfg); | 419 | __arm_lpae_sync_pte(ptep, cfg); |
| 410 | } | 420 | } |
| 411 | 421 | ||
| 412 | if (pte && !iopte_leaf(pte, lvl)) { | 422 | if (pte && !iopte_leaf(pte, lvl, data->iop.fmt)) { |
| 413 | cptep = iopte_deref(pte, data); | 423 | cptep = iopte_deref(pte, data); |
| 414 | } else if (pte) { | 424 | } else if (pte) { |
| 415 | /* We require an unmap first */ | 425 | /* We require an unmap first */ |
| @@ -429,31 +439,37 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data, | |||
| 429 | if (data->iop.fmt == ARM_64_LPAE_S1 || | 439 | if (data->iop.fmt == ARM_64_LPAE_S1 || |
| 430 | data->iop.fmt == ARM_32_LPAE_S1) { | 440 | data->iop.fmt == ARM_32_LPAE_S1) { |
| 431 | pte = ARM_LPAE_PTE_nG; | 441 | pte = ARM_LPAE_PTE_nG; |
| 432 | |||
| 433 | if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) | 442 | if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ)) |
| 434 | pte |= ARM_LPAE_PTE_AP_RDONLY; | 443 | pte |= ARM_LPAE_PTE_AP_RDONLY; |
| 435 | |||
| 436 | if (!(prot & IOMMU_PRIV)) | 444 | if (!(prot & IOMMU_PRIV)) |
| 437 | pte |= ARM_LPAE_PTE_AP_UNPRIV; | 445 | pte |= ARM_LPAE_PTE_AP_UNPRIV; |
| 438 | |||
| 439 | if (prot & IOMMU_MMIO) | ||
| 440 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV | ||
| 441 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 442 | else if (prot & IOMMU_CACHE) | ||
| 443 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE | ||
| 444 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 445 | } else { | 446 | } else { |
| 446 | pte = ARM_LPAE_PTE_HAP_FAULT; | 447 | pte = ARM_LPAE_PTE_HAP_FAULT; |
| 447 | if (prot & IOMMU_READ) | 448 | if (prot & IOMMU_READ) |
| 448 | pte |= ARM_LPAE_PTE_HAP_READ; | 449 | pte |= ARM_LPAE_PTE_HAP_READ; |
| 449 | if (prot & IOMMU_WRITE) | 450 | if (prot & IOMMU_WRITE) |
| 450 | pte |= ARM_LPAE_PTE_HAP_WRITE; | 451 | pte |= ARM_LPAE_PTE_HAP_WRITE; |
| 452 | } | ||
| 453 | |||
| 454 | /* | ||
| 455 | * Note that this logic is structured to accommodate Mali LPAE | ||
| 456 | * having stage-1-like attributes but stage-2-like permissions. | ||
| 457 | */ | ||
| 458 | if (data->iop.fmt == ARM_64_LPAE_S2 || | ||
| 459 | data->iop.fmt == ARM_32_LPAE_S2) { | ||
| 451 | if (prot & IOMMU_MMIO) | 460 | if (prot & IOMMU_MMIO) |
| 452 | pte |= ARM_LPAE_PTE_MEMATTR_DEV; | 461 | pte |= ARM_LPAE_PTE_MEMATTR_DEV; |
| 453 | else if (prot & IOMMU_CACHE) | 462 | else if (prot & IOMMU_CACHE) |
| 454 | pte |= ARM_LPAE_PTE_MEMATTR_OIWB; | 463 | pte |= ARM_LPAE_PTE_MEMATTR_OIWB; |
| 455 | else | 464 | else |
| 456 | pte |= ARM_LPAE_PTE_MEMATTR_NC; | 465 | pte |= ARM_LPAE_PTE_MEMATTR_NC; |
| 466 | } else { | ||
| 467 | if (prot & IOMMU_MMIO) | ||
| 468 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_DEV | ||
| 469 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 470 | else if (prot & IOMMU_CACHE) | ||
| 471 | pte |= (ARM_LPAE_MAIR_ATTR_IDX_CACHE | ||
| 472 | << ARM_LPAE_PTE_ATTRINDX_SHIFT); | ||
| 457 | } | 473 | } |
| 458 | 474 | ||
| 459 | if (prot & IOMMU_NOEXEC) | 475 | if (prot & IOMMU_NOEXEC) |
| @@ -511,7 +527,7 @@ static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl, | |||
| 511 | while (ptep != end) { | 527 | while (ptep != end) { |
| 512 | arm_lpae_iopte pte = *ptep++; | 528 | arm_lpae_iopte pte = *ptep++; |
| 513 | 529 | ||
| 514 | if (!pte || iopte_leaf(pte, lvl)) | 530 | if (!pte || iopte_leaf(pte, lvl, data->iop.fmt)) |
| 515 | continue; | 531 | continue; |
| 516 | 532 | ||
| 517 | __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); | 533 | __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data)); |
| @@ -602,7 +618,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, | |||
| 602 | if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) { | 618 | if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) { |
| 603 | __arm_lpae_set_pte(ptep, 0, &iop->cfg); | 619 | __arm_lpae_set_pte(ptep, 0, &iop->cfg); |
| 604 | 620 | ||
| 605 | if (!iopte_leaf(pte, lvl)) { | 621 | if (!iopte_leaf(pte, lvl, iop->fmt)) { |
| 606 | /* Also flush any partial walks */ | 622 | /* Also flush any partial walks */ |
| 607 | io_pgtable_tlb_add_flush(iop, iova, size, | 623 | io_pgtable_tlb_add_flush(iop, iova, size, |
| 608 | ARM_LPAE_GRANULE(data), false); | 624 | ARM_LPAE_GRANULE(data), false); |
| @@ -621,7 +637,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, | |||
| 621 | } | 637 | } |
| 622 | 638 | ||
| 623 | return size; | 639 | return size; |
| 624 | } else if (iopte_leaf(pte, lvl)) { | 640 | } else if (iopte_leaf(pte, lvl, iop->fmt)) { |
| 625 | /* | 641 | /* |
| 626 | * Insert a table at the next level to map the old region, | 642 | * Insert a table at the next level to map the old region, |
| 627 | * minus the part we want to unmap | 643 | * minus the part we want to unmap |
| @@ -669,7 +685,7 @@ static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops, | |||
| 669 | return 0; | 685 | return 0; |
| 670 | 686 | ||
| 671 | /* Leaf entry? */ | 687 | /* Leaf entry? */ |
| 672 | if (iopte_leaf(pte,lvl)) | 688 | if (iopte_leaf(pte, lvl, data->iop.fmt)) |
| 673 | goto found_translation; | 689 | goto found_translation; |
| 674 | 690 | ||
| 675 | /* Take it to the next level */ | 691 | /* Take it to the next level */ |
| @@ -995,6 +1011,32 @@ arm_32_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie) | |||
| 995 | return iop; | 1011 | return iop; |
| 996 | } | 1012 | } |
| 997 | 1013 | ||
| 1014 | static struct io_pgtable * | ||
| 1015 | arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) | ||
| 1016 | { | ||
| 1017 | struct io_pgtable *iop; | ||
| 1018 | |||
| 1019 | if (cfg->ias != 48 || cfg->oas > 40) | ||
| 1020 | return NULL; | ||
| 1021 | |||
| 1022 | cfg->pgsize_bitmap &= (SZ_4K | SZ_2M | SZ_1G); | ||
| 1023 | iop = arm_64_lpae_alloc_pgtable_s1(cfg, cookie); | ||
| 1024 | if (iop) { | ||
| 1025 | u64 mair, ttbr; | ||
| 1026 | |||
| 1027 | /* Copy values as union fields overlap */ | ||
| 1028 | mair = cfg->arm_lpae_s1_cfg.mair[0]; | ||
| 1029 | ttbr = cfg->arm_lpae_s1_cfg.ttbr[0]; | ||
| 1030 | |||
| 1031 | cfg->arm_mali_lpae_cfg.memattr = mair; | ||
| 1032 | cfg->arm_mali_lpae_cfg.transtab = ttbr | | ||
| 1033 | ARM_MALI_LPAE_TTBR_READ_INNER | | ||
| 1034 | ARM_MALI_LPAE_TTBR_ADRMODE_TABLE; | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | return iop; | ||
| 1038 | } | ||
| 1039 | |||
| 998 | struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { | 1040 | struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = { |
| 999 | .alloc = arm_64_lpae_alloc_pgtable_s1, | 1041 | .alloc = arm_64_lpae_alloc_pgtable_s1, |
| 1000 | .free = arm_lpae_free_pgtable, | 1042 | .free = arm_lpae_free_pgtable, |
| @@ -1015,6 +1057,11 @@ struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns = { | |||
| 1015 | .free = arm_lpae_free_pgtable, | 1057 | .free = arm_lpae_free_pgtable, |
| 1016 | }; | 1058 | }; |
| 1017 | 1059 | ||
| 1060 | struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = { | ||
| 1061 | .alloc = arm_mali_lpae_alloc_pgtable, | ||
| 1062 | .free = arm_lpae_free_pgtable, | ||
| 1063 | }; | ||
| 1064 | |||
| 1018 | #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST | 1065 | #ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST |
| 1019 | 1066 | ||
| 1020 | static struct io_pgtable_cfg *cfg_cookie; | 1067 | static struct io_pgtable_cfg *cfg_cookie; |
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c index 93f2880be6c6..5227cfdbb65b 100644 --- a/drivers/iommu/io-pgtable.c +++ b/drivers/iommu/io-pgtable.c | |||
| @@ -30,6 +30,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = { | |||
| 30 | [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns, | 30 | [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns, |
| 31 | [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns, | 31 | [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns, |
| 32 | [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, | 32 | [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns, |
| 33 | [ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns, | ||
| 33 | #endif | 34 | #endif |
| 34 | #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S | 35 | #ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S |
| 35 | [ARM_V7S] = &io_pgtable_arm_v7s_init_fns, | 36 | [ARM_V7S] = &io_pgtable_arm_v7s_init_fns, |
