| 1 | |
| 2 | * Copyright 2007-8 Advanced Micro Devices, Inc.
|
| 3 | * Copyright 2008 Red Hat Inc.
|
| 4 | *
|
| 5 | * Permission is hereby granted, free of charge, to any person obtaining a
|
| 6 | * copy of this software and associated documentation files (the "Software"),
|
| 7 | * to deal in the Software without restriction, including without limitation
|
| 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
| 9 | * and/or sell copies of the Software, and to permit persons to whom the
|
| 10 | * Software is furnished to do so, subject to the following conditions:
|
| 11 | *
|
| 12 | * The above copyright notice and this permission notice shall be included in
|
| 13 | * all copies or substantial portions of the Software.
|
| 14 | *
|
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
| 18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
| 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
| 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
| 21 | * OTHER DEALINGS IN THE SOFTWARE.
|
| 22 | *
|
| 23 | * Authors: Dave Airlie
|
| 24 | * Alex Deucher
|
| 25 |
|
| 26 |
|
| 27 | #include <drm/amdgpu_drm.h>
|
| 28 | #include "amdgpu.h"
|
| 29 | #include "amdgpu_i2c.h"
|
| 30 | #include "atom.h"
|
| 31 | #include "amdgpu_connectors.h"
|
| 32 | #include "amdgpu_display.h"
|
| 33 | #include "soc15_common.h"
|
| 34 | #include "gc/gc_11_0_0_offset.h"
|
| 35 | #include "gc/gc_11_0_0_sh_mask.h"
|
| 36 | #include "bif/bif_4_1_d.h"
|
| 37 | #include <asm/div64.h>
|
| 38 |
|
| 39 | #include <linux/pci.h>
|
| 40 | #include <linux/pm_runtime.h>
|
| 41 | #include <drm/drm_crtc_helper.h>
|
| 42 | #include <drm/drm_damage_helper.h>
|
| 43 | #include <drm/drm_drv.h>
|
| 44 | #include <drm/drm_edid.h>
|
| 45 | #include <drm/drm_fb_helper.h>
|
| 46 | #include <drm/drm_gem_framebuffer_helper.h>
|
| 47 | #include <drm/drm_fourcc.h>
|
| 48 | #include <drm/drm_modeset_helper.h>
|
| 49 | #include <drm/drm_vblank.h>
|
| 50 |
|
| 51 | |
| 52 | * amdgpu_display_hotplug_work_func - work handler for display hotplug event
|
| 53 | *
|
| 54 | * @work: work struct pointer
|
| 55 | *
|
| 56 | * This is the hotplug event work handler (all ASICs).
|
| 57 | * The work gets scheduled from the IRQ handler if there
|
| 58 | * was a hotplug interrupt. It walks through the connector table
|
| 59 | * and calls hotplug handler for each connector. After this, it sends
|
| 60 | * a DRM hotplug event to alert userspace.
|
| 61 | *
|
| 62 | * This design approach is required in order to defer hotplug event handling
|
| 63 | * from the IRQ handler to a work handler because hotplug handler has to use
|
| 64 | * mutexes which cannot be locked in an IRQ handler (since &mutex_lock may
|
| 65 | * sleep).
|
| 66 |
|
| 67 | void amdgpu_display_hotplug_work_func(struct work_struct *work)
|
| 68 | {
|
| 69 | struct amdgpu_device *adev = container_of(work, struct amdgpu_device,
|
| 70 | hotplug_work.work);
|
| 71 | struct drm_device *dev = adev_to_drm(adev);
|
| 72 | struct drm_mode_config *mode_config = &dev->mode_config;
|
| 73 | struct drm_connector *connector;
|
| 74 | struct drm_connector_list_iter iter;
|
| 75 |
|
| 76 | mutex_lock(&mode_config->mutex);
|
| 77 | drm_connector_list_iter_begin(dev, &iter);
|
| 78 | drm_for_each_connector_iter(connector, &iter)
|
| 79 | amdgpu_connector_hotplug(connector);
|
| 80 | drm_connector_list_iter_end(&iter);
|
| 81 | mutex_unlock(&mode_config->mutex);
|
| 82 |
|
| 83 | drm_helper_hpd_irq_event(dev);
|
| 84 | }
|
| 85 |
|
| 86 | static int amdgpu_display_framebuffer_init(struct drm_device *dev,
|
| 87 | struct amdgpu_framebuffer *rfb,
|
| 88 | const struct drm_mode_fb_cmd2 *mode_cmd,
|
| 89 | struct drm_gem_object *obj);
|
| 90 |
|
| 91 | static void amdgpu_display_flip_callback(struct dma_fence *f,
|
| 92 | struct dma_fence_cb *cb)
|
| 93 | {
|
| 94 | struct amdgpu_flip_work *work =
|
| 95 | container_of(cb, struct amdgpu_flip_work, cb);
|
| 96 |
|
| 97 | dma_fence_put(f);
|
| 98 | schedule_work(&work->flip_work.work);
|
| 99 | }
|
| 100 |
|
| 101 | static bool amdgpu_display_flip_handle_fence(struct amdgpu_flip_work *work,
|
| 102 | struct dma_fence **f)
|
| 103 | {
|
| 104 | struct dma_fence *fence = *f;
|
| 105 |
|
| 106 | if (fence == NULL)
|
| 107 | return false;
|
| 108 |
|
| 109 | *f = NULL;
|
| 110 |
|
| 111 | if (!dma_fence_add_callback(fence, &work->cb,
|
| 112 | amdgpu_display_flip_callback))
|
| 113 | return true;
|
| 114 |
|
| 115 | dma_fence_put(fence);
|
| 116 | return false;
|
| 117 | }
|
| 118 |
|
| 119 | static void amdgpu_display_flip_work_func(struct work_struct *__work)
|
| 120 | {
|
| 121 | struct delayed_work *delayed_work =
|
| 122 | container_of(__work, struct delayed_work, work);
|
| 123 | struct amdgpu_flip_work *work =
|
| 124 | container_of(delayed_work, struct amdgpu_flip_work, flip_work);
|
| 125 | struct amdgpu_device *adev = work->adev;
|
| 126 | struct amdgpu_crtc *amdgpu_crtc = adev->mode_info.crtcs[work->crtc_id];
|
| 127 |
|
| 128 | struct drm_crtc *crtc = &amdgpu_crtc->base;
|
| 129 | unsigned long flags;
|
| 130 | unsigned int i;
|
| 131 | int vpos, hpos;
|
| 132 |
|
| 133 | for (i = 0; i < work->shared_count; ++i)
|
| 134 | if (amdgpu_display_flip_handle_fence(work, &work->shared[i]))
|
| 135 | return;
|
| 136 |
|
| 137 | |
| 138 | * targeted by the flip
|
| 139 |
|
| 140 | if (amdgpu_crtc->enabled &&
|
| 141 | (amdgpu_display_get_crtc_scanoutpos(adev_to_drm(adev), work->crtc_id, 0,
|
| 142 | &vpos, &hpos, NULL, NULL,
|
| 143 | &crtc->hwmode)
|
| 144 | & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
|
| 145 | (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
|
| 146 | (int)(work->target_vblank -
|
| 147 | amdgpu_get_vblank_counter_kms(crtc)) > 0) {
|
| 148 | schedule_delayed_work(&work->flip_work, usecs_to_jiffies(1000));
|
| 149 | return;
|
| 150 | }
|
| 151 |
|
| 152 |
|
| 153 | spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
| 154 |
|
| 155 |
|
| 156 | adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base, work->async);
|
| 157 |
|
| 158 |
|
| 159 | amdgpu_crtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
|
| 160 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
| 161 |
|
| 162 |
|
| 163 | drm_dbg_vbl(adev_to_drm(adev),
|
| 164 | "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_SUBMITTED, work: %p,\n",
|
| 165 | amdgpu_crtc->crtc_id, amdgpu_crtc, work);
|
| 166 |
|
| 167 | }
|
| 168 |
|
| 169 | |
| 170 | * Handle unpin events outside the interrupt handler proper.
|
| 171 |
|
| 172 | static void amdgpu_display_unpin_work_func(struct work_struct *__work)
|
| 173 | {
|
| 174 | struct amdgpu_flip_work *work =
|
| 175 | container_of(__work, struct amdgpu_flip_work, unpin_work);
|
| 176 | int r;
|
| 177 |
|
| 178 |
|
| 179 | r = amdgpu_bo_reserve(work->old_abo, true);
|
| 180 | if (likely(r == 0)) {
|
| 181 | amdgpu_bo_unpin(work->old_abo);
|
| 182 | amdgpu_bo_unreserve(work->old_abo);
|
| 183 | } else
|
| 184 | DRM_ERROR("failed to reserve buffer after flip\n");
|
| 185 |
|
| 186 | amdgpu_bo_unref(&work->old_abo);
|
| 187 | kfree(work->shared);
|
| 188 | kfree(work);
|
| 189 | }
|
| 190 |
|
| 191 | int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,
|
| 192 | struct drm_framebuffer *fb,
|
| 193 | struct drm_pending_vblank_event *event,
|
| 194 | uint32_t page_flip_flags, uint32_t target,
|
| 195 | struct drm_modeset_acquire_ctx *ctx)
|
| 196 | {
|
| 197 | struct drm_device *dev = crtc->dev;
|
| 198 | struct amdgpu_device *adev = drm_to_adev(dev);
|
| 199 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
| 200 | struct drm_gem_object *obj;
|
| 201 | struct amdgpu_flip_work *work;
|
| 202 | struct amdgpu_bo *new_abo;
|
| 203 | unsigned long flags;
|
| 204 | u64 tiling_flags;
|
| 205 | int i, r;
|
| 206 |
|
| 207 | work = kzalloc_obj(*work);
|
| 208 | if (work == NULL)
|
| 209 | return -ENOMEM;
|
| 210 |
|
| 211 | INIT_DELAYED_WORK(&work->flip_work, amdgpu_display_flip_work_func);
|
| 212 | INIT_WORK(&work->unpin_work, amdgpu_display_unpin_work_func);
|
| 213 |
|
| 214 | work->event = event;
|
| 215 | work->adev = adev;
|
| 216 | work->crtc_id = amdgpu_crtc->crtc_id;
|
| 217 | work->async = (page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC) != 0;
|
| 218 |
|
| 219 |
|
| 220 | obj = crtc->primary->fb->obj[0];
|
| 221 |
|
| 222 |
|
| 223 | work->old_abo = gem_to_amdgpu_bo(obj);
|
| 224 | amdgpu_bo_ref(work->old_abo);
|
| 225 |
|
| 226 | obj = fb->obj[0];
|
| 227 | new_abo = gem_to_amdgpu_bo(obj);
|
| 228 |
|
| 229 |
|
| 230 | r = amdgpu_bo_reserve(new_abo, false);
|
| 231 | if (unlikely(r != 0)) {
|
| 232 | DRM_ERROR("failed to reserve new abo buffer before flip\n");
|
| 233 | goto cleanup;
|
| 234 | }
|
| 235 |
|
| 236 | if (!adev->enable_virtual_display) {
|
| 237 | new_abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
|
| 238 | r = amdgpu_bo_pin(new_abo,
|
| 239 | amdgpu_display_supported_domains(adev, new_abo->flags));
|
| 240 | if (unlikely(r != 0)) {
|
| 241 | DRM_ERROR("failed to pin new abo buffer before flip\n");
|
| 242 | goto unreserve;
|
| 243 | }
|
| 244 | }
|
| 245 |
|
| 246 | r = amdgpu_ttm_alloc_gart(&new_abo->tbo);
|
| 247 | if (unlikely(r != 0)) {
|
| 248 | DRM_ERROR("%p bind failed\n", new_abo);
|
| 249 | goto unpin;
|
| 250 | }
|
| 251 |
|
| 252 | r = dma_resv_get_fences(new_abo->tbo.base.resv, DMA_RESV_USAGE_WRITE,
|
| 253 | &work->shared_count,
|
| 254 | &work->shared);
|
| 255 | if (unlikely(r != 0)) {
|
| 256 | DRM_ERROR("failed to get fences for buffer\n");
|
| 257 | goto unpin;
|
| 258 | }
|
| 259 |
|
| 260 | amdgpu_bo_get_tiling_flags(new_abo, &tiling_flags);
|
| 261 | amdgpu_bo_unreserve(new_abo);
|
| 262 |
|
| 263 | if (!adev->enable_virtual_display)
|
| 264 | work->base = amdgpu_bo_gpu_offset(new_abo);
|
| 265 | work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) +
|
| 266 | amdgpu_get_vblank_counter_kms(crtc);
|
| 267 |
|
| 268 |
|
| 269 | spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
| 270 | if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_NONE) {
|
| 271 | DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
|
| 272 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
| 273 | r = -EBUSY;
|
| 274 | goto pflip_cleanup;
|
| 275 | }
|
| 276 |
|
| 277 | amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING;
|
| 278 | amdgpu_crtc->pflip_works = work;
|
| 279 |
|
| 280 |
|
| 281 | DRM_DEBUG_DRIVER("crtc:%d[%p], pflip_stat:AMDGPU_FLIP_PENDING, work: %p,\n",
|
| 282 | amdgpu_crtc->crtc_id, amdgpu_crtc, work);
|
| 283 |
|
| 284 | crtc->primary->fb = fb;
|
| 285 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
| 286 | amdgpu_display_flip_work_func(&work->flip_work.work);
|
| 287 | return 0;
|
| 288 |
|
| 289 | pflip_cleanup:
|
| 290 | if (unlikely(amdgpu_bo_reserve(new_abo, false) != 0)) {
|
| 291 | DRM_ERROR("failed to reserve new abo in error path\n");
|
| 292 | goto cleanup;
|
| 293 | }
|
| 294 | unpin:
|
| 295 | if (!adev->enable_virtual_display)
|
| 296 | amdgpu_bo_unpin(new_abo);
|
| 297 |
|
| 298 | unreserve:
|
| 299 | amdgpu_bo_unreserve(new_abo);
|
| 300 |
|
| 301 | cleanup:
|
| 302 | amdgpu_bo_unref(&work->old_abo);
|
| 303 | for (i = 0; i < work->shared_count; ++i)
|
| 304 | dma_fence_put(work->shared[i]);
|
| 305 | kfree(work->shared);
|
| 306 | kfree(work);
|
| 307 |
|
| 308 | return r;
|
| 309 | }
|
| 310 |
|
| 311 | int amdgpu_display_crtc_set_config(struct drm_mode_set *set,
|
| 312 | struct drm_modeset_acquire_ctx *ctx)
|
| 313 | {
|
| 314 | struct drm_device *dev;
|
| 315 | struct amdgpu_device *adev;
|
| 316 | struct drm_crtc *crtc;
|
| 317 | bool active = false;
|
| 318 | int ret;
|
| 319 |
|
| 320 | if (!set || !set->crtc)
|
| 321 | return -EINVAL;
|
| 322 |
|
| 323 | dev = set->crtc->dev;
|
| 324 |
|
| 325 | ret = pm_runtime_get_sync(dev->dev);
|
| 326 | if (ret < 0)
|
| 327 | goto out;
|
| 328 |
|
| 329 | ret = drm_crtc_helper_set_config(set, ctx);
|
| 330 |
|
| 331 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
| 332 | if (crtc->enabled)
|
| 333 | active = true;
|
| 334 |
|
| 335 | adev = drm_to_adev(dev);
|
| 336 | |
| 337 | * take the current one
|
| 338 |
|
| 339 | if (active && !adev->have_disp_power_ref) {
|
| 340 | adev->have_disp_power_ref = true;
|
| 341 | return ret;
|
| 342 | }
|
| 343 | |
| 344 | * drop the power ref we got before
|
| 345 |
|
| 346 | if (!active && adev->have_disp_power_ref)
|
| 347 | adev->have_disp_power_ref = false;
|
| 348 | out:
|
| 349 |
|
| 350 | pm_runtime_put_autosuspend(dev->dev);
|
| 351 | return ret;
|
| 352 | }
|
| 353 |
|
| 354 | static const char *encoder_names[41] = {
|
| 355 | "NONE",
|
| 356 | "INTERNAL_LVDS",
|
| 357 | "INTERNAL_TMDS1",
|
| 358 | "INTERNAL_TMDS2",
|
| 359 | "INTERNAL_DAC1",
|
| 360 | "INTERNAL_DAC2",
|
| 361 | "INTERNAL_SDVOA",
|
| 362 | "INTERNAL_SDVOB",
|
| 363 | "SI170B",
|
| 364 | "CH7303",
|
| 365 | "CH7301",
|
| 366 | "INTERNAL_DVO1",
|
| 367 | "EXTERNAL_SDVOA",
|
| 368 | "EXTERNAL_SDVOB",
|
| 369 | "TITFP513",
|
| 370 | "INTERNAL_LVTM1",
|
| 371 | "VT1623",
|
| 372 | "HDMI_SI1930",
|
| 373 | "HDMI_INTERNAL",
|
| 374 | "INTERNAL_KLDSCP_TMDS1",
|
| 375 | "INTERNAL_KLDSCP_DVO1",
|
| 376 | "INTERNAL_KLDSCP_DAC1",
|
| 377 | "INTERNAL_KLDSCP_DAC2",
|
| 378 | "SI178",
|
| 379 | "MVPU_FPGA",
|
| 380 | "INTERNAL_DDI",
|
| 381 | "VT1625",
|
| 382 | "HDMI_SI1932",
|
| 383 | "DP_AN9801",
|
| 384 | "DP_DP501",
|
| 385 | "INTERNAL_UNIPHY",
|
| 386 | "INTERNAL_KLDSCP_LVTMA",
|
| 387 | "INTERNAL_UNIPHY1",
|
| 388 | "INTERNAL_UNIPHY2",
|
| 389 | "NUTMEG",
|
| 390 | "TRAVIS",
|
| 391 | "INTERNAL_VCE",
|
| 392 | "INTERNAL_UNIPHY3",
|
| 393 | "HDMI_ANX9805",
|
| 394 | "INTERNAL_AMCLK",
|
| 395 | "VIRTUAL",
|
| 396 | };
|
| 397 |
|
| 398 | static const char *hpd_names[6] = {
|
| 399 | "HPD1",
|
| 400 | "HPD2",
|
| 401 | "HPD3",
|
| 402 | "HPD4",
|
| 403 | "HPD5",
|
| 404 | "HPD6",
|
| 405 | };
|
| 406 |
|
| 407 | void amdgpu_display_print_display_setup(struct drm_device *dev)
|
| 408 | {
|
| 409 | struct drm_connector *connector;
|
| 410 | struct amdgpu_connector *amdgpu_connector;
|
| 411 | struct drm_encoder *encoder;
|
| 412 | struct amdgpu_encoder *amdgpu_encoder;
|
| 413 | struct drm_connector_list_iter iter;
|
| 414 | uint32_t devices;
|
| 415 | int i = 0;
|
| 416 |
|
| 417 | drm_connector_list_iter_begin(dev, &iter);
|
| 418 | drm_info(dev, "AMDGPU Display Connectors\n");
|
| 419 | drm_for_each_connector_iter(connector, &iter) {
|
| 420 | amdgpu_connector = to_amdgpu_connector(connector);
|
| 421 | drm_info(dev, "Connector %d:\n", i);
|
| 422 | drm_info(dev, " %s\n", connector->name);
|
| 423 | if (amdgpu_connector->hpd.hpd != AMDGPU_HPD_NONE)
|
| 424 | drm_info(dev, " %s\n", hpd_names[amdgpu_connector->hpd.hpd]);
|
| 425 | if (amdgpu_connector->ddc_bus) {
|
| 426 | drm_info(dev, " DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
|
| 427 | amdgpu_connector->ddc_bus->rec.mask_clk_reg,
|
| 428 | amdgpu_connector->ddc_bus->rec.mask_data_reg,
|
| 429 | amdgpu_connector->ddc_bus->rec.a_clk_reg,
|
| 430 | amdgpu_connector->ddc_bus->rec.a_data_reg,
|
| 431 | amdgpu_connector->ddc_bus->rec.en_clk_reg,
|
| 432 | amdgpu_connector->ddc_bus->rec.en_data_reg,
|
| 433 | amdgpu_connector->ddc_bus->rec.y_clk_reg,
|
| 434 | amdgpu_connector->ddc_bus->rec.y_data_reg);
|
| 435 | if (amdgpu_connector->router.ddc_valid)
|
| 436 | drm_info(dev, " DDC Router 0x%x/0x%x\n",
|
| 437 | amdgpu_connector->router.ddc_mux_control_pin,
|
| 438 | amdgpu_connector->router.ddc_mux_state);
|
| 439 | if (amdgpu_connector->router.cd_valid)
|
| 440 | drm_info(dev, " Clock/Data Router 0x%x/0x%x\n",
|
| 441 | amdgpu_connector->router.cd_mux_control_pin,
|
| 442 | amdgpu_connector->router.cd_mux_state);
|
| 443 | } else {
|
| 444 | if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
|
| 445 | connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
|
| 446 | connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
|
| 447 | connector->connector_type == DRM_MODE_CONNECTOR_DVIA ||
|
| 448 | connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
|
| 449 | connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)
|
| 450 | drm_info(dev, " DDC: no ddc bus - possible BIOS bug - please report to [email protected]\n");
|
| 451 | }
|
| 452 | drm_info(dev, " Encoders:\n");
|
| 453 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
| 454 | amdgpu_encoder = to_amdgpu_encoder(encoder);
|
| 455 | devices = amdgpu_encoder->devices & amdgpu_connector->devices;
|
| 456 | if (devices) {
|
| 457 | if (devices & ATOM_DEVICE_CRT1_SUPPORT)
|
| 458 | drm_info(dev, " CRT1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 459 | if (devices & ATOM_DEVICE_CRT2_SUPPORT)
|
| 460 | drm_info(dev, " CRT2: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 461 | if (devices & ATOM_DEVICE_LCD1_SUPPORT)
|
| 462 | drm_info(dev, " LCD1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 463 | if (devices & ATOM_DEVICE_DFP1_SUPPORT)
|
| 464 | drm_info(dev, " DFP1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 465 | if (devices & ATOM_DEVICE_DFP2_SUPPORT)
|
| 466 | drm_info(dev, " DFP2: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 467 | if (devices & ATOM_DEVICE_DFP3_SUPPORT)
|
| 468 | drm_info(dev, " DFP3: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 469 | if (devices & ATOM_DEVICE_DFP4_SUPPORT)
|
| 470 | drm_info(dev, " DFP4: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 471 | if (devices & ATOM_DEVICE_DFP5_SUPPORT)
|
| 472 | drm_info(dev, " DFP5: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 473 | if (devices & ATOM_DEVICE_DFP6_SUPPORT)
|
| 474 | drm_info(dev, " DFP6: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 475 | if (devices & ATOM_DEVICE_TV1_SUPPORT)
|
| 476 | drm_info(dev, " TV1: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 477 | if (devices & ATOM_DEVICE_CV_SUPPORT)
|
| 478 | drm_info(dev, " CV: %s\n", encoder_names[amdgpu_encoder->encoder_id]);
|
| 479 | }
|
| 480 | }
|
| 481 | i++;
|
| 482 | }
|
| 483 | drm_connector_list_iter_end(&iter);
|
| 484 | }
|
| 485 |
|
| 486 | bool amdgpu_display_ddc_probe(struct amdgpu_connector *amdgpu_connector,
|
| 487 | bool use_aux)
|
| 488 | {
|
| 489 | u8 out = 0x0;
|
| 490 | u8 buf[8];
|
| 491 | int ret;
|
| 492 | struct i2c_msg msgs[] = {
|
| 493 | {
|
| 494 | .addr = DDC_ADDR,
|
| 495 | .flags = 0,
|
| 496 | .len = 1,
|
| 497 | .buf = &out,
|
| 498 | },
|
| 499 | {
|
| 500 | .addr = DDC_ADDR,
|
| 501 | .flags = I2C_M_RD,
|
| 502 | .len = 8,
|
| 503 | .buf = buf,
|
| 504 | }
|
| 505 | };
|
| 506 |
|
| 507 |
|
| 508 | if (amdgpu_connector->router.ddc_valid)
|
| 509 | amdgpu_i2c_router_select_ddc_port(amdgpu_connector);
|
| 510 |
|
| 511 | if (use_aux)
|
| 512 | ret = i2c_transfer(&amdgpu_connector->ddc_bus->aux.ddc, msgs, 2);
|
| 513 | else
|
| 514 | ret = i2c_transfer(&amdgpu_connector->ddc_bus->adapter, msgs, 2);
|
| 515 |
|
| 516 | if (ret != 2)
|
| 517 |
|
| 518 | return false;
|
| 519 | |
| 520 | * EDID header starts with:
|
| 521 | * 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00.
|
| 522 | * Only the first 6 bytes must be valid as
|
| 523 | * drm_edid_block_valid() can fix the last 2 bytes
|
| 524 |
|
| 525 | if (drm_edid_header_is_valid(buf) < 6) {
|
| 526 | |
| 527 | * connector
|
| 528 |
|
| 529 | return false;
|
| 530 | }
|
| 531 | return true;
|
| 532 | }
|
| 533 |
|
| 534 | static int amdgpu_dirtyfb(struct drm_framebuffer *fb, struct drm_file *file,
|
| 535 | unsigned int flags, unsigned int color,
|
| 536 | struct drm_clip_rect *clips, unsigned int num_clips)
|
| 537 | {
|
| 538 |
|
| 539 | if (file)
|
| 540 | return -ENOSYS;
|
| 541 |
|
| 542 | return drm_atomic_helper_dirtyfb(fb, file, flags, color, clips,
|
| 543 | num_clips);
|
| 544 | }
|
| 545 |
|
| 546 | static const struct drm_framebuffer_funcs amdgpu_fb_funcs = {
|
| 547 | .destroy = drm_gem_fb_destroy,
|
| 548 | .create_handle = drm_gem_fb_create_handle,
|
| 549 | };
|
| 550 |
|
| 551 | static const struct drm_framebuffer_funcs amdgpu_fb_funcs_atomic = {
|
| 552 | .destroy = drm_gem_fb_destroy,
|
| 553 | .create_handle = drm_gem_fb_create_handle,
|
| 554 | .dirty = amdgpu_dirtyfb
|
| 555 | };
|
| 556 |
|
| 557 | uint32_t amdgpu_display_supported_domains(struct amdgpu_device *adev,
|
| 558 | uint64_t bo_flags)
|
| 559 | {
|
| 560 | uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM;
|
| 561 |
|
| 562 | #if defined(CONFIG_DRM_AMD_DC)
|
| 563 | |
| 564 | * if amdgpu_bo_support_uswc returns false it means that USWC mappings
|
| 565 | * is not supported for this board. But this mapping is required
|
| 566 | * to avoid hang caused by placement of scanout BO in GTT on certain
|
| 567 | * APUs. So force the BO placement to VRAM in case this architecture
|
| 568 | * will not allow USWC mappings.
|
| 569 | * Also, don't allow GTT domain if the BO doesn't have USWC flag set.
|
| 570 |
|
| 571 | if ((bo_flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) &&
|
| 572 | amdgpu_bo_support_uswc(bo_flags) &&
|
| 573 | adev->dc_enabled &&
|
| 574 | adev->mode_info.gpu_vm_support)
|
| 575 | domain |= AMDGPU_GEM_DOMAIN_GTT;
|
| 576 | #endif
|
| 577 |
|
| 578 | return domain;
|
| 579 | }
|
| 580 |
|
| 581 | static const struct drm_format_info dcc_formats[] = {
|
| 582 | { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 2,
|
| 583 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 584 | { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 2,
|
| 585 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 586 | { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 2,
|
| 587 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 588 | .has_alpha = true, },
|
| 589 | { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 2,
|
| 590 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 591 | .has_alpha = true, },
|
| 592 | { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 2,
|
| 593 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 594 | .has_alpha = true, },
|
| 595 | { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 2,
|
| 596 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 597 | { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 2,
|
| 598 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 599 | { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 2,
|
| 600 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 601 | .has_alpha = true, },
|
| 602 | { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 2,
|
| 603 | .cpp = { 4, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 604 | .has_alpha = true, },
|
| 605 | { .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 2,
|
| 606 | .cpp = { 2, 0, }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 607 | };
|
| 608 |
|
| 609 | static const struct drm_format_info dcc_retile_formats[] = {
|
| 610 | { .format = DRM_FORMAT_XRGB8888, .depth = 24, .num_planes = 3,
|
| 611 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 612 | { .format = DRM_FORMAT_XBGR8888, .depth = 24, .num_planes = 3,
|
| 613 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 614 | { .format = DRM_FORMAT_ARGB8888, .depth = 32, .num_planes = 3,
|
| 615 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 616 | .has_alpha = true, },
|
| 617 | { .format = DRM_FORMAT_ABGR8888, .depth = 32, .num_planes = 3,
|
| 618 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 619 | .has_alpha = true, },
|
| 620 | { .format = DRM_FORMAT_BGRA8888, .depth = 32, .num_planes = 3,
|
| 621 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 622 | .has_alpha = true, },
|
| 623 | { .format = DRM_FORMAT_XRGB2101010, .depth = 30, .num_planes = 3,
|
| 624 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 625 | { .format = DRM_FORMAT_XBGR2101010, .depth = 30, .num_planes = 3,
|
| 626 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 627 | { .format = DRM_FORMAT_ARGB2101010, .depth = 30, .num_planes = 3,
|
| 628 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 629 | .has_alpha = true, },
|
| 630 | { .format = DRM_FORMAT_ABGR2101010, .depth = 30, .num_planes = 3,
|
| 631 | .cpp = { 4, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1,
|
| 632 | .has_alpha = true, },
|
| 633 | { .format = DRM_FORMAT_RGB565, .depth = 16, .num_planes = 3,
|
| 634 | .cpp = { 2, 0, 0 }, .block_w = {1, 1, 1}, .block_h = {1, 1, 1}, .hsub = 1, .vsub = 1, },
|
| 635 | };
|
| 636 |
|
| 637 | static const struct drm_format_info *
|
| 638 | lookup_format_info(const struct drm_format_info formats[],
|
| 639 | int num_formats, u32 format)
|
| 640 | {
|
| 641 | int i;
|
| 642 |
|
| 643 | for (i = 0; i < num_formats; i++) {
|
| 644 | if (formats[i].format == format)
|
| 645 | return &formats[i];
|
| 646 | }
|
| 647 |
|
| 648 | return NULL;
|
| 649 | }
|
| 650 |
|
| 651 | const struct drm_format_info *
|
| 652 | amdgpu_lookup_format_info(u32 format, uint64_t modifier)
|
| 653 | {
|
| 654 | if (!IS_AMD_FMT_MOD(modifier))
|
| 655 | return NULL;
|
| 656 |
|
| 657 | if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) < AMD_FMT_MOD_TILE_VER_GFX9 ||
|
| 658 | AMD_FMT_MOD_GET(TILE_VERSION, modifier) >= AMD_FMT_MOD_TILE_VER_GFX12)
|
| 659 | return NULL;
|
| 660 |
|
| 661 | if (AMD_FMT_MOD_GET(DCC_RETILE, modifier))
|
| 662 | return lookup_format_info(dcc_retile_formats,
|
| 663 | ARRAY_SIZE(dcc_retile_formats),
|
| 664 | format);
|
| 665 |
|
| 666 | if (AMD_FMT_MOD_GET(DCC, modifier))
|
| 667 | return lookup_format_info(dcc_formats, ARRAY_SIZE(dcc_formats),
|
| 668 | format);
|
| 669 |
|
| 670 |
|
| 671 | return NULL;
|
| 672 | }
|
| 673 |
|
| 674 |
|
| 675 | |
| 676 | * Tries to extract the renderable DCC offset from the opaque metadata attached
|
| 677 | * to the buffer.
|
| 678 |
|
| 679 | static int
|
| 680 | extract_render_dcc_offset(struct amdgpu_device *adev,
|
| 681 | struct drm_gem_object *obj,
|
| 682 | uint64_t *offset)
|
| 683 | {
|
| 684 | struct amdgpu_bo *rbo;
|
| 685 | int r = 0;
|
| 686 | uint32_t metadata[10];
|
| 687 | uint32_t size;
|
| 688 |
|
| 689 | rbo = gem_to_amdgpu_bo(obj);
|
| 690 | r = amdgpu_bo_reserve(rbo, false);
|
| 691 |
|
| 692 | if (unlikely(r)) {
|
| 693 |
|
| 694 | if (r != -ERESTARTSYS)
|
| 695 | DRM_ERROR("Unable to reserve buffer: %d\n", r);
|
| 696 | return r;
|
| 697 | }
|
| 698 |
|
| 699 | r = amdgpu_bo_get_metadata(rbo, metadata, sizeof(metadata), &size, NULL);
|
| 700 | amdgpu_bo_unreserve(rbo);
|
| 701 |
|
| 702 | if (r)
|
| 703 | return r;
|
| 704 |
|
| 705 | |
| 706 | * The first word is the metadata version, and we need space for at least
|
| 707 | * the version + pci vendor+device id + 8 words for a descriptor.
|
| 708 |
|
| 709 | if (size < 40 || metadata[0] != 1)
|
| 710 | return -EINVAL;
|
| 711 |
|
| 712 | if (adev->family >= AMDGPU_FAMILY_NV) {
|
| 713 |
|
| 714 | *offset = ((u64)metadata[9] << 16u) |
|
| 715 | ((metadata[8] & 0xFF000000u) >> 16);
|
| 716 | } else {
|
| 717 |
|
| 718 | *offset = ((u64)metadata[9] << 8u) |
|
| 719 | ((u64)(metadata[7] & 0x1FE0000u) << 23);
|
| 720 | }
|
| 721 |
|
| 722 | return 0;
|
| 723 | }
|
| 724 |
|
| 725 | static int convert_tiling_flags_to_modifier_gfx12(struct amdgpu_framebuffer *afb)
|
| 726 | {
|
| 727 | u64 modifier = 0;
|
| 728 | int swizzle_mode = AMDGPU_TILING_GET(afb->tiling_flags, GFX12_SWIZZLE_MODE);
|
| 729 |
|
| 730 | if (!swizzle_mode) {
|
| 731 | modifier = DRM_FORMAT_MOD_LINEAR;
|
| 732 | } else {
|
| 733 | int max_comp_block =
|
| 734 | AMDGPU_TILING_GET(afb->tiling_flags, GFX12_DCC_MAX_COMPRESSED_BLOCK);
|
| 735 |
|
| 736 | modifier =
|
| 737 | AMD_FMT_MOD |
|
| 738 | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX12) |
|
| 739 | AMD_FMT_MOD_SET(TILE, swizzle_mode) |
|
| 740 | AMD_FMT_MOD_SET(DCC, afb->gfx12_dcc) |
|
| 741 | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, max_comp_block);
|
| 742 | }
|
| 743 |
|
| 744 | afb->base.modifier = modifier;
|
| 745 | afb->base.flags |= DRM_MODE_FB_MODIFIERS;
|
| 746 | return 0;
|
| 747 | }
|
| 748 |
|
| 749 | static int convert_tiling_flags_to_modifier_gfx9(struct amdgpu_framebuffer *afb)
|
| 750 | {
|
| 751 | struct amdgpu_device *adev = drm_to_adev(afb->base.dev);
|
| 752 | uint64_t modifier = 0;
|
| 753 | int num_pipes = 0;
|
| 754 | int num_pkrs = 0;
|
| 755 |
|
| 756 | num_pkrs = adev->gfx.config.gb_addr_config_fields.num_pkrs;
|
| 757 | num_pipes = adev->gfx.config.gb_addr_config_fields.num_pipes;
|
| 758 |
|
| 759 | if (!afb->tiling_flags || !AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE)) {
|
| 760 | modifier = DRM_FORMAT_MOD_LINEAR;
|
| 761 | } else {
|
| 762 | int swizzle = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE);
|
| 763 | bool has_xor = swizzle >= 16;
|
| 764 | int block_size_bits;
|
| 765 | int version;
|
| 766 | int pipe_xor_bits = 0;
|
| 767 | int bank_xor_bits = 0;
|
| 768 | int packers = 0;
|
| 769 | int rb = 0;
|
| 770 | int pipes = ilog2(num_pipes);
|
| 771 | uint32_t dcc_offset = AMDGPU_TILING_GET(afb->tiling_flags, DCC_OFFSET_256B);
|
| 772 |
|
| 773 | switch (swizzle >> 2) {
|
| 774 | case 0:
|
| 775 | block_size_bits = 8;
|
| 776 | break;
|
| 777 | case 1:
|
| 778 | case 5:
|
| 779 | block_size_bits = 12;
|
| 780 | break;
|
| 781 | case 2:
|
| 782 | case 4:
|
| 783 | case 6:
|
| 784 | block_size_bits = 16;
|
| 785 | break;
|
| 786 | case 7:
|
| 787 | block_size_bits = 18;
|
| 788 | break;
|
| 789 | default:
|
| 790 |
|
| 791 | return -EINVAL;
|
| 792 | }
|
| 793 |
|
| 794 | if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(11, 0, 0))
|
| 795 | version = AMD_FMT_MOD_TILE_VER_GFX11;
|
| 796 | else if (amdgpu_ip_version(adev, GC_HWIP, 0) >=
|
| 797 | IP_VERSION(10, 3, 0))
|
| 798 | version = AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS;
|
| 799 | else if (amdgpu_ip_version(adev, GC_HWIP, 0) >=
|
| 800 | IP_VERSION(10, 0, 0))
|
| 801 | version = AMD_FMT_MOD_TILE_VER_GFX10;
|
| 802 | else
|
| 803 | version = AMD_FMT_MOD_TILE_VER_GFX9;
|
| 804 |
|
| 805 | switch (swizzle & 3) {
|
| 806 | case 0:
|
| 807 | return -EINVAL;
|
| 808 | case 1:
|
| 809 | if (amdgpu_ip_version(adev, GC_HWIP, 0) <
|
| 810 | IP_VERSION(11, 0, 0)) {
|
| 811 | if (!has_xor)
|
| 812 | version = AMD_FMT_MOD_TILE_VER_GFX9;
|
| 813 | }
|
| 814 | break;
|
| 815 | case 2:
|
| 816 | if (amdgpu_ip_version(adev, GC_HWIP, 0) <
|
| 817 | IP_VERSION(11, 0, 0)) {
|
| 818 | if (!has_xor && afb->base.format->cpp[0] != 4)
|
| 819 | version = AMD_FMT_MOD_TILE_VER_GFX9;
|
| 820 | }
|
| 821 | break;
|
| 822 | case 3:
|
| 823 | break;
|
| 824 | }
|
| 825 |
|
| 826 | if (has_xor) {
|
| 827 | if (num_pipes == num_pkrs && num_pkrs == 0) {
|
| 828 | DRM_ERROR("invalid number of pipes and packers\n");
|
| 829 | return -EINVAL;
|
| 830 | }
|
| 831 |
|
| 832 | switch (version) {
|
| 833 | case AMD_FMT_MOD_TILE_VER_GFX11:
|
| 834 | pipe_xor_bits = min(block_size_bits - 8, pipes);
|
| 835 | packers = ilog2(adev->gfx.config.gb_addr_config_fields.num_pkrs);
|
| 836 | break;
|
| 837 | case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS:
|
| 838 | pipe_xor_bits = min(block_size_bits - 8, pipes);
|
| 839 | packers = min(block_size_bits - 8 - pipe_xor_bits,
|
| 840 | ilog2(adev->gfx.config.gb_addr_config_fields.num_pkrs));
|
| 841 | break;
|
| 842 | case AMD_FMT_MOD_TILE_VER_GFX10:
|
| 843 | pipe_xor_bits = min(block_size_bits - 8, pipes);
|
| 844 | break;
|
| 845 | case AMD_FMT_MOD_TILE_VER_GFX9:
|
| 846 | rb = ilog2(adev->gfx.config.gb_addr_config_fields.num_se) +
|
| 847 | ilog2(adev->gfx.config.gb_addr_config_fields.num_rb_per_se);
|
| 848 | pipe_xor_bits = min(block_size_bits - 8, pipes +
|
| 849 | ilog2(adev->gfx.config.gb_addr_config_fields.num_se));
|
| 850 | bank_xor_bits = min(block_size_bits - 8 - pipe_xor_bits,
|
| 851 | ilog2(adev->gfx.config.gb_addr_config_fields.num_banks));
|
| 852 | break;
|
| 853 | }
|
| 854 | }
|
| 855 |
|
| 856 | modifier = AMD_FMT_MOD |
|
| 857 | AMD_FMT_MOD_SET(TILE, AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE)) |
|
| 858 | AMD_FMT_MOD_SET(TILE_VERSION, version) |
|
| 859 | AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) |
|
| 860 | AMD_FMT_MOD_SET(BANK_XOR_BITS, bank_xor_bits) |
|
| 861 | AMD_FMT_MOD_SET(PACKERS, packers);
|
| 862 |
|
| 863 | if (dcc_offset != 0) {
|
| 864 | bool dcc_i64b = AMDGPU_TILING_GET(afb->tiling_flags, DCC_INDEPENDENT_64B) != 0;
|
| 865 | bool dcc_i128b = version >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS;
|
| 866 | const struct drm_format_info *format_info;
|
| 867 | u64 render_dcc_offset;
|
| 868 |
|
| 869 |
|
| 870 | bool dcc_constant_encode =
|
| 871 | (adev->asic_type > CHIP_RAVEN ||
|
| 872 | (adev->asic_type == CHIP_RAVEN &&
|
| 873 | adev->external_rev_id >= 0x81)) &&
|
| 874 | amdgpu_ip_version(adev, GC_HWIP, 0) <
|
| 875 | IP_VERSION(11, 0, 0);
|
| 876 |
|
| 877 | int max_cblock_size = dcc_i64b ? AMD_FMT_MOD_DCC_BLOCK_64B :
|
| 878 | dcc_i128b ? AMD_FMT_MOD_DCC_BLOCK_128B :
|
| 879 | AMD_FMT_MOD_DCC_BLOCK_256B;
|
| 880 |
|
| 881 | modifier |= AMD_FMT_MOD_SET(DCC, 1) |
|
| 882 | AMD_FMT_MOD_SET(DCC_CONSTANT_ENCODE, dcc_constant_encode) |
|
| 883 | AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, dcc_i64b) |
|
| 884 | AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, dcc_i128b) |
|
| 885 | AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, max_cblock_size);
|
| 886 |
|
| 887 | afb->base.offsets[1] = dcc_offset * 256 + afb->base.offsets[0];
|
| 888 | afb->base.pitches[1] =
|
| 889 | AMDGPU_TILING_GET(afb->tiling_flags, DCC_PITCH_MAX) + 1;
|
| 890 |
|
| 891 | |
| 892 | * If the userspace driver uses retiling the tiling flags do not contain
|
| 893 | * info on the renderable DCC buffer. Luckily the opaque metadata contains
|
| 894 | * the info so we can try to extract it. The kernel does not use this info
|
| 895 | * but we should convert it to a modifier plane for getfb2, so the
|
| 896 | * userspace driver that gets it doesn't have to juggle around another DCC
|
| 897 | * plane internally.
|
| 898 |
|
| 899 | if (extract_render_dcc_offset(adev, afb->base.obj[0],
|
| 900 | &render_dcc_offset) == 0 &&
|
| 901 | render_dcc_offset != 0 &&
|
| 902 | render_dcc_offset != afb->base.offsets[1] &&
|
| 903 | render_dcc_offset < UINT_MAX) {
|
| 904 | uint32_t dcc_block_bits;
|
| 905 |
|
| 906 | modifier |= AMD_FMT_MOD_SET(DCC_RETILE, 1);
|
| 907 | afb->base.offsets[2] = render_dcc_offset;
|
| 908 |
|
| 909 | if (adev->family >= AMDGPU_FAMILY_NV) {
|
| 910 | int extra_pipe = 0;
|
| 911 |
|
| 912 | if ((amdgpu_ip_version(adev, GC_HWIP,
|
| 913 | 0) >=
|
| 914 | IP_VERSION(10, 3, 0)) &&
|
| 915 | pipes == packers && pipes > 1)
|
| 916 | extra_pipe = 1;
|
| 917 |
|
| 918 | dcc_block_bits = max(20, 16 + pipes + extra_pipe);
|
| 919 | } else {
|
| 920 | modifier |= AMD_FMT_MOD_SET(RB, rb) |
|
| 921 | AMD_FMT_MOD_SET(PIPE, pipes);
|
| 922 | dcc_block_bits = max(20, 18 + rb);
|
| 923 | }
|
| 924 |
|
| 925 | dcc_block_bits -= ilog2(afb->base.format->cpp[0]);
|
| 926 | afb->base.pitches[2] = ALIGN(afb->base.width,
|
| 927 | 1u << ((dcc_block_bits + 1) / 2));
|
| 928 | }
|
| 929 | format_info = amdgpu_lookup_format_info(afb->base.format->format,
|
| 930 | modifier);
|
| 931 | if (!format_info)
|
| 932 | return -EINVAL;
|
| 933 |
|
| 934 | afb->base.format = format_info;
|
| 935 | }
|
| 936 | }
|
| 937 |
|
| 938 | afb->base.modifier = modifier;
|
| 939 | afb->base.flags |= DRM_MODE_FB_MODIFIERS;
|
| 940 | return 0;
|
| 941 | }
|
| 942 |
|
| 943 | static int convert_tiling_flags_to_modifier_gfx6(struct amdgpu_framebuffer *afb)
|
| 944 | {
|
| 945 | const uint32_t array_mode = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE);
|
| 946 | const uint32_t pipe_config = AMDGPU_TILING_GET(afb->tiling_flags, PIPE_CONFIG);
|
| 947 | const uint32_t tile_split = AMDGPU_TILING_GET(afb->tiling_flags, TILE_SPLIT);
|
| 948 | const uint32_t micro_tile_mode = AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE);
|
| 949 | const uint32_t bank_width = AMDGPU_TILING_GET(afb->tiling_flags, BANK_WIDTH);
|
| 950 | const uint32_t bank_height = AMDGPU_TILING_GET(afb->tiling_flags, BANK_HEIGHT);
|
| 951 | const uint32_t macro_tile_aspect = AMDGPU_TILING_GET(afb->tiling_flags, MACRO_TILE_ASPECT);
|
| 952 | const uint32_t num_banks = AMDGPU_TILING_GET(afb->tiling_flags, NUM_BANKS);
|
| 953 | struct amdgpu_device *adev = drm_to_adev(afb->base.dev);
|
| 954 | uint64_t modifier = 0;
|
| 955 |
|
| 956 | switch (array_mode) {
|
| 957 | case DC_ARRAY_LINEAR_GENERAL:
|
| 958 | case DC_ARRAY_LINEAR_ALLIGNED:
|
| 959 | modifier = DRM_FORMAT_MOD_LINEAR;
|
| 960 | break;
|
| 961 |
|
| 962 | case DC_ARRAY_2D_TILED_THIN1:
|
| 963 |
|
| 964 | modifier |=
|
| 965 | AMD_FMT_MOD_SET(PIPE_CONFIG, pipe_config) |
|
| 966 | AMD_FMT_MOD_SET(TILE_SPLIT, tile_split) |
|
| 967 | AMD_FMT_MOD_SET(BANK_WIDTH, bank_width) |
|
| 968 | AMD_FMT_MOD_SET(BANK_HEIGHT, bank_height) |
|
| 969 | AMD_FMT_MOD_SET(MACRO_TILE_ASPECT, macro_tile_aspect) |
|
| 970 | AMD_FMT_MOD_SET(NUM_BANKS, num_banks);
|
| 971 | fallthrough;
|
| 972 |
|
| 973 | case DC_ARRAY_1D_TILED_THIN1:
|
| 974 |
|
| 975 | modifier |=
|
| 976 | AMD_FMT_MOD |
|
| 977 | AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX6) |
|
| 978 | AMD_FMT_MOD_SET(TILE, array_mode) |
|
| 979 | AMD_FMT_MOD_SET(MICROTILE, micro_tile_mode);
|
| 980 | break;
|
| 981 |
|
| 982 | default:
|
| 983 | drm_dbg_kms(&adev->ddev, "array mode 0x%x not supported\n", array_mode);
|
| 984 | return -EINVAL;
|
| 985 | }
|
| 986 |
|
| 987 | afb->base.modifier = modifier;
|
| 988 | afb->base.flags |= DRM_MODE_FB_MODIFIERS;
|
| 989 | return 0;
|
| 990 | }
|
| 991 |
|
| 992 | static int check_tiling_flags_gfx6(struct amdgpu_framebuffer *afb)
|
| 993 | {
|
| 994 | const u64 array_mode = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE);
|
| 995 | const u64 micro_tile_mode = AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE);
|
| 996 |
|
| 997 |
|
| 998 | if (array_mode == 0 || array_mode == 1)
|
| 999 | return 0;
|
| 1000 |
|
| 1001 |
|
| 1002 | if (array_mode != 2 && array_mode != 4) {
|
| 1003 | drm_dbg_kms(afb->base.dev,
|
| 1004 | "Array mode %llu not supported for scanout\n",
|
| 1005 | array_mode);
|
| 1006 | return -EINVAL;
|
| 1007 | }
|
| 1008 |
|
| 1009 | |
| 1010 | * For now, only allow DISPLAY micro tile mode.
|
| 1011 | * THIN, DEPTH and THICK modes are not displayable.
|
| 1012 | * ROTATED has never been supported by Linux or Mesa.
|
| 1013 |
|
| 1014 | if (micro_tile_mode != 0) {
|
| 1015 | drm_dbg_kms(afb->base.dev,
|
| 1016 | "Micro tile mode %llu not supported for scanout\n",
|
| 1017 | micro_tile_mode);
|
| 1018 | return -EINVAL;
|
| 1019 | }
|
| 1020 |
|
| 1021 | return 0;
|
| 1022 | }
|
| 1023 |
|
| 1024 | static void get_block_dimensions(unsigned int block_log2, unsigned int cpp,
|
| 1025 | unsigned int *width, unsigned int *height)
|
| 1026 | {
|
| 1027 | unsigned int cpp_log2 = ilog2(cpp);
|
| 1028 | unsigned int pixel_log2 = block_log2 - cpp_log2;
|
| 1029 | unsigned int width_log2 = (pixel_log2 + 1) / 2;
|
| 1030 | unsigned int height_log2 = pixel_log2 - width_log2;
|
| 1031 |
|
| 1032 | *width = 1 << width_log2;
|
| 1033 | *height = 1 << height_log2;
|
| 1034 | }
|
| 1035 |
|
| 1036 | static unsigned int get_dcc_block_size(uint64_t modifier, bool rb_aligned,
|
| 1037 | bool pipe_aligned)
|
| 1038 | {
|
| 1039 | unsigned int ver = AMD_FMT_MOD_GET(TILE_VERSION, modifier);
|
| 1040 |
|
| 1041 | switch (ver) {
|
| 1042 | case AMD_FMT_MOD_TILE_VER_GFX9: {
|
| 1043 | |
| 1044 | * TODO: for pipe aligned we may need to check the alignment of the
|
| 1045 | * total size of the surface, which may need to be bigger than the
|
| 1046 | * natural alignment due to some HW workarounds
|
| 1047 |
|
| 1048 | return max(10 + (rb_aligned ? (int)AMD_FMT_MOD_GET(RB, modifier) : 0), 12);
|
| 1049 | }
|
| 1050 | case AMD_FMT_MOD_TILE_VER_GFX10:
|
| 1051 | case AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS:
|
| 1052 | case AMD_FMT_MOD_TILE_VER_GFX11: {
|
| 1053 | int pipes_log2 = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier);
|
| 1054 |
|
| 1055 | if (ver >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS && pipes_log2 > 1 &&
|
| 1056 | AMD_FMT_MOD_GET(PACKERS, modifier) == pipes_log2)
|
| 1057 | ++pipes_log2;
|
| 1058 |
|
| 1059 | return max(8 + (pipe_aligned ? pipes_log2 : 0), 12);
|
| 1060 | }
|
| 1061 | default:
|
| 1062 | return 0;
|
| 1063 | }
|
| 1064 | }
|
| 1065 |
|
| 1066 | static int amdgpu_display_verify_plane(struct amdgpu_framebuffer *rfb, int plane,
|
| 1067 | const struct drm_format_info *format,
|
| 1068 | unsigned int block_width, unsigned int block_height,
|
| 1069 | unsigned int block_size_log2)
|
| 1070 | {
|
| 1071 | unsigned int width = rfb->base.width /
|
| 1072 | ((plane && plane < format->num_planes) ? format->hsub : 1);
|
| 1073 | unsigned int height = rfb->base.height /
|
| 1074 | ((plane && plane < format->num_planes) ? format->vsub : 1);
|
| 1075 | unsigned int cpp = plane < format->num_planes ? format->cpp[plane] : 1;
|
| 1076 | unsigned int block_pitch = block_width * cpp;
|
| 1077 | unsigned int min_pitch = ALIGN(width * cpp, block_pitch);
|
| 1078 | unsigned int block_size = 1 << block_size_log2;
|
| 1079 | uint64_t size;
|
| 1080 |
|
| 1081 | if (rfb->base.pitches[plane] % block_pitch) {
|
| 1082 | drm_dbg_kms(rfb->base.dev,
|
| 1083 | "pitch %d for plane %d is not a multiple of block pitch %d\n",
|
| 1084 | rfb->base.pitches[plane], plane, block_pitch);
|
| 1085 | return -EINVAL;
|
| 1086 | }
|
| 1087 | if (rfb->base.pitches[plane] < min_pitch) {
|
| 1088 | drm_dbg_kms(rfb->base.dev,
|
| 1089 | "pitch %d for plane %d is less than minimum pitch %d\n",
|
| 1090 | rfb->base.pitches[plane], plane, min_pitch);
|
| 1091 | return -EINVAL;
|
| 1092 | }
|
| 1093 |
|
| 1094 |
|
| 1095 | if (rfb->base.offsets[plane] % block_size) {
|
| 1096 | drm_dbg_kms(rfb->base.dev,
|
| 1097 | "offset 0x%x for plane %d is not a multiple of block pitch 0x%x\n",
|
| 1098 | rfb->base.offsets[plane], plane, block_size);
|
| 1099 | return -EINVAL;
|
| 1100 | }
|
| 1101 |
|
| 1102 | size = rfb->base.offsets[plane] +
|
| 1103 | (uint64_t)rfb->base.pitches[plane] / block_pitch *
|
| 1104 | block_size * DIV_ROUND_UP(height, block_height);
|
| 1105 |
|
| 1106 | if (rfb->base.obj[0]->size < size) {
|
| 1107 | drm_dbg_kms(rfb->base.dev,
|
| 1108 | "BO size 0x%zx is less than 0x%llx required for plane %d\n",
|
| 1109 | rfb->base.obj[0]->size, size, plane);
|
| 1110 | return -EINVAL;
|
| 1111 | }
|
| 1112 |
|
| 1113 | return 0;
|
| 1114 | }
|
| 1115 |
|
| 1116 | static int amdgpu_display_verify_sizes_gfx6(struct drm_device *dev,
|
| 1117 | const u64 modifier,
|
| 1118 | unsigned int *out_block_width,
|
| 1119 | unsigned int *out_block_height)
|
| 1120 | {
|
| 1121 | const u32 display_micro_tile_pitch = 32;
|
| 1122 | const u32 micro_tile_width = 8;
|
| 1123 | const u32 micro_tile_height = 8;
|
| 1124 | const u32 micro_tile_mode = AMD_FMT_MOD_GET(MICROTILE, modifier);
|
| 1125 | const u32 array_mode = AMD_FMT_MOD_GET(TILE, modifier);
|
| 1126 | u32 num_banks, bank_width, bank_height, pipe_config, macro_tile_aspect;
|
| 1127 | u32 num_pipes;
|
| 1128 |
|
| 1129 | if (AMD_FMT_MOD_GET(DCC, modifier)) {
|
| 1130 | drm_dbg_kms(dev, "DCC is not displayable on GFX6-8\n");
|
| 1131 | return -EINVAL;
|
| 1132 | }
|
| 1133 | if (array_mode != AMD_FMT_MOD_TILE_GFX6_1D_TILED_THIN1 &&
|
| 1134 | array_mode != AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) {
|
| 1135 | drm_dbg_kms(dev, "Array mode %u not supported for scanout\n", array_mode);
|
| 1136 | return -EINVAL;
|
| 1137 | }
|
| 1138 | if (micro_tile_mode != AMD_FMT_MOD_MICROTILE_DISPLAY) {
|
| 1139 | drm_dbg_kms(dev, "Microtile mode %u not supported for scanout\n", micro_tile_mode);
|
| 1140 | return -EINVAL;
|
| 1141 | }
|
| 1142 |
|
| 1143 | num_banks = 2 << AMD_FMT_MOD_GET(NUM_BANKS, modifier);
|
| 1144 | bank_width = 1 << AMD_FMT_MOD_GET(BANK_WIDTH, modifier);
|
| 1145 | bank_height = 1 << AMD_FMT_MOD_GET(BANK_HEIGHT, modifier);
|
| 1146 | pipe_config = AMD_FMT_MOD_GET(PIPE_CONFIG, modifier);
|
| 1147 | macro_tile_aspect = 1 << AMD_FMT_MOD_GET(MACRO_TILE_ASPECT, modifier);
|
| 1148 |
|
| 1149 | switch (pipe_config) {
|
| 1150 | case AMD_FMT_MOD_PIPE_CONFIG_P16_32x32_8x16:
|
| 1151 | case AMD_FMT_MOD_PIPE_CONFIG_P16_32x32_16x16:
|
| 1152 | num_pipes = 16;
|
| 1153 | break;
|
| 1154 |
|
| 1155 | case AMD_FMT_MOD_PIPE_CONFIG_P8_16x16_8x16:
|
| 1156 | case AMD_FMT_MOD_PIPE_CONFIG_P8_16x32_8x16:
|
| 1157 | case AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_8x16:
|
| 1158 | case AMD_FMT_MOD_PIPE_CONFIG_P8_16x32_16x16:
|
| 1159 | case AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_16x16:
|
| 1160 | case AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_16x32:
|
| 1161 | case AMD_FMT_MOD_PIPE_CONFIG_P8_32x64_32x32:
|
| 1162 | num_pipes = 8;
|
| 1163 | break;
|
| 1164 |
|
| 1165 | case AMD_FMT_MOD_PIPE_CONFIG_P4_8x16:
|
| 1166 | case AMD_FMT_MOD_PIPE_CONFIG_P4_16x16:
|
| 1167 | case AMD_FMT_MOD_PIPE_CONFIG_P4_16x32:
|
| 1168 | case AMD_FMT_MOD_PIPE_CONFIG_P4_32x32:
|
| 1169 | num_pipes = 4;
|
| 1170 | break;
|
| 1171 |
|
| 1172 | case AMD_FMT_MOD_PIPE_CONFIG_P2:
|
| 1173 | num_pipes = 2;
|
| 1174 | break;
|
| 1175 |
|
| 1176 | default:
|
| 1177 | drm_dbg_kms(dev, "Pipe config %u invalid\n", pipe_config);
|
| 1178 | return -EINVAL;
|
| 1179 | }
|
| 1180 |
|
| 1181 |
|
| 1182 | if (array_mode < AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) {
|
| 1183 | *out_block_width = display_micro_tile_pitch;
|
| 1184 | *out_block_height = micro_tile_height;
|
| 1185 | } else {
|
| 1186 |
|
| 1187 | *out_block_width = num_pipes * micro_tile_width *
|
| 1188 | bank_width * macro_tile_aspect;
|
| 1189 | *out_block_height = micro_tile_height * bank_height *
|
| 1190 | num_banks / macro_tile_aspect;
|
| 1191 | }
|
| 1192 |
|
| 1193 | return 0;
|
| 1194 | }
|
| 1195 |
|
| 1196 | static int amdgpu_display_verify_sizes(struct amdgpu_framebuffer *rfb)
|
| 1197 | {
|
| 1198 | const struct drm_format_info *format_info = drm_format_info(rfb->base.format->format);
|
| 1199 | uint64_t modifier = rfb->base.modifier;
|
| 1200 | int ret;
|
| 1201 | unsigned int i, block_width, block_height, block_size_log2;
|
| 1202 |
|
| 1203 | if (rfb->base.dev->mode_config.fb_modifiers_not_supported)
|
| 1204 | return 0;
|
| 1205 |
|
| 1206 | for (i = 0; i < format_info->num_planes; ++i) {
|
| 1207 | if (modifier == DRM_FORMAT_MOD_LINEAR) {
|
| 1208 | block_width = 256 / format_info->cpp[i];
|
| 1209 | block_height = 1;
|
| 1210 | block_size_log2 = 8;
|
| 1211 | } else if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) >= AMD_FMT_MOD_TILE_VER_GFX12) {
|
| 1212 | int swizzle = AMD_FMT_MOD_GET(TILE, modifier);
|
| 1213 |
|
| 1214 | switch (swizzle) {
|
| 1215 | case AMD_FMT_MOD_TILE_GFX12_256B_2D:
|
| 1216 | block_size_log2 = 8;
|
| 1217 | break;
|
| 1218 | case AMD_FMT_MOD_TILE_GFX12_4K_2D:
|
| 1219 | block_size_log2 = 12;
|
| 1220 | break;
|
| 1221 | case AMD_FMT_MOD_TILE_GFX12_64K_2D:
|
| 1222 | block_size_log2 = 16;
|
| 1223 | break;
|
| 1224 | case AMD_FMT_MOD_TILE_GFX12_256K_2D:
|
| 1225 | block_size_log2 = 18;
|
| 1226 | break;
|
| 1227 | default:
|
| 1228 | drm_dbg_kms(rfb->base.dev,
|
| 1229 | "Gfx12 swizzle mode with unknown block size: %d\n", swizzle);
|
| 1230 | return -EINVAL;
|
| 1231 | }
|
| 1232 |
|
| 1233 | get_block_dimensions(block_size_log2, format_info->cpp[i],
|
| 1234 | &block_width, &block_height);
|
| 1235 | } else if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) >= AMD_FMT_MOD_TILE_VER_GFX9) {
|
| 1236 | int swizzle = AMD_FMT_MOD_GET(TILE, modifier);
|
| 1237 |
|
| 1238 | switch ((swizzle & ~3) + 1) {
|
| 1239 | case DC_SW_256B_S:
|
| 1240 | block_size_log2 = 8;
|
| 1241 | break;
|
| 1242 | case DC_SW_4KB_S:
|
| 1243 | case DC_SW_4KB_S_X:
|
| 1244 | block_size_log2 = 12;
|
| 1245 | break;
|
| 1246 | case DC_SW_64KB_S:
|
| 1247 | case DC_SW_64KB_S_T:
|
| 1248 | case DC_SW_64KB_S_X:
|
| 1249 | block_size_log2 = 16;
|
| 1250 | break;
|
| 1251 | case DC_SW_VAR_S_X:
|
| 1252 | block_size_log2 = 18;
|
| 1253 | break;
|
| 1254 | default:
|
| 1255 | drm_dbg_kms(rfb->base.dev,
|
| 1256 | "Swizzle mode with unknown block size: %d\n", swizzle);
|
| 1257 | return -EINVAL;
|
| 1258 | }
|
| 1259 |
|
| 1260 | get_block_dimensions(block_size_log2, format_info->cpp[i],
|
| 1261 | &block_width, &block_height);
|
| 1262 | } else if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) == AMD_FMT_MOD_TILE_VER_GFX6) {
|
| 1263 | ret = amdgpu_display_verify_sizes_gfx6(rfb->base.dev, modifier,
|
| 1264 | &block_width, &block_height);
|
| 1265 | if (ret)
|
| 1266 | return ret;
|
| 1267 |
|
| 1268 |
|
| 1269 | WARN_ON(!is_power_of_2(block_width));
|
| 1270 | WARN_ON(!is_power_of_2(block_height));
|
| 1271 | WARN_ON(!is_power_of_2(format_info->cpp[i]));
|
| 1272 |
|
| 1273 | block_size_log2 = ilog2(block_width * block_height * format_info->cpp[i]);
|
| 1274 | }
|
| 1275 |
|
| 1276 | ret = amdgpu_display_verify_plane(rfb, i, format_info,
|
| 1277 | block_width, block_height, block_size_log2);
|
| 1278 | if (ret)
|
| 1279 | return ret;
|
| 1280 | }
|
| 1281 |
|
| 1282 | if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) <= AMD_FMT_MOD_TILE_VER_GFX11 &&
|
| 1283 | AMD_FMT_MOD_GET(DCC, modifier)) {
|
| 1284 | if (AMD_FMT_MOD_GET(DCC_RETILE, modifier)) {
|
| 1285 | block_size_log2 = get_dcc_block_size(modifier, false, false);
|
| 1286 | get_block_dimensions(block_size_log2 + 8, format_info->cpp[0],
|
| 1287 | &block_width, &block_height);
|
| 1288 | ret = amdgpu_display_verify_plane(rfb, i, format_info,
|
| 1289 | block_width, block_height,
|
| 1290 | block_size_log2);
|
| 1291 | if (ret)
|
| 1292 | return ret;
|
| 1293 |
|
| 1294 | ++i;
|
| 1295 | block_size_log2 = get_dcc_block_size(modifier, true, true);
|
| 1296 | } else {
|
| 1297 | bool pipe_aligned = AMD_FMT_MOD_GET(DCC_PIPE_ALIGN, modifier);
|
| 1298 |
|
| 1299 | block_size_log2 = get_dcc_block_size(modifier, true, pipe_aligned);
|
| 1300 | }
|
| 1301 | get_block_dimensions(block_size_log2 + 8, format_info->cpp[0],
|
| 1302 | &block_width, &block_height);
|
| 1303 | ret = amdgpu_display_verify_plane(rfb, i, format_info,
|
| 1304 | block_width, block_height, block_size_log2);
|
| 1305 | if (ret)
|
| 1306 | return ret;
|
| 1307 | }
|
| 1308 |
|
| 1309 | return 0;
|
| 1310 | }
|
| 1311 |
|
| 1312 | static int amdgpu_display_get_fb_info(const struct amdgpu_framebuffer *amdgpu_fb,
|
| 1313 | uint64_t *tiling_flags, bool *tmz_surface,
|
| 1314 | bool *gfx12_dcc)
|
| 1315 | {
|
| 1316 | struct amdgpu_bo *rbo;
|
| 1317 | int r;
|
| 1318 |
|
| 1319 | if (!amdgpu_fb) {
|
| 1320 | *tiling_flags = 0;
|
| 1321 | *tmz_surface = false;
|
| 1322 | *gfx12_dcc = false;
|
| 1323 | return 0;
|
| 1324 | }
|
| 1325 |
|
| 1326 | rbo = gem_to_amdgpu_bo(amdgpu_fb->base.obj[0]);
|
| 1327 | r = amdgpu_bo_reserve(rbo, false);
|
| 1328 |
|
| 1329 | if (unlikely(r)) {
|
| 1330 |
|
| 1331 | if (r != -ERESTARTSYS)
|
| 1332 | DRM_ERROR("Unable to reserve buffer: %d\n", r);
|
| 1333 | return r;
|
| 1334 | }
|
| 1335 |
|
| 1336 | amdgpu_bo_get_tiling_flags(rbo, tiling_flags);
|
| 1337 | *tmz_surface = amdgpu_bo_encrypted(rbo);
|
| 1338 | *gfx12_dcc = rbo->flags & AMDGPU_GEM_CREATE_GFX12_DCC;
|
| 1339 |
|
| 1340 | amdgpu_bo_unreserve(rbo);
|
| 1341 |
|
| 1342 | return r;
|
| 1343 | }
|
| 1344 |
|
| 1345 | static int amdgpu_display_gem_fb_verify_and_init(struct drm_device *dev,
|
| 1346 | struct amdgpu_framebuffer *rfb,
|
| 1347 | struct drm_file *file_priv,
|
| 1348 | const struct drm_format_info *info,
|
| 1349 | const struct drm_mode_fb_cmd2 *mode_cmd,
|
| 1350 | struct drm_gem_object *obj)
|
| 1351 | {
|
| 1352 | int ret;
|
| 1353 |
|
| 1354 | rfb->base.obj[0] = obj;
|
| 1355 | drm_helper_mode_fill_fb_struct(dev, &rfb->base, info, mode_cmd);
|
| 1356 |
|
| 1357 | if (!drm_any_plane_has_format(dev, mode_cmd->pixel_format,
|
| 1358 | mode_cmd->modifier[0])) {
|
| 1359 | drm_dbg_kms(dev,
|
| 1360 | "unsupported pixel format %p4cc / modifier 0x%llx\n",
|
| 1361 | &mode_cmd->pixel_format, mode_cmd->modifier[0]);
|
| 1362 |
|
| 1363 | ret = -EINVAL;
|
| 1364 | goto err;
|
| 1365 | }
|
| 1366 |
|
| 1367 | ret = amdgpu_display_framebuffer_init(dev, rfb, mode_cmd, obj);
|
| 1368 | if (ret)
|
| 1369 | goto err;
|
| 1370 |
|
| 1371 | if (drm_drv_uses_atomic_modeset(dev))
|
| 1372 | ret = drm_framebuffer_init(dev, &rfb->base,
|
| 1373 | &amdgpu_fb_funcs_atomic);
|
| 1374 | else
|
| 1375 | ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs);
|
| 1376 |
|
| 1377 | if (ret)
|
| 1378 | goto err;
|
| 1379 |
|
| 1380 | return 0;
|
| 1381 | err:
|
| 1382 | drm_dbg_kms(dev, "Failed to verify and init gem fb: %d\n", ret);
|
| 1383 | rfb->base.obj[0] = NULL;
|
| 1384 | return ret;
|
| 1385 | }
|
| 1386 |
|
| 1387 | static int amdgpu_display_framebuffer_init(struct drm_device *dev,
|
| 1388 | struct amdgpu_framebuffer *rfb,
|
| 1389 | const struct drm_mode_fb_cmd2 *mode_cmd,
|
| 1390 | struct drm_gem_object *obj)
|
| 1391 | {
|
| 1392 | struct amdgpu_device *adev = drm_to_adev(dev);
|
| 1393 | int ret, i;
|
| 1394 |
|
| 1395 | |
| 1396 | * This needs to happen before modifier conversion as that might change
|
| 1397 | * the number of planes.
|
| 1398 |
|
| 1399 | for (i = 1; i < rfb->base.format->num_planes; ++i) {
|
| 1400 | if (mode_cmd->handles[i] != mode_cmd->handles[0]) {
|
| 1401 | drm_dbg_kms(dev, "Plane 0 and %d have different BOs: %u vs. %u\n",
|
| 1402 | i, mode_cmd->handles[0], mode_cmd->handles[i]);
|
| 1403 | ret = -EINVAL;
|
| 1404 | return ret;
|
| 1405 | }
|
| 1406 | }
|
| 1407 |
|
| 1408 | ret = amdgpu_display_get_fb_info(rfb, &rfb->tiling_flags, &rfb->tmz_surface,
|
| 1409 | &rfb->gfx12_dcc);
|
| 1410 | if (ret)
|
| 1411 | return ret;
|
| 1412 |
|
| 1413 | if (dev->mode_config.fb_modifiers_not_supported && !adev->enable_virtual_display) {
|
| 1414 | drm_WARN_ONCE(dev, adev->family >= AMDGPU_FAMILY_AI,
|
| 1415 | "GFX9+ requires FB check based on format modifier\n");
|
| 1416 | ret = check_tiling_flags_gfx6(rfb);
|
| 1417 | if (ret)
|
| 1418 | return ret;
|
| 1419 | }
|
| 1420 |
|
| 1421 | if (!dev->mode_config.fb_modifiers_not_supported &&
|
| 1422 | !(rfb->base.flags & DRM_MODE_FB_MODIFIERS)) {
|
| 1423 | if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(12, 0, 0))
|
| 1424 | ret = convert_tiling_flags_to_modifier_gfx12(rfb);
|
| 1425 | else if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(9, 0, 0))
|
| 1426 | ret = convert_tiling_flags_to_modifier_gfx9(rfb);
|
| 1427 | else
|
| 1428 | ret = convert_tiling_flags_to_modifier_gfx6(rfb);
|
| 1429 |
|
| 1430 | if (ret) {
|
| 1431 | drm_dbg_kms(dev, "Failed to convert tiling flags 0x%llX to a modifier",
|
| 1432 | rfb->tiling_flags);
|
| 1433 | return ret;
|
| 1434 | }
|
| 1435 | }
|
| 1436 |
|
| 1437 | ret = amdgpu_display_verify_sizes(rfb);
|
| 1438 | if (ret)
|
| 1439 | return ret;
|
| 1440 |
|
| 1441 | for (i = 0; i < rfb->base.format->num_planes; ++i) {
|
| 1442 | drm_gem_object_get(rfb->base.obj[0]);
|
| 1443 | rfb->base.obj[i] = rfb->base.obj[0];
|
| 1444 | }
|
| 1445 |
|
| 1446 | return 0;
|
| 1447 | }
|
| 1448 |
|
| 1449 | struct drm_framebuffer *
|
| 1450 | amdgpu_display_user_framebuffer_create(struct drm_device *dev,
|
| 1451 | struct drm_file *file_priv,
|
| 1452 | const struct drm_format_info *info,
|
| 1453 | const struct drm_mode_fb_cmd2 *mode_cmd)
|
| 1454 | {
|
| 1455 | struct amdgpu_framebuffer *amdgpu_fb;
|
| 1456 | struct drm_gem_object *obj;
|
| 1457 | struct amdgpu_bo *bo;
|
| 1458 | uint32_t domains;
|
| 1459 | int ret;
|
| 1460 |
|
| 1461 | obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
|
| 1462 | if (obj == NULL) {
|
| 1463 | drm_dbg_kms(dev,
|
| 1464 | "No GEM object associated to handle 0x%08X, can't create framebuffer\n",
|
| 1465 | mode_cmd->handles[0]);
|
| 1466 |
|
| 1467 | return ERR_PTR(-ENOENT);
|
| 1468 | }
|
| 1469 |
|
| 1470 |
|
| 1471 | bo = gem_to_amdgpu_bo(obj);
|
| 1472 | domains = amdgpu_display_supported_domains(drm_to_adev(dev), bo->flags);
|
| 1473 | if (drm_gem_is_imported(obj) && !(domains & AMDGPU_GEM_DOMAIN_GTT)) {
|
| 1474 | drm_dbg_kms(dev, "Cannot create framebuffer from imported dma_buf\n");
|
| 1475 | drm_gem_object_put(obj);
|
| 1476 | return ERR_PTR(-EINVAL);
|
| 1477 | }
|
| 1478 |
|
| 1479 | amdgpu_fb = kzalloc_obj(*amdgpu_fb);
|
| 1480 | if (amdgpu_fb == NULL) {
|
| 1481 | drm_gem_object_put(obj);
|
| 1482 | return ERR_PTR(-ENOMEM);
|
| 1483 | }
|
| 1484 |
|
| 1485 | ret = amdgpu_display_gem_fb_verify_and_init(dev, amdgpu_fb, file_priv,
|
| 1486 | info, mode_cmd, obj);
|
| 1487 | if (ret) {
|
| 1488 | kfree(amdgpu_fb);
|
| 1489 | drm_gem_object_put(obj);
|
| 1490 | return ERR_PTR(ret);
|
| 1491 | }
|
| 1492 |
|
| 1493 | drm_gem_object_put(obj);
|
| 1494 | return &amdgpu_fb->base;
|
| 1495 | }
|
| 1496 |
|
| 1497 | const struct drm_mode_config_funcs amdgpu_mode_funcs = {
|
| 1498 | .fb_create = amdgpu_display_user_framebuffer_create,
|
| 1499 | };
|
| 1500 |
|
| 1501 | static const struct drm_prop_enum_list amdgpu_underscan_enum_list[] = {
|
| 1502 | { UNDERSCAN_OFF, "off" },
|
| 1503 | { UNDERSCAN_ON, "on" },
|
| 1504 | { UNDERSCAN_AUTO, "auto" },
|
| 1505 | };
|
| 1506 |
|
| 1507 | static const struct drm_prop_enum_list amdgpu_audio_enum_list[] = {
|
| 1508 | { AMDGPU_AUDIO_DISABLE, "off" },
|
| 1509 | { AMDGPU_AUDIO_ENABLE, "on" },
|
| 1510 | { AMDGPU_AUDIO_AUTO, "auto" },
|
| 1511 | };
|
| 1512 |
|
| 1513 |
|
| 1514 | static const struct drm_prop_enum_list amdgpu_dither_enum_list[] = {
|
| 1515 | { AMDGPU_FMT_DITHER_DISABLE, "off" },
|
| 1516 | { AMDGPU_FMT_DITHER_ENABLE, "on" },
|
| 1517 | };
|
| 1518 |
|
| 1519 | |
| 1520 | * DOC: property for adaptive backlight modulation
|
| 1521 | *
|
| 1522 | * The 'adaptive backlight modulation' property is used for the compositor to
|
| 1523 | * directly control the adaptive backlight modulation power savings feature
|
| 1524 | * that is part of DCN hardware.
|
| 1525 | *
|
| 1526 | * The property will be attached specifically to eDP panels that support it.
|
| 1527 | *
|
| 1528 | * The property is by default set to 'sysfs' to allow the sysfs file 'panel_power_savings'
|
| 1529 | * to be able to control it.
|
| 1530 | * If set to 'off' the compositor will ensure it stays off.
|
| 1531 | * The other values 'min', 'bias min', 'bias max', and 'max' will control the
|
| 1532 | * intensity of the power savings.
|
| 1533 | *
|
| 1534 | * Modifying this value can have implications on color accuracy, so tread
|
| 1535 | * carefully.
|
| 1536 |
|
| 1537 | static int amdgpu_display_setup_abm_prop(struct amdgpu_device *adev)
|
| 1538 | {
|
| 1539 | const struct drm_prop_enum_list props[] = {
|
| 1540 | { ABM_SYSFS_CONTROL, "sysfs" },
|
| 1541 | { ABM_LEVEL_OFF, "off" },
|
| 1542 | { ABM_LEVEL_MIN, "min" },
|
| 1543 | { ABM_LEVEL_BIAS_MIN, "bias min" },
|
| 1544 | { ABM_LEVEL_BIAS_MAX, "bias max" },
|
| 1545 | { ABM_LEVEL_MAX, "max" },
|
| 1546 | };
|
| 1547 | struct drm_property *prop;
|
| 1548 | int i;
|
| 1549 |
|
| 1550 | if (!adev->dc_enabled)
|
| 1551 | return 0;
|
| 1552 |
|
| 1553 | prop = drm_property_create(adev_to_drm(adev), DRM_MODE_PROP_ENUM,
|
| 1554 | "adaptive backlight modulation",
|
| 1555 | 6);
|
| 1556 | if (!prop)
|
| 1557 | return -ENOMEM;
|
| 1558 |
|
| 1559 | for (i = 0; i < ARRAY_SIZE(props); i++) {
|
| 1560 | int ret;
|
| 1561 |
|
| 1562 | ret = drm_property_add_enum(prop, props[i].type,
|
| 1563 | props[i].name);
|
| 1564 |
|
| 1565 | if (ret) {
|
| 1566 | drm_property_destroy(adev_to_drm(adev), prop);
|
| 1567 |
|
| 1568 | return ret;
|
| 1569 | }
|
| 1570 | }
|
| 1571 |
|
| 1572 | adev->mode_info.abm_level_property = prop;
|
| 1573 |
|
| 1574 | return 0;
|
| 1575 | }
|
| 1576 |
|
| 1577 | int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
|
| 1578 | {
|
| 1579 | int sz;
|
| 1580 |
|
| 1581 | adev->mode_info.coherent_mode_property =
|
| 1582 | drm_property_create_range(adev_to_drm(adev), 0, "coherent", 0, 1);
|
| 1583 | if (!adev->mode_info.coherent_mode_property)
|
| 1584 | return -ENOMEM;
|
| 1585 |
|
| 1586 | adev->mode_info.load_detect_property =
|
| 1587 | drm_property_create_range(adev_to_drm(adev), 0, "load detection", 0, 1);
|
| 1588 | if (!adev->mode_info.load_detect_property)
|
| 1589 | return -ENOMEM;
|
| 1590 |
|
| 1591 | drm_mode_create_scaling_mode_property(adev_to_drm(adev));
|
| 1592 |
|
| 1593 | sz = ARRAY_SIZE(amdgpu_underscan_enum_list);
|
| 1594 | adev->mode_info.underscan_property =
|
| 1595 | drm_property_create_enum(adev_to_drm(adev), 0,
|
| 1596 | "underscan",
|
| 1597 | amdgpu_underscan_enum_list, sz);
|
| 1598 |
|
| 1599 | adev->mode_info.underscan_hborder_property =
|
| 1600 | drm_property_create_range(adev_to_drm(adev), 0,
|
| 1601 | "underscan hborder", 0, 128);
|
| 1602 | if (!adev->mode_info.underscan_hborder_property)
|
| 1603 | return -ENOMEM;
|
| 1604 |
|
| 1605 | adev->mode_info.underscan_vborder_property =
|
| 1606 | drm_property_create_range(adev_to_drm(adev), 0,
|
| 1607 | "underscan vborder", 0, 128);
|
| 1608 | if (!adev->mode_info.underscan_vborder_property)
|
| 1609 | return -ENOMEM;
|
| 1610 |
|
| 1611 | sz = ARRAY_SIZE(amdgpu_audio_enum_list);
|
| 1612 | adev->mode_info.audio_property =
|
| 1613 | drm_property_create_enum(adev_to_drm(adev), 0,
|
| 1614 | "audio",
|
| 1615 | amdgpu_audio_enum_list, sz);
|
| 1616 |
|
| 1617 | sz = ARRAY_SIZE(amdgpu_dither_enum_list);
|
| 1618 | adev->mode_info.dither_property =
|
| 1619 | drm_property_create_enum(adev_to_drm(adev), 0,
|
| 1620 | "dither",
|
| 1621 | amdgpu_dither_enum_list, sz);
|
| 1622 |
|
| 1623 | return amdgpu_display_setup_abm_prop(adev);
|
| 1624 | }
|
| 1625 |
|
| 1626 | void amdgpu_display_update_priority(struct amdgpu_device *adev)
|
| 1627 | {
|
| 1628 |
|
| 1629 | if ((amdgpu_disp_priority == 0) || (amdgpu_disp_priority > 2))
|
| 1630 | adev->mode_info.disp_priority = 0;
|
| 1631 | else
|
| 1632 | adev->mode_info.disp_priority = amdgpu_disp_priority;
|
| 1633 |
|
| 1634 | }
|
| 1635 |
|
| 1636 | static bool amdgpu_display_is_hdtv_mode(const struct drm_display_mode *mode)
|
| 1637 | {
|
| 1638 |
|
| 1639 | if ((mode->vdisplay == 480 && mode->hdisplay == 720) ||
|
| 1640 | (mode->vdisplay == 576) ||
|
| 1641 | (mode->vdisplay == 720) ||
|
| 1642 | (mode->vdisplay == 1080))
|
| 1643 | return true;
|
| 1644 | else
|
| 1645 | return false;
|
| 1646 | }
|
| 1647 |
|
| 1648 | bool amdgpu_display_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
|
| 1649 | const struct drm_display_mode *mode,
|
| 1650 | struct drm_display_mode *adjusted_mode)
|
| 1651 | {
|
| 1652 | struct drm_device *dev = crtc->dev;
|
| 1653 | struct drm_encoder *encoder;
|
| 1654 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
| 1655 | struct amdgpu_encoder *amdgpu_encoder;
|
| 1656 | struct drm_connector *connector;
|
| 1657 | u32 src_v = 1, dst_v = 1;
|
| 1658 | u32 src_h = 1, dst_h = 1;
|
| 1659 |
|
| 1660 | amdgpu_crtc->h_border = 0;
|
| 1661 | amdgpu_crtc->v_border = 0;
|
| 1662 |
|
| 1663 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
| 1664 | if (encoder->crtc != crtc)
|
| 1665 | continue;
|
| 1666 | amdgpu_encoder = to_amdgpu_encoder(encoder);
|
| 1667 | connector = amdgpu_get_connector_for_encoder(encoder);
|
| 1668 |
|
| 1669 |
|
| 1670 | if (amdgpu_encoder->rmx_type == RMX_OFF)
|
| 1671 | amdgpu_crtc->rmx_type = RMX_OFF;
|
| 1672 | else if (mode->hdisplay < amdgpu_encoder->native_mode.hdisplay ||
|
| 1673 | mode->vdisplay < amdgpu_encoder->native_mode.vdisplay)
|
| 1674 | amdgpu_crtc->rmx_type = amdgpu_encoder->rmx_type;
|
| 1675 | else
|
| 1676 | amdgpu_crtc->rmx_type = RMX_OFF;
|
| 1677 |
|
| 1678 | memcpy(&amdgpu_crtc->native_mode,
|
| 1679 | &amdgpu_encoder->native_mode,
|
| 1680 | sizeof(struct drm_display_mode));
|
| 1681 | src_v = crtc->mode.vdisplay;
|
| 1682 | dst_v = amdgpu_crtc->native_mode.vdisplay;
|
| 1683 | src_h = crtc->mode.hdisplay;
|
| 1684 | dst_h = amdgpu_crtc->native_mode.hdisplay;
|
| 1685 |
|
| 1686 |
|
| 1687 | if ((!(mode->flags & DRM_MODE_FLAG_INTERLACE)) &&
|
| 1688 | ((amdgpu_encoder->underscan_type == UNDERSCAN_ON) ||
|
| 1689 | ((amdgpu_encoder->underscan_type == UNDERSCAN_AUTO) &&
|
| 1690 | connector && connector->display_info.is_hdmi &&
|
| 1691 | amdgpu_display_is_hdtv_mode(mode)))) {
|
| 1692 | if (amdgpu_encoder->underscan_hborder != 0)
|
| 1693 | amdgpu_crtc->h_border = amdgpu_encoder->underscan_hborder;
|
| 1694 | else
|
| 1695 | amdgpu_crtc->h_border = (mode->hdisplay >> 5) + 16;
|
| 1696 | if (amdgpu_encoder->underscan_vborder != 0)
|
| 1697 | amdgpu_crtc->v_border = amdgpu_encoder->underscan_vborder;
|
| 1698 | else
|
| 1699 | amdgpu_crtc->v_border = (mode->vdisplay >> 5) + 16;
|
| 1700 | amdgpu_crtc->rmx_type = RMX_FULL;
|
| 1701 | src_v = crtc->mode.vdisplay;
|
| 1702 | dst_v = crtc->mode.vdisplay - (amdgpu_crtc->v_border * 2);
|
| 1703 | src_h = crtc->mode.hdisplay;
|
| 1704 | dst_h = crtc->mode.hdisplay - (amdgpu_crtc->h_border * 2);
|
| 1705 | }
|
| 1706 | }
|
| 1707 | if (amdgpu_crtc->rmx_type != RMX_OFF) {
|
| 1708 | fixed20_12 a, b;
|
| 1709 |
|
| 1710 | a.full = dfixed_const(src_v);
|
| 1711 | b.full = dfixed_const(dst_v);
|
| 1712 | amdgpu_crtc->vsc.full = dfixed_div(a, b);
|
| 1713 | a.full = dfixed_const(src_h);
|
| 1714 | b.full = dfixed_const(dst_h);
|
| 1715 | amdgpu_crtc->hsc.full = dfixed_div(a, b);
|
| 1716 | } else {
|
| 1717 | amdgpu_crtc->vsc.full = dfixed_const(1);
|
| 1718 | amdgpu_crtc->hsc.full = dfixed_const(1);
|
| 1719 | }
|
| 1720 | return true;
|
| 1721 | }
|
| 1722 |
|
| 1723 | |
| 1724 | * Retrieve current video scanout position of crtc on a given gpu, and
|
| 1725 | * an optional accurate timestamp of when query happened.
|
| 1726 | *
|
| 1727 | * \param dev Device to query.
|
| 1728 | * \param pipe Crtc to query.
|
| 1729 | * \param flags from caller (DRM_CALLED_FROM_VBLIRQ or 0).
|
| 1730 | * For driver internal use only also supports these flags:
|
| 1731 | *
|
| 1732 | * USE_REAL_VBLANKSTART to use the real start of vblank instead
|
| 1733 | * of a fudged earlier start of vblank.
|
| 1734 | *
|
| 1735 | * GET_DISTANCE_TO_VBLANKSTART to return distance to the
|
| 1736 | * fudged earlier start of vblank in *vpos and the distance
|
| 1737 | * to true start of vblank in *hpos.
|
| 1738 | *
|
| 1739 | * \param *vpos Location where vertical scanout position should be stored.
|
| 1740 | * \param *hpos Location where horizontal scanout position should go.
|
| 1741 | * \param *stime Target location for timestamp taken immediately before
|
| 1742 | * scanout position query. Can be NULL to skip timestamp.
|
| 1743 | * \param *etime Target location for timestamp taken immediately after
|
| 1744 | * scanout position query. Can be NULL to skip timestamp.
|
| 1745 | *
|
| 1746 | * Returns vpos as a positive number while in active scanout area.
|
| 1747 | * Returns vpos as a negative number inside vblank, counting the number
|
| 1748 | * of scanlines to go until end of vblank, e.g., -1 means "one scanline
|
| 1749 | * until start of active scanout / end of vblank."
|
| 1750 | *
|
| 1751 | * \return Flags, or'ed together as follows:
|
| 1752 | *
|
| 1753 | * DRM_SCANOUTPOS_VALID = Query successful.
|
| 1754 | * DRM_SCANOUTPOS_INVBL = Inside vblank.
|
| 1755 | * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of
|
| 1756 | * this flag means that returned position may be offset by a constant but
|
| 1757 | * unknown small number of scanlines wrt. real scanout position.
|
| 1758 | *
|
| 1759 |
|
| 1760 | int amdgpu_display_get_crtc_scanoutpos(struct drm_device *dev,
|
| 1761 | unsigned int pipe, unsigned int flags, int *vpos,
|
| 1762 | int *hpos, ktime_t *stime, ktime_t *etime,
|
| 1763 | const struct drm_display_mode *mode)
|
| 1764 | {
|
| 1765 | u32 vbl = 0, position = 0;
|
| 1766 | int vbl_start, vbl_end, vtotal, ret = 0;
|
| 1767 | bool in_vbl = true;
|
| 1768 |
|
| 1769 | struct amdgpu_device *adev = drm_to_adev(dev);
|
| 1770 |
|
| 1771 |
|
| 1772 |
|
| 1773 |
|
| 1774 | if (stime)
|
| 1775 | *stime = ktime_get();
|
| 1776 |
|
| 1777 | if (amdgpu_display_page_flip_get_scanoutpos(adev, pipe, &vbl, &position) == 0)
|
| 1778 | ret |= DRM_SCANOUTPOS_VALID;
|
| 1779 |
|
| 1780 |
|
| 1781 | if (etime)
|
| 1782 | *etime = ktime_get();
|
| 1783 |
|
| 1784 |
|
| 1785 |
|
| 1786 |
|
| 1787 | *vpos = position & 0x1fff;
|
| 1788 | *hpos = (position >> 16) & 0x1fff;
|
| 1789 |
|
| 1790 |
|
| 1791 | if (vbl > 0) {
|
| 1792 |
|
| 1793 | ret |= DRM_SCANOUTPOS_ACCURATE;
|
| 1794 | vbl_start = vbl & 0x1fff;
|
| 1795 | vbl_end = (vbl >> 16) & 0x1fff;
|
| 1796 | } else {
|
| 1797 |
|
| 1798 | vbl_start = mode->crtc_vdisplay;
|
| 1799 | vbl_end = 0;
|
| 1800 | }
|
| 1801 |
|
| 1802 |
|
| 1803 | if (flags & GET_DISTANCE_TO_VBLANKSTART) {
|
| 1804 |
|
| 1805 | *hpos = *vpos - vbl_start;
|
| 1806 | }
|
| 1807 |
|
| 1808 | |
| 1809 | * problem that vblank irqs fire a few scanlines before start
|
| 1810 | * of vblank. Some driver internal callers need the true vblank
|
| 1811 | * start to be used and signal this via the USE_REAL_VBLANKSTART flag.
|
| 1812 | *
|
| 1813 | * The cause of the "early" vblank irq is that the irq is triggered
|
| 1814 | * by the line buffer logic when the line buffer read position enters
|
| 1815 | * the vblank, whereas our crtc scanout position naturally lags the
|
| 1816 | * line buffer read position.
|
| 1817 |
|
| 1818 | if (!(flags & USE_REAL_VBLANKSTART))
|
| 1819 | vbl_start -= adev->mode_info.crtcs[pipe]->lb_vblank_lead_lines;
|
| 1820 |
|
| 1821 |
|
| 1822 | if ((*vpos < vbl_start) && (*vpos >= vbl_end))
|
| 1823 | in_vbl = false;
|
| 1824 |
|
| 1825 |
|
| 1826 | if (in_vbl)
|
| 1827 | ret |= DRM_SCANOUTPOS_IN_VBLANK;
|
| 1828 |
|
| 1829 |
|
| 1830 | if (flags & GET_DISTANCE_TO_VBLANKSTART) {
|
| 1831 |
|
| 1832 | *vpos -= vbl_start;
|
| 1833 | return ret;
|
| 1834 | }
|
| 1835 |
|
| 1836 | |
| 1837 | * vpos will then be >=0 in video scanout area, but negative
|
| 1838 | * within vblank area, counting down the number of lines until
|
| 1839 | * start of scanout.
|
| 1840 |
|
| 1841 |
|
| 1842 |
|
| 1843 | if (in_vbl && (*vpos >= vbl_start)) {
|
| 1844 | vtotal = mode->crtc_vtotal;
|
| 1845 |
|
| 1846 | |
| 1847 | * the vtotal value. Clamp to 0 to return -vbl_end instead
|
| 1848 | * of guessing the remaining number of lines until scanout.
|
| 1849 |
|
| 1850 | *vpos = (*vpos < vtotal) ? (*vpos - vtotal) : 0;
|
| 1851 | }
|
| 1852 |
|
| 1853 |
|
| 1854 | *vpos = *vpos - vbl_end;
|
| 1855 |
|
| 1856 | return ret;
|
| 1857 | }
|
| 1858 |
|
| 1859 | int amdgpu_display_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc)
|
| 1860 | {
|
| 1861 | if (crtc < 0 || crtc >= adev->mode_info.num_crtc)
|
| 1862 | return AMDGPU_CRTC_IRQ_NONE;
|
| 1863 |
|
| 1864 | switch (crtc) {
|
| 1865 | case 0:
|
| 1866 | return AMDGPU_CRTC_IRQ_VBLANK1;
|
| 1867 | case 1:
|
| 1868 | return AMDGPU_CRTC_IRQ_VBLANK2;
|
| 1869 | case 2:
|
| 1870 | return AMDGPU_CRTC_IRQ_VBLANK3;
|
| 1871 | case 3:
|
| 1872 | return AMDGPU_CRTC_IRQ_VBLANK4;
|
| 1873 | case 4:
|
| 1874 | return AMDGPU_CRTC_IRQ_VBLANK5;
|
| 1875 | case 5:
|
| 1876 | return AMDGPU_CRTC_IRQ_VBLANK6;
|
| 1877 | default:
|
| 1878 | return AMDGPU_CRTC_IRQ_NONE;
|
| 1879 | }
|
| 1880 | }
|
| 1881 |
|
| 1882 | bool amdgpu_crtc_get_scanout_position(struct drm_crtc *crtc,
|
| 1883 | bool in_vblank_irq, int *vpos,
|
| 1884 | int *hpos, ktime_t *stime, ktime_t *etime,
|
| 1885 | const struct drm_display_mode *mode)
|
| 1886 | {
|
| 1887 | struct drm_device *dev = crtc->dev;
|
| 1888 | unsigned int pipe = crtc->index;
|
| 1889 |
|
| 1890 | return amdgpu_display_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
|
| 1891 | stime, etime, mode);
|
| 1892 | }
|
| 1893 |
|
| 1894 | int amdgpu_display_suspend_helper(struct amdgpu_device *adev)
|
| 1895 | {
|
| 1896 | struct drm_device *dev = adev_to_drm(adev);
|
| 1897 | struct drm_crtc *crtc;
|
| 1898 | struct drm_connector *connector;
|
| 1899 | struct drm_connector_list_iter iter;
|
| 1900 | int r;
|
| 1901 |
|
| 1902 | drm_kms_helper_poll_disable(dev);
|
| 1903 |
|
| 1904 |
|
| 1905 | drm_modeset_lock_all(dev);
|
| 1906 | drm_connector_list_iter_begin(dev, &iter);
|
| 1907 | drm_for_each_connector_iter(connector, &iter)
|
| 1908 | drm_helper_connector_dpms(connector,
|
| 1909 | DRM_MODE_DPMS_OFF);
|
| 1910 | drm_connector_list_iter_end(&iter);
|
| 1911 | drm_modeset_unlock_all(dev);
|
| 1912 |
|
| 1913 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
| 1914 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
| 1915 | struct drm_framebuffer *fb = crtc->primary->fb;
|
| 1916 |
|
| 1917 | if (amdgpu_crtc->cursor_bo && !adev->enable_virtual_display) {
|
| 1918 | struct amdgpu_bo *aobj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo);
|
| 1919 |
|
| 1920 | r = amdgpu_bo_reserve(aobj, true);
|
| 1921 | if (r == 0) {
|
| 1922 | amdgpu_bo_unpin(aobj);
|
| 1923 | amdgpu_bo_unreserve(aobj);
|
| 1924 | }
|
| 1925 | }
|
| 1926 |
|
| 1927 | if (!fb || !fb->obj[0])
|
| 1928 | continue;
|
| 1929 |
|
| 1930 | if (!drm_fb_helper_gem_is_fb(dev->fb_helper, fb->obj[0])) {
|
| 1931 | struct amdgpu_bo *robj = gem_to_amdgpu_bo(fb->obj[0]);
|
| 1932 |
|
| 1933 | r = amdgpu_bo_reserve(robj, true);
|
| 1934 | if (r == 0) {
|
| 1935 | amdgpu_bo_unpin(robj);
|
| 1936 | amdgpu_bo_unreserve(robj);
|
| 1937 | }
|
| 1938 | }
|
| 1939 | }
|
| 1940 | return 0;
|
| 1941 | }
|
| 1942 |
|
| 1943 | int amdgpu_display_resume_helper(struct amdgpu_device *adev)
|
| 1944 | {
|
| 1945 | struct drm_device *dev = adev_to_drm(adev);
|
| 1946 | struct drm_connector *connector;
|
| 1947 | struct drm_connector_list_iter iter;
|
| 1948 | struct drm_crtc *crtc;
|
| 1949 | int r;
|
| 1950 |
|
| 1951 |
|
| 1952 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
| 1953 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
|
| 1954 |
|
| 1955 | if (amdgpu_crtc->cursor_bo && !adev->enable_virtual_display) {
|
| 1956 | struct amdgpu_bo *aobj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo);
|
| 1957 |
|
| 1958 | r = amdgpu_bo_reserve(aobj, true);
|
| 1959 | if (r == 0) {
|
| 1960 | aobj->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
|
| 1961 | r = amdgpu_bo_pin(aobj, AMDGPU_GEM_DOMAIN_VRAM);
|
| 1962 | if (r != 0)
|
| 1963 | dev_err(adev->dev, "Failed to pin cursor BO (%d)\n", r);
|
| 1964 | amdgpu_crtc->cursor_addr = amdgpu_bo_gpu_offset(aobj);
|
| 1965 | amdgpu_bo_unreserve(aobj);
|
| 1966 | }
|
| 1967 | }
|
| 1968 | }
|
| 1969 |
|
| 1970 | drm_helper_resume_force_mode(dev);
|
| 1971 |
|
| 1972 |
|
| 1973 | drm_modeset_lock_all(dev);
|
| 1974 |
|
| 1975 | drm_connector_list_iter_begin(dev, &iter);
|
| 1976 | drm_for_each_connector_iter(connector, &iter)
|
| 1977 | drm_helper_connector_dpms(connector,
|
| 1978 | DRM_MODE_DPMS_ON);
|
| 1979 | drm_connector_list_iter_end(&iter);
|
| 1980 |
|
| 1981 | drm_modeset_unlock_all(dev);
|
| 1982 |
|
| 1983 | drm_kms_helper_poll_enable(dev);
|
| 1984 |
|
| 1985 | return 0;
|
| 1986 | }
|
| 1987 |
|
| 1988 | |
| 1989 | * they are called from the panic handler, and protected by the drm_panic spinlock.
|
| 1990 |
|
| 1991 | static struct amdgpu_bo *panic_abo;
|
| 1992 |
|
| 1993 | |
| 1994 | * This is a simplified version of amdgpu_device_mm_access()
|
| 1995 |
|
| 1996 | static void amdgpu_display_set_pixel(struct drm_scanout_buffer *sb,
|
| 1997 | unsigned int x,
|
| 1998 | unsigned int y,
|
| 1999 | u32 color)
|
| 2000 | {
|
| 2001 | struct amdgpu_res_cursor cursor;
|
| 2002 | unsigned long offset;
|
| 2003 | struct amdgpu_bo *abo = panic_abo;
|
| 2004 | struct amdgpu_device *adev = amdgpu_ttm_adev(abo->tbo.bdev);
|
| 2005 | uint32_t tmp;
|
| 2006 |
|
| 2007 | offset = x * 4 + y * sb->pitch[0];
|
| 2008 | amdgpu_res_first(abo->tbo.resource, offset, 4, &cursor);
|
| 2009 |
|
| 2010 | tmp = cursor.start >> 31;
|
| 2011 | WREG32_NO_KIQ(mmMM_INDEX, ((uint32_t) cursor.start) | 0x80000000);
|
| 2012 | if (tmp != 0xffffffff)
|
| 2013 | WREG32_NO_KIQ(mmMM_INDEX_HI, tmp);
|
| 2014 | WREG32_NO_KIQ(mmMM_DATA, color);
|
| 2015 | }
|
| 2016 |
|
| 2017 | int amdgpu_display_get_scanout_buffer(struct drm_plane *plane,
|
| 2018 | struct drm_scanout_buffer *sb)
|
| 2019 | {
|
| 2020 | struct amdgpu_bo *abo;
|
| 2021 | struct drm_framebuffer *fb;
|
| 2022 |
|
| 2023 | if (drm_drv_uses_atomic_modeset(plane->dev))
|
| 2024 | fb = plane->state->fb;
|
| 2025 | else
|
| 2026 | fb = plane->fb;
|
| 2027 |
|
| 2028 | if (!fb)
|
| 2029 | return -EINVAL;
|
| 2030 |
|
| 2031 | DRM_DEBUG_KMS("Framebuffer %dx%d %p4cc\n", fb->width, fb->height, &fb->format->format);
|
| 2032 |
|
| 2033 | abo = gem_to_amdgpu_bo(fb->obj[0]);
|
| 2034 | if (!abo)
|
| 2035 | return -EINVAL;
|
| 2036 |
|
| 2037 | sb->width = fb->width;
|
| 2038 | sb->height = fb->height;
|
| 2039 |
|
| 2040 | sb->format = drm_format_info(fb->format->format);
|
| 2041 | if (!sb->format)
|
| 2042 | return -EINVAL;
|
| 2043 |
|
| 2044 | sb->pitch[0] = fb->pitches[0];
|
| 2045 |
|
| 2046 | if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) {
|
| 2047 | if (abo->tbo.resource->mem_type != TTM_PL_VRAM) {
|
| 2048 | drm_warn(plane->dev, "amdgpu panic, framebuffer not in VRAM\n");
|
| 2049 | return -EINVAL;
|
| 2050 | }
|
| 2051 |
|
| 2052 | if (fb->format->cpp[0] != 4) {
|
| 2053 | drm_warn(plane->dev, "amdgpu panic, pixel format is not 32bits\n");
|
| 2054 | return -EINVAL;
|
| 2055 | }
|
| 2056 | sb->set_pixel = amdgpu_display_set_pixel;
|
| 2057 | panic_abo = abo;
|
| 2058 | return 0;
|
| 2059 | }
|
| 2060 | if (!abo->kmap.virtual &&
|
| 2061 | ttm_bo_kmap(&abo->tbo, 0, PFN_UP(abo->tbo.base.size), &abo->kmap)) {
|
| 2062 | drm_warn(plane->dev, "amdgpu bo map failed, panic won't be displayed\n");
|
| 2063 | return -ENOMEM;
|
| 2064 | }
|
| 2065 | if (abo->kmap.bo_kmap_type & TTM_BO_MAP_IOMEM_MASK)
|
| 2066 | iosys_map_set_vaddr_iomem(&sb->map[0], abo->kmap.virtual);
|
| 2067 | else
|
| 2068 | iosys_map_set_vaddr(&sb->map[0], abo->kmap.virtual);
|
| 2069 |
|
| 2070 | return 0;
|
| 2071 | }
|