18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2011 Red Hat Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Authors: Ben Skeggs 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci#include "disp.h" 258c2ecf20Sopenharmony_ci#include "atom.h" 268c2ecf20Sopenharmony_ci#include "core.h" 278c2ecf20Sopenharmony_ci#include "head.h" 288c2ecf20Sopenharmony_ci#include "wndw.h" 298c2ecf20Sopenharmony_ci#include "handles.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 328c2ecf20Sopenharmony_ci#include <linux/hdmi.h> 338c2ecf20Sopenharmony_ci#include <linux/component.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h> 378c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 388c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 398c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 408c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 418c2ecf20Sopenharmony_ci#include <drm/drm_scdc_helper.h> 428c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include <nvif/push507c.h> 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include <nvif/class.h> 478c2ecf20Sopenharmony_ci#include <nvif/cl0002.h> 488c2ecf20Sopenharmony_ci#include <nvif/cl5070.h> 498c2ecf20Sopenharmony_ci#include <nvif/cl507d.h> 508c2ecf20Sopenharmony_ci#include <nvif/event.h> 518c2ecf20Sopenharmony_ci#include <nvif/timer.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include <nvhw/class/cl507c.h> 548c2ecf20Sopenharmony_ci#include <nvhw/class/cl507d.h> 558c2ecf20Sopenharmony_ci#include <nvhw/class/cl837d.h> 568c2ecf20Sopenharmony_ci#include <nvhw/class/cl887d.h> 578c2ecf20Sopenharmony_ci#include <nvhw/class/cl907d.h> 588c2ecf20Sopenharmony_ci#include <nvhw/class/cl917d.h> 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#include "nouveau_drv.h" 618c2ecf20Sopenharmony_ci#include "nouveau_dma.h" 628c2ecf20Sopenharmony_ci#include "nouveau_gem.h" 638c2ecf20Sopenharmony_ci#include "nouveau_connector.h" 648c2ecf20Sopenharmony_ci#include "nouveau_encoder.h" 658c2ecf20Sopenharmony_ci#include "nouveau_fence.h" 668c2ecf20Sopenharmony_ci#include "nouveau_fbcon.h" 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#include <subdev/bios/dp.h> 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/****************************************************************************** 718c2ecf20Sopenharmony_ci * EVO channel 728c2ecf20Sopenharmony_ci *****************************************************************************/ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int 758c2ecf20Sopenharmony_cinv50_chan_create(struct nvif_device *device, struct nvif_object *disp, 768c2ecf20Sopenharmony_ci const s32 *oclass, u8 head, void *data, u32 size, 778c2ecf20Sopenharmony_ci struct nv50_chan *chan) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct nvif_sclass *sclass; 808c2ecf20Sopenharmony_ci int ret, i, n; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci chan->device = device; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = n = nvif_object_sclass_get(disp, &sclass); 858c2ecf20Sopenharmony_ci if (ret < 0) 868c2ecf20Sopenharmony_ci return ret; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci while (oclass[0]) { 898c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 908c2ecf20Sopenharmony_ci if (sclass[i].oclass == oclass[0]) { 918c2ecf20Sopenharmony_ci ret = nvif_object_ctor(disp, "kmsChan", 0, 928c2ecf20Sopenharmony_ci oclass[0], data, size, 938c2ecf20Sopenharmony_ci &chan->user); 948c2ecf20Sopenharmony_ci if (ret == 0) 958c2ecf20Sopenharmony_ci nvif_object_map(&chan->user, NULL, 0); 968c2ecf20Sopenharmony_ci nvif_object_sclass_put(&sclass); 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci oclass++; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci nvif_object_sclass_put(&sclass); 1048c2ecf20Sopenharmony_ci return -ENOSYS; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void 1088c2ecf20Sopenharmony_cinv50_chan_destroy(struct nv50_chan *chan) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci nvif_object_dtor(&chan->user); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/****************************************************************************** 1148c2ecf20Sopenharmony_ci * DMA EVO channel 1158c2ecf20Sopenharmony_ci *****************************************************************************/ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_civoid 1188c2ecf20Sopenharmony_cinv50_dmac_destroy(struct nv50_dmac *dmac) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci nvif_object_dtor(&dmac->vram); 1218c2ecf20Sopenharmony_ci nvif_object_dtor(&dmac->sync); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci nv50_chan_destroy(&dmac->base); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci nvif_mem_dtor(&dmac->_push.mem); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void 1298c2ecf20Sopenharmony_cinv50_dmac_kick(struct nvif_push *push) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct nv50_dmac *dmac = container_of(push, typeof(*dmac), _push); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci dmac->cur = push->cur - (u32 *)dmac->_push.mem.object.map.ptr; 1348c2ecf20Sopenharmony_ci if (dmac->put != dmac->cur) { 1358c2ecf20Sopenharmony_ci /* Push buffer fetches are not coherent with BAR1, we need to ensure 1368c2ecf20Sopenharmony_ci * writes have been flushed right through to VRAM before writing PUT. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci if (dmac->push->mem.type & NVIF_MEM_VRAM) { 1398c2ecf20Sopenharmony_ci struct nvif_device *device = dmac->base.device; 1408c2ecf20Sopenharmony_ci nvif_wr32(&device->object, 0x070000, 0x00000001); 1418c2ecf20Sopenharmony_ci nvif_msec(device, 2000, 1428c2ecf20Sopenharmony_ci if (!(nvif_rd32(&device->object, 0x070000) & 0x00000002)) 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci ); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci NVIF_WV32(&dmac->base.user, NV507C, PUT, PTR, dmac->cur); 1488c2ecf20Sopenharmony_ci dmac->put = dmac->cur; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci push->bgn = push->cur; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int 1558c2ecf20Sopenharmony_cinv50_dmac_free(struct nv50_dmac *dmac) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci u32 get = NVIF_RV32(&dmac->base.user, NV507C, GET, PTR); 1588c2ecf20Sopenharmony_ci if (get > dmac->cur) /* NVIDIA stay 5 away from GET, do the same. */ 1598c2ecf20Sopenharmony_ci return get - dmac->cur - 5; 1608c2ecf20Sopenharmony_ci return dmac->max - dmac->cur; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int 1648c2ecf20Sopenharmony_cinv50_dmac_wind(struct nv50_dmac *dmac) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci /* Wait for GET to depart from the beginning of the push buffer to 1678c2ecf20Sopenharmony_ci * prevent writing PUT == GET, which would be ignored by HW. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci u32 get = NVIF_RV32(&dmac->base.user, NV507C, GET, PTR); 1708c2ecf20Sopenharmony_ci if (get == 0) { 1718c2ecf20Sopenharmony_ci /* Corner-case, HW idle, but non-committed work pending. */ 1728c2ecf20Sopenharmony_ci if (dmac->put == 0) 1738c2ecf20Sopenharmony_ci nv50_dmac_kick(dmac->push); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (nvif_msec(dmac->base.device, 2000, 1768c2ecf20Sopenharmony_ci if (NVIF_TV32(&dmac->base.user, NV507C, GET, PTR, >, 0)) 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci ) < 0) 1798c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci PUSH_RSVD(dmac->push, PUSH_JUMP(dmac->push, 0)); 1838c2ecf20Sopenharmony_ci dmac->cur = 0; 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int 1888c2ecf20Sopenharmony_cinv50_dmac_wait(struct nvif_push *push, u32 size) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct nv50_dmac *dmac = container_of(push, typeof(*dmac), _push); 1918c2ecf20Sopenharmony_ci int free; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (WARN_ON(size > dmac->max)) 1948c2ecf20Sopenharmony_ci return -EINVAL; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci dmac->cur = push->cur - (u32 *)dmac->_push.mem.object.map.ptr; 1978c2ecf20Sopenharmony_ci if (dmac->cur + size >= dmac->max) { 1988c2ecf20Sopenharmony_ci int ret = nv50_dmac_wind(dmac); 1998c2ecf20Sopenharmony_ci if (ret) 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci push->cur = dmac->_push.mem.object.map.ptr; 2038c2ecf20Sopenharmony_ci push->cur = push->cur + dmac->cur; 2048c2ecf20Sopenharmony_ci nv50_dmac_kick(push); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (nvif_msec(dmac->base.device, 2000, 2088c2ecf20Sopenharmony_ci if ((free = nv50_dmac_free(dmac)) >= size) 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci ) < 0) { 2118c2ecf20Sopenharmony_ci WARN_ON(1); 2128c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci push->bgn = dmac->_push.mem.object.map.ptr; 2168c2ecf20Sopenharmony_ci push->bgn = push->bgn + dmac->cur; 2178c2ecf20Sopenharmony_ci push->cur = push->bgn; 2188c2ecf20Sopenharmony_ci push->end = push->cur + free; 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciint 2238c2ecf20Sopenharmony_cinv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, 2248c2ecf20Sopenharmony_ci const s32 *oclass, u8 head, void *data, u32 size, s64 syncbuf, 2258c2ecf20Sopenharmony_ci struct nv50_dmac *dmac) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct nouveau_cli *cli = (void *)device->object.client; 2288c2ecf20Sopenharmony_ci struct nv50_disp_core_channel_dma_v0 *args = data; 2298c2ecf20Sopenharmony_ci u8 type = NVIF_MEM_COHERENT; 2308c2ecf20Sopenharmony_ci int ret; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci mutex_init(&dmac->lock); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Pascal added support for 47-bit physical addresses, but some 2358c2ecf20Sopenharmony_ci * parts of EVO still only accept 40-bit PAs. 2368c2ecf20Sopenharmony_ci * 2378c2ecf20Sopenharmony_ci * To avoid issues on systems with large amounts of RAM, and on 2388c2ecf20Sopenharmony_ci * systems where an IOMMU maps pages at a high address, we need 2398c2ecf20Sopenharmony_ci * to allocate push buffers in VRAM instead. 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * This appears to match NVIDIA's behaviour on Pascal. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci if (device->info.family == NV_DEVICE_INFO_V0_PASCAL) 2448c2ecf20Sopenharmony_ci type |= NVIF_MEM_VRAM; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci ret = nvif_mem_ctor_map(&cli->mmu, "kmsChanPush", type, 0x1000, 2478c2ecf20Sopenharmony_ci &dmac->_push.mem); 2488c2ecf20Sopenharmony_ci if (ret) 2498c2ecf20Sopenharmony_ci return ret; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dmac->ptr = dmac->_push.mem.object.map.ptr; 2528c2ecf20Sopenharmony_ci dmac->_push.wait = nv50_dmac_wait; 2538c2ecf20Sopenharmony_ci dmac->_push.kick = nv50_dmac_kick; 2548c2ecf20Sopenharmony_ci dmac->push = &dmac->_push; 2558c2ecf20Sopenharmony_ci dmac->push->bgn = dmac->_push.mem.object.map.ptr; 2568c2ecf20Sopenharmony_ci dmac->push->cur = dmac->push->bgn; 2578c2ecf20Sopenharmony_ci dmac->push->end = dmac->push->bgn; 2588c2ecf20Sopenharmony_ci dmac->max = 0x1000/4 - 1; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* EVO channels are affected by a HW bug where the last 12 DWORDs 2618c2ecf20Sopenharmony_ci * of the push buffer aren't able to be used safely. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci if (disp->oclass < GV100_DISP) 2648c2ecf20Sopenharmony_ci dmac->max -= 12; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci args->pushbuf = nvif_handle(&dmac->_push.mem.object); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ret = nv50_chan_create(device, disp, oclass, head, data, size, 2698c2ecf20Sopenharmony_ci &dmac->base); 2708c2ecf20Sopenharmony_ci if (ret) 2718c2ecf20Sopenharmony_ci return ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (syncbuf < 0) 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ret = nvif_object_ctor(&dmac->base.user, "kmsSyncCtxDma", NV50_DISP_HANDLE_SYNCBUF, 2778c2ecf20Sopenharmony_ci NV_DMA_IN_MEMORY, 2788c2ecf20Sopenharmony_ci &(struct nv_dma_v0) { 2798c2ecf20Sopenharmony_ci .target = NV_DMA_V0_TARGET_VRAM, 2808c2ecf20Sopenharmony_ci .access = NV_DMA_V0_ACCESS_RDWR, 2818c2ecf20Sopenharmony_ci .start = syncbuf + 0x0000, 2828c2ecf20Sopenharmony_ci .limit = syncbuf + 0x0fff, 2838c2ecf20Sopenharmony_ci }, sizeof(struct nv_dma_v0), 2848c2ecf20Sopenharmony_ci &dmac->sync); 2858c2ecf20Sopenharmony_ci if (ret) 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = nvif_object_ctor(&dmac->base.user, "kmsVramCtxDma", NV50_DISP_HANDLE_VRAM, 2898c2ecf20Sopenharmony_ci NV_DMA_IN_MEMORY, 2908c2ecf20Sopenharmony_ci &(struct nv_dma_v0) { 2918c2ecf20Sopenharmony_ci .target = NV_DMA_V0_TARGET_VRAM, 2928c2ecf20Sopenharmony_ci .access = NV_DMA_V0_ACCESS_RDWR, 2938c2ecf20Sopenharmony_ci .start = 0, 2948c2ecf20Sopenharmony_ci .limit = device->info.ram_user - 1, 2958c2ecf20Sopenharmony_ci }, sizeof(struct nv_dma_v0), 2968c2ecf20Sopenharmony_ci &dmac->vram); 2978c2ecf20Sopenharmony_ci if (ret) 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return ret; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/****************************************************************************** 3048c2ecf20Sopenharmony_ci * Output path helpers 3058c2ecf20Sopenharmony_ci *****************************************************************************/ 3068c2ecf20Sopenharmony_cistatic void 3078c2ecf20Sopenharmony_cinv50_outp_release(struct nouveau_encoder *nv_encoder) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev); 3108c2ecf20Sopenharmony_ci struct { 3118c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 3128c2ecf20Sopenharmony_ci } args = { 3138c2ecf20Sopenharmony_ci .base.version = 1, 3148c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_RELEASE, 3158c2ecf20Sopenharmony_ci .base.hasht = nv_encoder->dcb->hasht, 3168c2ecf20Sopenharmony_ci .base.hashm = nv_encoder->dcb->hashm, 3178c2ecf20Sopenharmony_ci }; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci nvif_mthd(&disp->disp->object, 0, &args, sizeof(args)); 3208c2ecf20Sopenharmony_ci nv_encoder->or = -1; 3218c2ecf20Sopenharmony_ci nv_encoder->link = 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int 3258c2ecf20Sopenharmony_cinv50_outp_acquire(struct nouveau_encoder *nv_encoder, bool hda) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); 3288c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(drm->dev); 3298c2ecf20Sopenharmony_ci struct { 3308c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 3318c2ecf20Sopenharmony_ci struct nv50_disp_acquire_v0 info; 3328c2ecf20Sopenharmony_ci } args = { 3338c2ecf20Sopenharmony_ci .base.version = 1, 3348c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_ACQUIRE, 3358c2ecf20Sopenharmony_ci .base.hasht = nv_encoder->dcb->hasht, 3368c2ecf20Sopenharmony_ci .base.hashm = nv_encoder->dcb->hashm, 3378c2ecf20Sopenharmony_ci .info.hda = hda, 3388c2ecf20Sopenharmony_ci }; 3398c2ecf20Sopenharmony_ci int ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = nvif_mthd(&disp->disp->object, 0, &args, sizeof(args)); 3428c2ecf20Sopenharmony_ci if (ret) { 3438c2ecf20Sopenharmony_ci NV_ERROR(drm, "error acquiring output path: %d\n", ret); 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci nv_encoder->or = args.info.or; 3488c2ecf20Sopenharmony_ci nv_encoder->link = args.info.link; 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int 3538c2ecf20Sopenharmony_cinv50_outp_atomic_check_view(struct drm_encoder *encoder, 3548c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 3558c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state, 3568c2ecf20Sopenharmony_ci struct drm_display_mode *native_mode) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; 3598c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc_state->mode; 3608c2ecf20Sopenharmony_ci struct drm_connector *connector = conn_state->connector; 3618c2ecf20Sopenharmony_ci struct nouveau_conn_atom *asyc = nouveau_conn_atom(conn_state); 3628c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s atomic_check\n", encoder->name); 3658c2ecf20Sopenharmony_ci asyc->scaler.full = false; 3668c2ecf20Sopenharmony_ci if (!native_mode) 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (asyc->scaler.mode == DRM_MODE_SCALE_NONE) { 3708c2ecf20Sopenharmony_ci switch (connector->connector_type) { 3718c2ecf20Sopenharmony_ci case DRM_MODE_CONNECTOR_LVDS: 3728c2ecf20Sopenharmony_ci case DRM_MODE_CONNECTOR_eDP: 3738c2ecf20Sopenharmony_ci /* Don't force scaler for EDID modes with 3748c2ecf20Sopenharmony_ci * same size as the native one (e.g. different 3758c2ecf20Sopenharmony_ci * refresh rate) 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci if (mode->hdisplay == native_mode->hdisplay && 3788c2ecf20Sopenharmony_ci mode->vdisplay == native_mode->vdisplay && 3798c2ecf20Sopenharmony_ci mode->type & DRM_MODE_TYPE_DRIVER) 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci mode = native_mode; 3828c2ecf20Sopenharmony_ci asyc->scaler.full = true; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci default: 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci } else { 3888c2ecf20Sopenharmony_ci mode = native_mode; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!drm_mode_equal(adjusted_mode, mode)) { 3928c2ecf20Sopenharmony_ci drm_mode_copy(adjusted_mode, mode); 3938c2ecf20Sopenharmony_ci crtc_state->mode_changed = true; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void 4008c2ecf20Sopenharmony_cinv50_outp_atomic_fix_depth(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 4038c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 4048c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &asyh->state.adjusted_mode; 4058c2ecf20Sopenharmony_ci unsigned int max_rate, mode_rate; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci switch (nv_encoder->dcb->type) { 4088c2ecf20Sopenharmony_ci case DCB_OUTPUT_DP: 4098c2ecf20Sopenharmony_ci max_rate = nv_encoder->dp.link_nr * nv_encoder->dp.link_bw; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* we don't support more than 10 anyway */ 4128c2ecf20Sopenharmony_ci asyh->or.bpc = min_t(u8, asyh->or.bpc, 10); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* reduce the bpc until it works out */ 4158c2ecf20Sopenharmony_ci while (asyh->or.bpc > 6) { 4168c2ecf20Sopenharmony_ci mode_rate = DIV_ROUND_UP(mode->clock * asyh->or.bpc * 3, 8); 4178c2ecf20Sopenharmony_ci if (mode_rate <= max_rate) 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci asyh->or.bpc -= 2; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci default: 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int 4298c2ecf20Sopenharmony_cinv50_outp_atomic_check(struct drm_encoder *encoder, 4308c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 4318c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct drm_connector *connector = conn_state->connector; 4348c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 4358c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 4368c2ecf20Sopenharmony_ci int ret; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = nv50_outp_atomic_check_view(encoder, crtc_state, conn_state, 4398c2ecf20Sopenharmony_ci nv_connector->native_mode); 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (crtc_state->mode_changed || crtc_state->connectors_changed) 4448c2ecf20Sopenharmony_ci asyh->or.bpc = connector->display_info.bpc; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* We might have to reduce the bpc */ 4478c2ecf20Sopenharmony_ci nv50_outp_atomic_fix_depth(encoder, crtc_state); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistruct nouveau_connector * 4538c2ecf20Sopenharmony_cinv50_outp_get_new_connector(struct nouveau_encoder *outp, 4548c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct drm_connector *connector; 4578c2ecf20Sopenharmony_ci struct drm_connector_state *connector_state; 4588c2ecf20Sopenharmony_ci struct drm_encoder *encoder = to_drm_encoder(outp); 4598c2ecf20Sopenharmony_ci int i; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci for_each_new_connector_in_state(state, connector, connector_state, i) { 4628c2ecf20Sopenharmony_ci if (connector_state->best_encoder == encoder) 4638c2ecf20Sopenharmony_ci return nouveau_connector(connector); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return NULL; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistruct nouveau_connector * 4708c2ecf20Sopenharmony_cinv50_outp_get_old_connector(struct nouveau_encoder *outp, 4718c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct drm_connector *connector; 4748c2ecf20Sopenharmony_ci struct drm_connector_state *connector_state; 4758c2ecf20Sopenharmony_ci struct drm_encoder *encoder = to_drm_encoder(outp); 4768c2ecf20Sopenharmony_ci int i; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for_each_old_connector_in_state(state, connector, connector_state, i) { 4798c2ecf20Sopenharmony_ci if (connector_state->best_encoder == encoder) 4808c2ecf20Sopenharmony_ci return nouveau_connector(connector); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return NULL; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci/****************************************************************************** 4878c2ecf20Sopenharmony_ci * DAC 4888c2ecf20Sopenharmony_ci *****************************************************************************/ 4898c2ecf20Sopenharmony_cistatic void 4908c2ecf20Sopenharmony_cinv50_dac_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 4938c2ecf20Sopenharmony_ci struct nv50_core *core = nv50_disp(encoder->dev)->core; 4948c2ecf20Sopenharmony_ci const u32 ctrl = NVDEF(NV507D, DAC_SET_CONTROL, OWNER, NONE); 4958c2ecf20Sopenharmony_ci if (nv_encoder->crtc) 4968c2ecf20Sopenharmony_ci core->func->dac->ctrl(core, nv_encoder->or, ctrl, NULL); 4978c2ecf20Sopenharmony_ci nv_encoder->crtc = NULL; 4988c2ecf20Sopenharmony_ci nv50_outp_release(nv_encoder); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cistatic void 5028c2ecf20Sopenharmony_cinv50_dac_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 5058c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 5068c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(nv_crtc->base.state); 5078c2ecf20Sopenharmony_ci struct nv50_core *core = nv50_disp(encoder->dev)->core; 5088c2ecf20Sopenharmony_ci u32 ctrl = 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci switch (nv_crtc->index) { 5118c2ecf20Sopenharmony_ci case 0: ctrl |= NVDEF(NV507D, DAC_SET_CONTROL, OWNER, HEAD0); break; 5128c2ecf20Sopenharmony_ci case 1: ctrl |= NVDEF(NV507D, DAC_SET_CONTROL, OWNER, HEAD1); break; 5138c2ecf20Sopenharmony_ci case 2: ctrl |= NVDEF(NV907D, DAC_SET_CONTROL, OWNER_MASK, HEAD2); break; 5148c2ecf20Sopenharmony_ci case 3: ctrl |= NVDEF(NV907D, DAC_SET_CONTROL, OWNER_MASK, HEAD3); break; 5158c2ecf20Sopenharmony_ci default: 5168c2ecf20Sopenharmony_ci WARN_ON(1); 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci ctrl |= NVDEF(NV507D, DAC_SET_CONTROL, PROTOCOL, RGB_CRT); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci nv50_outp_acquire(nv_encoder, false); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci core->func->dac->ctrl(core, nv_encoder->or, ctrl, asyh); 5258c2ecf20Sopenharmony_ci asyh->or.depth = 0; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci nv_encoder->crtc = encoder->crtc; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic enum drm_connector_status 5318c2ecf20Sopenharmony_cinv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 5348c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(encoder->dev); 5358c2ecf20Sopenharmony_ci struct { 5368c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 5378c2ecf20Sopenharmony_ci struct nv50_disp_dac_load_v0 load; 5388c2ecf20Sopenharmony_ci } args = { 5398c2ecf20Sopenharmony_ci .base.version = 1, 5408c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_DAC_LOAD, 5418c2ecf20Sopenharmony_ci .base.hasht = nv_encoder->dcb->hasht, 5428c2ecf20Sopenharmony_ci .base.hashm = nv_encoder->dcb->hashm, 5438c2ecf20Sopenharmony_ci }; 5448c2ecf20Sopenharmony_ci int ret; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci args.load.data = nouveau_drm(encoder->dev)->vbios.dactestval; 5478c2ecf20Sopenharmony_ci if (args.load.data == 0) 5488c2ecf20Sopenharmony_ci args.load.data = 340; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci ret = nvif_mthd(&disp->disp->object, 0, &args, sizeof(args)); 5518c2ecf20Sopenharmony_ci if (ret || !args.load.load) 5528c2ecf20Sopenharmony_ci return connector_status_disconnected; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return connector_status_connected; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs 5588c2ecf20Sopenharmony_cinv50_dac_help = { 5598c2ecf20Sopenharmony_ci .atomic_check = nv50_outp_atomic_check, 5608c2ecf20Sopenharmony_ci .atomic_enable = nv50_dac_enable, 5618c2ecf20Sopenharmony_ci .atomic_disable = nv50_dac_disable, 5628c2ecf20Sopenharmony_ci .detect = nv50_dac_detect 5638c2ecf20Sopenharmony_ci}; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic void 5668c2ecf20Sopenharmony_cinv50_dac_destroy(struct drm_encoder *encoder) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 5698c2ecf20Sopenharmony_ci kfree(encoder); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs 5738c2ecf20Sopenharmony_cinv50_dac_func = { 5748c2ecf20Sopenharmony_ci .destroy = nv50_dac_destroy, 5758c2ecf20Sopenharmony_ci}; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int 5788c2ecf20Sopenharmony_cinv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(connector->dev); 5818c2ecf20Sopenharmony_ci struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); 5828c2ecf20Sopenharmony_ci struct nvkm_i2c_bus *bus; 5838c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder; 5848c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 5858c2ecf20Sopenharmony_ci int type = DRM_MODE_ENCODER_DAC; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); 5888c2ecf20Sopenharmony_ci if (!nv_encoder) 5898c2ecf20Sopenharmony_ci return -ENOMEM; 5908c2ecf20Sopenharmony_ci nv_encoder->dcb = dcbe; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); 5938c2ecf20Sopenharmony_ci if (bus) 5948c2ecf20Sopenharmony_ci nv_encoder->i2c = &bus->i2c; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci encoder = to_drm_encoder(nv_encoder); 5978c2ecf20Sopenharmony_ci encoder->possible_crtcs = dcbe->heads; 5988c2ecf20Sopenharmony_ci encoder->possible_clones = 0; 5998c2ecf20Sopenharmony_ci drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type, 6008c2ecf20Sopenharmony_ci "dac-%04x-%04x", dcbe->hasht, dcbe->hashm); 6018c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &nv50_dac_help); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/* 6088c2ecf20Sopenharmony_ci * audio component binding for ELD notification 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_cistatic void 6118c2ecf20Sopenharmony_cinv50_audio_component_eld_notify(struct drm_audio_component *acomp, int port, 6128c2ecf20Sopenharmony_ci int dev_id) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) 6158c2ecf20Sopenharmony_ci acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, 6168c2ecf20Sopenharmony_ci port, dev_id); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int 6208c2ecf20Sopenharmony_cinv50_audio_component_get_eld(struct device *kdev, int port, int dev_id, 6218c2ecf20Sopenharmony_ci bool *enabled, unsigned char *buf, int max_bytes) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(kdev); 6248c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(drm_dev); 6258c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 6268c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder; 6278c2ecf20Sopenharmony_ci struct drm_connector *connector; 6288c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc; 6298c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 6308c2ecf20Sopenharmony_ci int ret = 0; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci *enabled = false; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci drm_for_each_encoder(encoder, drm->dev) { 6358c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector = NULL; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci nv_encoder = nouveau_encoder(encoder); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(drm_dev, &conn_iter); 6408c2ecf20Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 6418c2ecf20Sopenharmony_ci if (connector->state->best_encoder == encoder) { 6428c2ecf20Sopenharmony_ci nv_connector = nouveau_connector(connector); 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 6478c2ecf20Sopenharmony_ci if (!nv_connector) 6488c2ecf20Sopenharmony_ci continue; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci nv_crtc = nouveau_crtc(encoder->crtc); 6518c2ecf20Sopenharmony_ci if (!nv_crtc || nv_encoder->or != port || 6528c2ecf20Sopenharmony_ci nv_crtc->index != dev_id) 6538c2ecf20Sopenharmony_ci continue; 6548c2ecf20Sopenharmony_ci *enabled = nv_encoder->audio; 6558c2ecf20Sopenharmony_ci if (*enabled) { 6568c2ecf20Sopenharmony_ci ret = drm_eld_size(nv_connector->base.eld); 6578c2ecf20Sopenharmony_ci memcpy(buf, nv_connector->base.eld, 6588c2ecf20Sopenharmony_ci min(max_bytes, ret)); 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return ret; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic const struct drm_audio_component_ops nv50_audio_component_ops = { 6678c2ecf20Sopenharmony_ci .get_eld = nv50_audio_component_get_eld, 6688c2ecf20Sopenharmony_ci}; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic int 6718c2ecf20Sopenharmony_cinv50_audio_component_bind(struct device *kdev, struct device *hda_kdev, 6728c2ecf20Sopenharmony_ci void *data) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(kdev); 6758c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(drm_dev); 6768c2ecf20Sopenharmony_ci struct drm_audio_component *acomp = data; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (WARN_ON(!device_link_add(hda_kdev, kdev, DL_FLAG_STATELESS))) 6798c2ecf20Sopenharmony_ci return -ENOMEM; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci drm_modeset_lock_all(drm_dev); 6828c2ecf20Sopenharmony_ci acomp->ops = &nv50_audio_component_ops; 6838c2ecf20Sopenharmony_ci acomp->dev = kdev; 6848c2ecf20Sopenharmony_ci drm->audio.component = acomp; 6858c2ecf20Sopenharmony_ci drm_modeset_unlock_all(drm_dev); 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic void 6908c2ecf20Sopenharmony_cinv50_audio_component_unbind(struct device *kdev, struct device *hda_kdev, 6918c2ecf20Sopenharmony_ci void *data) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct drm_device *drm_dev = dev_get_drvdata(kdev); 6948c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(drm_dev); 6958c2ecf20Sopenharmony_ci struct drm_audio_component *acomp = data; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci drm_modeset_lock_all(drm_dev); 6988c2ecf20Sopenharmony_ci drm->audio.component = NULL; 6998c2ecf20Sopenharmony_ci acomp->ops = NULL; 7008c2ecf20Sopenharmony_ci acomp->dev = NULL; 7018c2ecf20Sopenharmony_ci drm_modeset_unlock_all(drm_dev); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic const struct component_ops nv50_audio_component_bind_ops = { 7058c2ecf20Sopenharmony_ci .bind = nv50_audio_component_bind, 7068c2ecf20Sopenharmony_ci .unbind = nv50_audio_component_unbind, 7078c2ecf20Sopenharmony_ci}; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic void 7108c2ecf20Sopenharmony_cinv50_audio_component_init(struct nouveau_drm *drm) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci if (!component_add(drm->dev->dev, &nv50_audio_component_bind_ops)) 7138c2ecf20Sopenharmony_ci drm->audio.component_registered = true; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic void 7178c2ecf20Sopenharmony_cinv50_audio_component_fini(struct nouveau_drm *drm) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci if (drm->audio.component_registered) { 7208c2ecf20Sopenharmony_ci component_del(drm->dev->dev, &nv50_audio_component_bind_ops); 7218c2ecf20Sopenharmony_ci drm->audio.component_registered = false; 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/****************************************************************************** 7268c2ecf20Sopenharmony_ci * Audio 7278c2ecf20Sopenharmony_ci *****************************************************************************/ 7288c2ecf20Sopenharmony_cistatic void 7298c2ecf20Sopenharmony_cinv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 7328c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 7338c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(encoder->dev); 7348c2ecf20Sopenharmony_ci struct { 7358c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 7368c2ecf20Sopenharmony_ci struct nv50_disp_sor_hda_eld_v0 eld; 7378c2ecf20Sopenharmony_ci } args = { 7388c2ecf20Sopenharmony_ci .base.version = 1, 7398c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, 7408c2ecf20Sopenharmony_ci .base.hasht = nv_encoder->dcb->hasht, 7418c2ecf20Sopenharmony_ci .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | 7428c2ecf20Sopenharmony_ci (0x0100 << nv_crtc->index), 7438c2ecf20Sopenharmony_ci }; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (!nv_encoder->audio) 7468c2ecf20Sopenharmony_ci return; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci nv_encoder->audio = false; 7498c2ecf20Sopenharmony_ci nvif_mthd(&disp->disp->object, 0, &args, sizeof(args)); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci nv50_audio_component_eld_notify(drm->audio.component, nv_encoder->or, 7528c2ecf20Sopenharmony_ci nv_crtc->index); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic void 7568c2ecf20Sopenharmony_cinv50_audio_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, 7578c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 7608c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 7618c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 7628c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector; 7638c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(encoder->dev); 7648c2ecf20Sopenharmony_ci struct __packed { 7658c2ecf20Sopenharmony_ci struct { 7668c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 mthd; 7678c2ecf20Sopenharmony_ci struct nv50_disp_sor_hda_eld_v0 eld; 7688c2ecf20Sopenharmony_ci } base; 7698c2ecf20Sopenharmony_ci u8 data[sizeof(nv_connector->base.eld)]; 7708c2ecf20Sopenharmony_ci } args = { 7718c2ecf20Sopenharmony_ci .base.mthd.version = 1, 7728c2ecf20Sopenharmony_ci .base.mthd.method = NV50_DISP_MTHD_V1_SOR_HDA_ELD, 7738c2ecf20Sopenharmony_ci .base.mthd.hasht = nv_encoder->dcb->hasht, 7748c2ecf20Sopenharmony_ci .base.mthd.hashm = (0xf0ff & nv_encoder->dcb->hashm) | 7758c2ecf20Sopenharmony_ci (0x0100 << nv_crtc->index), 7768c2ecf20Sopenharmony_ci }; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci nv_connector = nv50_outp_get_new_connector(nv_encoder, state); 7798c2ecf20Sopenharmony_ci if (!drm_detect_monitor_audio(nv_connector->edid)) 7808c2ecf20Sopenharmony_ci return; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci memcpy(args.data, nv_connector->base.eld, sizeof(args.data)); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci nvif_mthd(&disp->disp->object, 0, &args, 7858c2ecf20Sopenharmony_ci sizeof(args.base) + drm_eld_size(args.data)); 7868c2ecf20Sopenharmony_ci nv_encoder->audio = true; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci nv50_audio_component_eld_notify(drm->audio.component, nv_encoder->or, 7898c2ecf20Sopenharmony_ci nv_crtc->index); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/****************************************************************************** 7938c2ecf20Sopenharmony_ci * HDMI 7948c2ecf20Sopenharmony_ci *****************************************************************************/ 7958c2ecf20Sopenharmony_cistatic void 7968c2ecf20Sopenharmony_cinv50_hdmi_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) 7978c2ecf20Sopenharmony_ci{ 7988c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 7998c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(encoder->dev); 8008c2ecf20Sopenharmony_ci struct { 8018c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 8028c2ecf20Sopenharmony_ci struct nv50_disp_sor_hdmi_pwr_v0 pwr; 8038c2ecf20Sopenharmony_ci } args = { 8048c2ecf20Sopenharmony_ci .base.version = 1, 8058c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, 8068c2ecf20Sopenharmony_ci .base.hasht = nv_encoder->dcb->hasht, 8078c2ecf20Sopenharmony_ci .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | 8088c2ecf20Sopenharmony_ci (0x0100 << nv_crtc->index), 8098c2ecf20Sopenharmony_ci }; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci nvif_mthd(&disp->disp->object, 0, &args, sizeof(args)); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic void 8158c2ecf20Sopenharmony_cinv50_hdmi_enable(struct drm_encoder *encoder, struct drm_atomic_state *state, 8168c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(encoder->dev); 8198c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 8208c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 8218c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(encoder->dev); 8228c2ecf20Sopenharmony_ci struct { 8238c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 8248c2ecf20Sopenharmony_ci struct nv50_disp_sor_hdmi_pwr_v0 pwr; 8258c2ecf20Sopenharmony_ci u8 infoframes[2 * 17]; /* two frames, up to 17 bytes each */ 8268c2ecf20Sopenharmony_ci } args = { 8278c2ecf20Sopenharmony_ci .base.version = 1, 8288c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR, 8298c2ecf20Sopenharmony_ci .base.hasht = nv_encoder->dcb->hasht, 8308c2ecf20Sopenharmony_ci .base.hashm = (0xf0ff & nv_encoder->dcb->hashm) | 8318c2ecf20Sopenharmony_ci (0x0100 << nv_crtc->index), 8328c2ecf20Sopenharmony_ci .pwr.state = 1, 8338c2ecf20Sopenharmony_ci .pwr.rekey = 56, /* binary driver, and tegra, constant */ 8348c2ecf20Sopenharmony_ci }; 8358c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector; 8368c2ecf20Sopenharmony_ci struct drm_hdmi_info *hdmi; 8378c2ecf20Sopenharmony_ci u32 max_ac_packet; 8388c2ecf20Sopenharmony_ci union hdmi_infoframe avi_frame; 8398c2ecf20Sopenharmony_ci union hdmi_infoframe vendor_frame; 8408c2ecf20Sopenharmony_ci bool high_tmds_clock_ratio = false, scrambling = false; 8418c2ecf20Sopenharmony_ci u8 config; 8428c2ecf20Sopenharmony_ci int ret; 8438c2ecf20Sopenharmony_ci int size; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci nv_connector = nv50_outp_get_new_connector(nv_encoder, state); 8468c2ecf20Sopenharmony_ci if (!drm_detect_hdmi_monitor(nv_connector->edid)) 8478c2ecf20Sopenharmony_ci return; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci hdmi = &nv_connector->base.display_info.hdmi; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, 8528c2ecf20Sopenharmony_ci &nv_connector->base, mode); 8538c2ecf20Sopenharmony_ci if (!ret) { 8548c2ecf20Sopenharmony_ci /* We have an AVI InfoFrame, populate it to the display */ 8558c2ecf20Sopenharmony_ci args.pwr.avi_infoframe_length 8568c2ecf20Sopenharmony_ci = hdmi_infoframe_pack(&avi_frame, args.infoframes, 17); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, 8608c2ecf20Sopenharmony_ci &nv_connector->base, mode); 8618c2ecf20Sopenharmony_ci if (!ret) { 8628c2ecf20Sopenharmony_ci /* We have a Vendor InfoFrame, populate it to the display */ 8638c2ecf20Sopenharmony_ci args.pwr.vendor_infoframe_length 8648c2ecf20Sopenharmony_ci = hdmi_infoframe_pack(&vendor_frame, 8658c2ecf20Sopenharmony_ci args.infoframes 8668c2ecf20Sopenharmony_ci + args.pwr.avi_infoframe_length, 8678c2ecf20Sopenharmony_ci 17); 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci max_ac_packet = mode->htotal - mode->hdisplay; 8718c2ecf20Sopenharmony_ci max_ac_packet -= args.pwr.rekey; 8728c2ecf20Sopenharmony_ci max_ac_packet -= 18; /* constant from tegra */ 8738c2ecf20Sopenharmony_ci args.pwr.max_ac_packet = max_ac_packet / 32; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (hdmi->scdc.scrambling.supported) { 8768c2ecf20Sopenharmony_ci high_tmds_clock_ratio = mode->clock > 340000; 8778c2ecf20Sopenharmony_ci scrambling = high_tmds_clock_ratio || 8788c2ecf20Sopenharmony_ci hdmi->scdc.scrambling.low_rates; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci args.pwr.scdc = 8828c2ecf20Sopenharmony_ci NV50_DISP_SOR_HDMI_PWR_V0_SCDC_SCRAMBLE * scrambling | 8838c2ecf20Sopenharmony_ci NV50_DISP_SOR_HDMI_PWR_V0_SCDC_DIV_BY_4 * high_tmds_clock_ratio; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci size = sizeof(args.base) 8868c2ecf20Sopenharmony_ci + sizeof(args.pwr) 8878c2ecf20Sopenharmony_ci + args.pwr.avi_infoframe_length 8888c2ecf20Sopenharmony_ci + args.pwr.vendor_infoframe_length; 8898c2ecf20Sopenharmony_ci nvif_mthd(&disp->disp->object, 0, &args, size); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci nv50_audio_enable(encoder, state, mode); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* If SCDC is supported by the downstream monitor, update 8948c2ecf20Sopenharmony_ci * divider / scrambling settings to what we programmed above. 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci if (!hdmi->scdc.scrambling.supported) 8978c2ecf20Sopenharmony_ci return; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &config); 9008c2ecf20Sopenharmony_ci if (ret < 0) { 9018c2ecf20Sopenharmony_ci NV_ERROR(drm, "Failure to read SCDC_TMDS_CONFIG: %d\n", ret); 9028c2ecf20Sopenharmony_ci return; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci config &= ~(SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE); 9058c2ecf20Sopenharmony_ci config |= SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 * high_tmds_clock_ratio; 9068c2ecf20Sopenharmony_ci config |= SCDC_SCRAMBLING_ENABLE * scrambling; 9078c2ecf20Sopenharmony_ci ret = drm_scdc_writeb(nv_encoder->i2c, SCDC_TMDS_CONFIG, config); 9088c2ecf20Sopenharmony_ci if (ret < 0) 9098c2ecf20Sopenharmony_ci NV_ERROR(drm, "Failure to write SCDC_TMDS_CONFIG = 0x%02x: %d\n", 9108c2ecf20Sopenharmony_ci config, ret); 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci/****************************************************************************** 9148c2ecf20Sopenharmony_ci * MST 9158c2ecf20Sopenharmony_ci *****************************************************************************/ 9168c2ecf20Sopenharmony_ci#define nv50_mstm(p) container_of((p), struct nv50_mstm, mgr) 9178c2ecf20Sopenharmony_ci#define nv50_mstc(p) container_of((p), struct nv50_mstc, connector) 9188c2ecf20Sopenharmony_ci#define nv50_msto(p) container_of((p), struct nv50_msto, encoder) 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistruct nv50_mstc { 9218c2ecf20Sopenharmony_ci struct nv50_mstm *mstm; 9228c2ecf20Sopenharmony_ci struct drm_dp_mst_port *port; 9238c2ecf20Sopenharmony_ci struct drm_connector connector; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci struct drm_display_mode *native; 9268c2ecf20Sopenharmony_ci struct edid *edid; 9278c2ecf20Sopenharmony_ci}; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistruct nv50_msto { 9308c2ecf20Sopenharmony_ci struct drm_encoder encoder; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci struct nv50_head *head; 9338c2ecf20Sopenharmony_ci struct nv50_mstc *mstc; 9348c2ecf20Sopenharmony_ci bool disabled; 9358c2ecf20Sopenharmony_ci}; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistruct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct nv50_msto *msto; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) 9428c2ecf20Sopenharmony_ci return nouveau_encoder(encoder); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci msto = nv50_msto(encoder); 9458c2ecf20Sopenharmony_ci if (!msto->mstc) 9468c2ecf20Sopenharmony_ci return NULL; 9478c2ecf20Sopenharmony_ci return msto->mstc->mstm->outp; 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic struct drm_dp_payload * 9518c2ecf20Sopenharmony_cinv50_msto_payload(struct nv50_msto *msto) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); 9548c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = msto->mstc; 9558c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = mstc->mstm; 9568c2ecf20Sopenharmony_ci int vcpi = mstc->port->vcpi.vcpi, i; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&mstm->mgr.payload_lock)); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: vcpi %d\n", msto->encoder.name, vcpi); 9618c2ecf20Sopenharmony_ci for (i = 0; i < mstm->mgr.max_payloads; i++) { 9628c2ecf20Sopenharmony_ci struct drm_dp_payload *payload = &mstm->mgr.payloads[i]; 9638c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: %d: vcpi %d start 0x%02x slots 0x%02x\n", 9648c2ecf20Sopenharmony_ci mstm->outp->base.base.name, i, payload->vcpi, 9658c2ecf20Sopenharmony_ci payload->start_slot, payload->num_slots); 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci for (i = 0; i < mstm->mgr.max_payloads; i++) { 9698c2ecf20Sopenharmony_ci struct drm_dp_payload *payload = &mstm->mgr.payloads[i]; 9708c2ecf20Sopenharmony_ci if (payload->vcpi == vcpi) 9718c2ecf20Sopenharmony_ci return payload; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci return NULL; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cistatic void 9788c2ecf20Sopenharmony_cinv50_msto_cleanup(struct nv50_msto *msto) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); 9818c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = msto->mstc; 9828c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = mstc->mstm; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (!msto->disabled) 9858c2ecf20Sopenharmony_ci return; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: msto cleanup\n", msto->encoder.name); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci drm_dp_mst_deallocate_vcpi(&mstm->mgr, mstc->port); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci msto->mstc = NULL; 9928c2ecf20Sopenharmony_ci msto->disabled = false; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic void 9968c2ecf20Sopenharmony_cinv50_msto_prepare(struct nv50_msto *msto) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); 9998c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = msto->mstc; 10008c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = mstc->mstm; 10018c2ecf20Sopenharmony_ci struct { 10028c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 10038c2ecf20Sopenharmony_ci struct nv50_disp_sor_dp_mst_vcpi_v0 vcpi; 10048c2ecf20Sopenharmony_ci } args = { 10058c2ecf20Sopenharmony_ci .base.version = 1, 10068c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI, 10078c2ecf20Sopenharmony_ci .base.hasht = mstm->outp->dcb->hasht, 10088c2ecf20Sopenharmony_ci .base.hashm = (0xf0ff & mstm->outp->dcb->hashm) | 10098c2ecf20Sopenharmony_ci (0x0100 << msto->head->base.index), 10108c2ecf20Sopenharmony_ci }; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci mutex_lock(&mstm->mgr.payload_lock); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name); 10158c2ecf20Sopenharmony_ci if (mstc->port->vcpi.vcpi > 0) { 10168c2ecf20Sopenharmony_ci struct drm_dp_payload *payload = nv50_msto_payload(msto); 10178c2ecf20Sopenharmony_ci if (payload) { 10188c2ecf20Sopenharmony_ci args.vcpi.start_slot = payload->start_slot; 10198c2ecf20Sopenharmony_ci args.vcpi.num_slots = payload->num_slots; 10208c2ecf20Sopenharmony_ci args.vcpi.pbn = mstc->port->vcpi.pbn; 10218c2ecf20Sopenharmony_ci args.vcpi.aligned_pbn = mstc->port->vcpi.aligned_pbn; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: %s: %02x %02x %04x %04x\n", 10268c2ecf20Sopenharmony_ci msto->encoder.name, msto->head->base.base.name, 10278c2ecf20Sopenharmony_ci args.vcpi.start_slot, args.vcpi.num_slots, 10288c2ecf20Sopenharmony_ci args.vcpi.pbn, args.vcpi.aligned_pbn); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci nvif_mthd(&drm->display->disp.object, 0, &args, sizeof(args)); 10318c2ecf20Sopenharmony_ci mutex_unlock(&mstm->mgr.payload_lock); 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_cistatic int 10358c2ecf20Sopenharmony_cinv50_msto_atomic_check(struct drm_encoder *encoder, 10368c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 10378c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci struct drm_atomic_state *state = crtc_state->state; 10408c2ecf20Sopenharmony_ci struct drm_connector *connector = conn_state->connector; 10418c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = nv50_mstc(connector); 10428c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = mstc->mstm; 10438c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); 10448c2ecf20Sopenharmony_ci int slots; 10458c2ecf20Sopenharmony_ci int ret; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci ret = nv50_outp_atomic_check_view(encoder, crtc_state, conn_state, 10488c2ecf20Sopenharmony_ci mstc->native); 10498c2ecf20Sopenharmony_ci if (ret) 10508c2ecf20Sopenharmony_ci return ret; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (!crtc_state->mode_changed && !crtc_state->connectors_changed) 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* 10568c2ecf20Sopenharmony_ci * When restoring duplicated states, we need to make sure that the bw 10578c2ecf20Sopenharmony_ci * remains the same and avoid recalculating it, as the connector's bpc 10588c2ecf20Sopenharmony_ci * may have changed after the state was duplicated 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_ci if (!state->duplicated) { 10618c2ecf20Sopenharmony_ci const int clock = crtc_state->adjusted_mode.clock; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci asyh->or.bpc = connector->display_info.bpc; 10648c2ecf20Sopenharmony_ci asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3, 10658c2ecf20Sopenharmony_ci false); 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port, 10698c2ecf20Sopenharmony_ci asyh->dp.pbn, 0); 10708c2ecf20Sopenharmony_ci if (slots < 0) 10718c2ecf20Sopenharmony_ci return slots; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci asyh->dp.tu = slots; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci return 0; 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cistatic u8 10798c2ecf20Sopenharmony_cinv50_dp_bpc_to_depth(unsigned int bpc) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci switch (bpc) { 10828c2ecf20Sopenharmony_ci case 6: return NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_18_444; 10838c2ecf20Sopenharmony_ci case 8: return NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_24_444; 10848c2ecf20Sopenharmony_ci case 10: 10858c2ecf20Sopenharmony_ci default: return NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_BPP_30_444; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic void 10908c2ecf20Sopenharmony_cinv50_msto_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(encoder->crtc); 10938c2ecf20Sopenharmony_ci struct nv50_head_atom *armh = nv50_head_atom(head->base.base.state); 10948c2ecf20Sopenharmony_ci struct nv50_msto *msto = nv50_msto(encoder); 10958c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = NULL; 10968c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = NULL; 10978c2ecf20Sopenharmony_ci struct drm_connector *connector; 10988c2ecf20Sopenharmony_ci struct drm_connector_list_iter conn_iter; 10998c2ecf20Sopenharmony_ci u8 proto; 11008c2ecf20Sopenharmony_ci bool r; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci drm_connector_list_iter_begin(encoder->dev, &conn_iter); 11038c2ecf20Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 11048c2ecf20Sopenharmony_ci if (connector->state->best_encoder == &msto->encoder) { 11058c2ecf20Sopenharmony_ci mstc = nv50_mstc(connector); 11068c2ecf20Sopenharmony_ci mstm = mstc->mstm; 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci if (WARN_ON(!mstc)) 11138c2ecf20Sopenharmony_ci return; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, armh->dp.pbn, 11168c2ecf20Sopenharmony_ci armh->dp.tu); 11178c2ecf20Sopenharmony_ci if (!r) 11188c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("Failed to allocate VCPI\n"); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (!mstm->links++) 11218c2ecf20Sopenharmony_ci nv50_outp_acquire(mstm->outp, false /*XXX: MST audio.*/); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (mstm->outp->link & 1) 11248c2ecf20Sopenharmony_ci proto = NV917D_SOR_SET_CONTROL_PROTOCOL_DP_A; 11258c2ecf20Sopenharmony_ci else 11268c2ecf20Sopenharmony_ci proto = NV917D_SOR_SET_CONTROL_PROTOCOL_DP_B; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci mstm->outp->update(mstm->outp, head->base.index, armh, proto, 11298c2ecf20Sopenharmony_ci nv50_dp_bpc_to_depth(armh->or.bpc)); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci msto->mstc = mstc; 11328c2ecf20Sopenharmony_ci mstm->modified = true; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cistatic void 11368c2ecf20Sopenharmony_cinv50_msto_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct nv50_msto *msto = nv50_msto(encoder); 11398c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = msto->mstc; 11408c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = mstc->mstm; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci drm_dp_mst_reset_vcpi_slots(&mstm->mgr, mstc->port); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); 11458c2ecf20Sopenharmony_ci mstm->modified = true; 11468c2ecf20Sopenharmony_ci if (!--mstm->links) 11478c2ecf20Sopenharmony_ci mstm->disabled = true; 11488c2ecf20Sopenharmony_ci msto->disabled = true; 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs 11528c2ecf20Sopenharmony_cinv50_msto_help = { 11538c2ecf20Sopenharmony_ci .atomic_disable = nv50_msto_disable, 11548c2ecf20Sopenharmony_ci .atomic_enable = nv50_msto_enable, 11558c2ecf20Sopenharmony_ci .atomic_check = nv50_msto_atomic_check, 11568c2ecf20Sopenharmony_ci}; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic void 11598c2ecf20Sopenharmony_cinv50_msto_destroy(struct drm_encoder *encoder) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci struct nv50_msto *msto = nv50_msto(encoder); 11628c2ecf20Sopenharmony_ci drm_encoder_cleanup(&msto->encoder); 11638c2ecf20Sopenharmony_ci kfree(msto); 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs 11678c2ecf20Sopenharmony_cinv50_msto = { 11688c2ecf20Sopenharmony_ci .destroy = nv50_msto_destroy, 11698c2ecf20Sopenharmony_ci}; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic struct nv50_msto * 11728c2ecf20Sopenharmony_cinv50_msto_new(struct drm_device *dev, struct nv50_head *head, int id) 11738c2ecf20Sopenharmony_ci{ 11748c2ecf20Sopenharmony_ci struct nv50_msto *msto; 11758c2ecf20Sopenharmony_ci int ret; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci msto = kzalloc(sizeof(*msto), GFP_KERNEL); 11788c2ecf20Sopenharmony_ci if (!msto) 11798c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci ret = drm_encoder_init(dev, &msto->encoder, &nv50_msto, 11828c2ecf20Sopenharmony_ci DRM_MODE_ENCODER_DPMST, "mst-%d", id); 11838c2ecf20Sopenharmony_ci if (ret) { 11848c2ecf20Sopenharmony_ci kfree(msto); 11858c2ecf20Sopenharmony_ci return ERR_PTR(ret); 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci drm_encoder_helper_add(&msto->encoder, &nv50_msto_help); 11898c2ecf20Sopenharmony_ci msto->encoder.possible_crtcs = drm_crtc_mask(&head->base.base); 11908c2ecf20Sopenharmony_ci msto->head = head; 11918c2ecf20Sopenharmony_ci return msto; 11928c2ecf20Sopenharmony_ci} 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic struct drm_encoder * 11958c2ecf20Sopenharmony_cinv50_mstc_atomic_best_encoder(struct drm_connector *connector, 11968c2ecf20Sopenharmony_ci struct drm_connector_state *connector_state) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = nv50_mstc(connector); 11998c2ecf20Sopenharmony_ci struct drm_crtc *crtc = connector_state->crtc; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci if (!(mstc->mstm->outp->dcb->heads & drm_crtc_mask(crtc))) 12028c2ecf20Sopenharmony_ci return NULL; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci return &nv50_head(crtc)->msto->encoder; 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic enum drm_mode_status 12088c2ecf20Sopenharmony_cinv50_mstc_mode_valid(struct drm_connector *connector, 12098c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = nv50_mstc(connector); 12128c2ecf20Sopenharmony_ci struct nouveau_encoder *outp = mstc->mstm->outp; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* TODO: calculate the PBN from the dotclock and validate against the 12158c2ecf20Sopenharmony_ci * MSTB's max possible PBN 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci return nv50_dp_mode_valid(connector, outp, mode, NULL); 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic int 12228c2ecf20Sopenharmony_cinv50_mstc_get_modes(struct drm_connector *connector) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = nv50_mstc(connector); 12258c2ecf20Sopenharmony_ci int ret = 0; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci mstc->edid = drm_dp_mst_get_edid(&mstc->connector, mstc->port->mgr, mstc->port); 12288c2ecf20Sopenharmony_ci drm_connector_update_edid_property(&mstc->connector, mstc->edid); 12298c2ecf20Sopenharmony_ci if (mstc->edid) 12308c2ecf20Sopenharmony_ci ret = drm_add_edid_modes(&mstc->connector, mstc->edid); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* 12338c2ecf20Sopenharmony_ci * XXX: Since we don't use HDR in userspace quite yet, limit the bpc 12348c2ecf20Sopenharmony_ci * to 8 to save bandwidth on the topology. In the future, we'll want 12358c2ecf20Sopenharmony_ci * to properly fix this by dynamically selecting the highest possible 12368c2ecf20Sopenharmony_ci * bpc that would fit in the topology 12378c2ecf20Sopenharmony_ci */ 12388c2ecf20Sopenharmony_ci if (connector->display_info.bpc) 12398c2ecf20Sopenharmony_ci connector->display_info.bpc = 12408c2ecf20Sopenharmony_ci clamp(connector->display_info.bpc, 6U, 8U); 12418c2ecf20Sopenharmony_ci else 12428c2ecf20Sopenharmony_ci connector->display_info.bpc = 8; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci if (mstc->native) 12458c2ecf20Sopenharmony_ci drm_mode_destroy(mstc->connector.dev, mstc->native); 12468c2ecf20Sopenharmony_ci mstc->native = nouveau_conn_native_mode(&mstc->connector); 12478c2ecf20Sopenharmony_ci return ret; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic int 12518c2ecf20Sopenharmony_cinv50_mstc_atomic_check(struct drm_connector *connector, 12528c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = nv50_mstc(connector); 12558c2ecf20Sopenharmony_ci struct drm_dp_mst_topology_mgr *mgr = &mstc->mstm->mgr; 12568c2ecf20Sopenharmony_ci struct drm_connector_state *new_conn_state = 12578c2ecf20Sopenharmony_ci drm_atomic_get_new_connector_state(state, connector); 12588c2ecf20Sopenharmony_ci struct drm_connector_state *old_conn_state = 12598c2ecf20Sopenharmony_ci drm_atomic_get_old_connector_state(state, connector); 12608c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state; 12618c2ecf20Sopenharmony_ci struct drm_crtc *new_crtc = new_conn_state->crtc; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (!old_conn_state->crtc) 12648c2ecf20Sopenharmony_ci return 0; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* We only want to free VCPI if this state disables the CRTC on this 12678c2ecf20Sopenharmony_ci * connector 12688c2ecf20Sopenharmony_ci */ 12698c2ecf20Sopenharmony_ci if (new_crtc) { 12708c2ecf20Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (!crtc_state || 12738c2ecf20Sopenharmony_ci !drm_atomic_crtc_needs_modeset(crtc_state) || 12748c2ecf20Sopenharmony_ci crtc_state->enable) 12758c2ecf20Sopenharmony_ci return 0; 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci return drm_dp_atomic_release_vcpi_slots(state, mgr, mstc->port); 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic int 12828c2ecf20Sopenharmony_cinv50_mstc_detect(struct drm_connector *connector, 12838c2ecf20Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx, bool force) 12848c2ecf20Sopenharmony_ci{ 12858c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = nv50_mstc(connector); 12868c2ecf20Sopenharmony_ci int ret; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (drm_connector_is_unregistered(connector)) 12898c2ecf20Sopenharmony_ci return connector_status_disconnected; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(connector->dev->dev); 12928c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 12938c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(connector->dev->dev); 12948c2ecf20Sopenharmony_ci return connector_status_disconnected; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci ret = drm_dp_mst_detect_port(connector, ctx, mstc->port->mgr, 12988c2ecf20Sopenharmony_ci mstc->port); 12998c2ecf20Sopenharmony_ci if (ret != connector_status_connected) 13008c2ecf20Sopenharmony_ci goto out; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ciout: 13038c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(connector->dev->dev); 13048c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(connector->dev->dev); 13058c2ecf20Sopenharmony_ci return ret; 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs 13098c2ecf20Sopenharmony_cinv50_mstc_help = { 13108c2ecf20Sopenharmony_ci .get_modes = nv50_mstc_get_modes, 13118c2ecf20Sopenharmony_ci .mode_valid = nv50_mstc_mode_valid, 13128c2ecf20Sopenharmony_ci .atomic_best_encoder = nv50_mstc_atomic_best_encoder, 13138c2ecf20Sopenharmony_ci .atomic_check = nv50_mstc_atomic_check, 13148c2ecf20Sopenharmony_ci .detect_ctx = nv50_mstc_detect, 13158c2ecf20Sopenharmony_ci}; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic void 13188c2ecf20Sopenharmony_cinv50_mstc_destroy(struct drm_connector *connector) 13198c2ecf20Sopenharmony_ci{ 13208c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = nv50_mstc(connector); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci drm_connector_cleanup(&mstc->connector); 13238c2ecf20Sopenharmony_ci drm_dp_mst_put_port_malloc(mstc->port); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci kfree(mstc); 13268c2ecf20Sopenharmony_ci} 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs 13298c2ecf20Sopenharmony_cinv50_mstc = { 13308c2ecf20Sopenharmony_ci .reset = nouveau_conn_reset, 13318c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 13328c2ecf20Sopenharmony_ci .destroy = nv50_mstc_destroy, 13338c2ecf20Sopenharmony_ci .atomic_duplicate_state = nouveau_conn_atomic_duplicate_state, 13348c2ecf20Sopenharmony_ci .atomic_destroy_state = nouveau_conn_atomic_destroy_state, 13358c2ecf20Sopenharmony_ci .atomic_set_property = nouveau_conn_atomic_set_property, 13368c2ecf20Sopenharmony_ci .atomic_get_property = nouveau_conn_atomic_get_property, 13378c2ecf20Sopenharmony_ci}; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_cistatic int 13408c2ecf20Sopenharmony_cinv50_mstc_new(struct nv50_mstm *mstm, struct drm_dp_mst_port *port, 13418c2ecf20Sopenharmony_ci const char *path, struct nv50_mstc **pmstc) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci struct drm_device *dev = mstm->outp->base.base.dev; 13448c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 13458c2ecf20Sopenharmony_ci struct nv50_mstc *mstc; 13468c2ecf20Sopenharmony_ci int ret; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (!(mstc = *pmstc = kzalloc(sizeof(*mstc), GFP_KERNEL))) 13498c2ecf20Sopenharmony_ci return -ENOMEM; 13508c2ecf20Sopenharmony_ci mstc->mstm = mstm; 13518c2ecf20Sopenharmony_ci mstc->port = port; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci ret = drm_connector_init(dev, &mstc->connector, &nv50_mstc, 13548c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DisplayPort); 13558c2ecf20Sopenharmony_ci if (ret) { 13568c2ecf20Sopenharmony_ci kfree(*pmstc); 13578c2ecf20Sopenharmony_ci *pmstc = NULL; 13588c2ecf20Sopenharmony_ci return ret; 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci drm_connector_helper_add(&mstc->connector, &nv50_mstc_help); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci mstc->connector.funcs->reset(&mstc->connector); 13648c2ecf20Sopenharmony_ci nouveau_conn_attach_properties(&mstc->connector); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 13678c2ecf20Sopenharmony_ci if (!(mstm->outp->dcb->heads & drm_crtc_mask(crtc))) 13688c2ecf20Sopenharmony_ci continue; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci drm_connector_attach_encoder(&mstc->connector, 13718c2ecf20Sopenharmony_ci &nv50_head(crtc)->msto->encoder); 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci drm_object_attach_property(&mstc->connector.base, dev->mode_config.path_property, 0); 13758c2ecf20Sopenharmony_ci drm_object_attach_property(&mstc->connector.base, dev->mode_config.tile_property, 0); 13768c2ecf20Sopenharmony_ci drm_connector_set_path_property(&mstc->connector, path); 13778c2ecf20Sopenharmony_ci drm_dp_mst_get_port_malloc(port); 13788c2ecf20Sopenharmony_ci return 0; 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_cistatic void 13828c2ecf20Sopenharmony_cinv50_mstm_cleanup(struct nv50_mstm *mstm) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev); 13858c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 13868c2ecf20Sopenharmony_ci int ret; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: mstm cleanup\n", mstm->outp->base.base.name); 13898c2ecf20Sopenharmony_ci ret = drm_dp_check_act_status(&mstm->mgr); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci ret = drm_dp_update_payload_part2(&mstm->mgr); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci drm_for_each_encoder(encoder, mstm->outp->base.base.dev) { 13948c2ecf20Sopenharmony_ci if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { 13958c2ecf20Sopenharmony_ci struct nv50_msto *msto = nv50_msto(encoder); 13968c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = msto->mstc; 13978c2ecf20Sopenharmony_ci if (mstc && mstc->mstm == mstm) 13988c2ecf20Sopenharmony_ci nv50_msto_cleanup(msto); 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci mstm->modified = false; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic void 14068c2ecf20Sopenharmony_cinv50_mstm_prepare(struct nv50_mstm *mstm) 14078c2ecf20Sopenharmony_ci{ 14088c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev); 14098c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 14108c2ecf20Sopenharmony_ci int ret; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: mstm prepare\n", mstm->outp->base.base.name); 14138c2ecf20Sopenharmony_ci ret = drm_dp_update_payload_part1(&mstm->mgr); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci drm_for_each_encoder(encoder, mstm->outp->base.base.dev) { 14168c2ecf20Sopenharmony_ci if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { 14178c2ecf20Sopenharmony_ci struct nv50_msto *msto = nv50_msto(encoder); 14188c2ecf20Sopenharmony_ci struct nv50_mstc *mstc = msto->mstc; 14198c2ecf20Sopenharmony_ci if (mstc && mstc->mstm == mstm) 14208c2ecf20Sopenharmony_ci nv50_msto_prepare(msto); 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if (mstm->disabled) { 14258c2ecf20Sopenharmony_ci if (!mstm->links) 14268c2ecf20Sopenharmony_ci nv50_outp_release(mstm->outp); 14278c2ecf20Sopenharmony_ci mstm->disabled = false; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci} 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_cistatic struct drm_connector * 14328c2ecf20Sopenharmony_cinv50_mstm_add_connector(struct drm_dp_mst_topology_mgr *mgr, 14338c2ecf20Sopenharmony_ci struct drm_dp_mst_port *port, const char *path) 14348c2ecf20Sopenharmony_ci{ 14358c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = nv50_mstm(mgr); 14368c2ecf20Sopenharmony_ci struct nv50_mstc *mstc; 14378c2ecf20Sopenharmony_ci int ret; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci ret = nv50_mstc_new(mstm, port, path, &mstc); 14408c2ecf20Sopenharmony_ci if (ret) 14418c2ecf20Sopenharmony_ci return NULL; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci return &mstc->connector; 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_cistatic const struct drm_dp_mst_topology_cbs 14478c2ecf20Sopenharmony_cinv50_mstm = { 14488c2ecf20Sopenharmony_ci .add_connector = nv50_mstm_add_connector, 14498c2ecf20Sopenharmony_ci}; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cibool 14528c2ecf20Sopenharmony_cinv50_mstm_service(struct nouveau_drm *drm, 14538c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector, 14548c2ecf20Sopenharmony_ci struct nv50_mstm *mstm) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci struct drm_dp_aux *aux = &nv_connector->aux; 14578c2ecf20Sopenharmony_ci bool handled = true, ret = true; 14588c2ecf20Sopenharmony_ci int rc; 14598c2ecf20Sopenharmony_ci u8 esi[8] = {}; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci while (handled) { 14628c2ecf20Sopenharmony_ci rc = drm_dp_dpcd_read(aux, DP_SINK_COUNT_ESI, esi, 8); 14638c2ecf20Sopenharmony_ci if (rc != 8) { 14648c2ecf20Sopenharmony_ci ret = false; 14658c2ecf20Sopenharmony_ci break; 14668c2ecf20Sopenharmony_ci } 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci drm_dp_mst_hpd_irq(&mstm->mgr, esi, &handled); 14698c2ecf20Sopenharmony_ci if (!handled) 14708c2ecf20Sopenharmony_ci break; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci rc = drm_dp_dpcd_write(aux, DP_SINK_COUNT_ESI + 1, &esi[1], 14738c2ecf20Sopenharmony_ci 3); 14748c2ecf20Sopenharmony_ci if (rc != 3) { 14758c2ecf20Sopenharmony_ci ret = false; 14768c2ecf20Sopenharmony_ci break; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (!ret) 14818c2ecf20Sopenharmony_ci NV_DEBUG(drm, "Failed to handle ESI on %s: %d\n", 14828c2ecf20Sopenharmony_ci nv_connector->base.name, rc); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci return ret; 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_civoid 14888c2ecf20Sopenharmony_cinv50_mstm_remove(struct nv50_mstm *mstm) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci mstm->is_mst = false; 14918c2ecf20Sopenharmony_ci drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false); 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic int 14958c2ecf20Sopenharmony_cinv50_mstm_enable(struct nv50_mstm *mstm, int state) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct nouveau_encoder *outp = mstm->outp; 14988c2ecf20Sopenharmony_ci struct { 14998c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 15008c2ecf20Sopenharmony_ci struct nv50_disp_sor_dp_mst_link_v0 mst; 15018c2ecf20Sopenharmony_ci } args = { 15028c2ecf20Sopenharmony_ci .base.version = 1, 15038c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_SOR_DP_MST_LINK, 15048c2ecf20Sopenharmony_ci .base.hasht = outp->dcb->hasht, 15058c2ecf20Sopenharmony_ci .base.hashm = outp->dcb->hashm, 15068c2ecf20Sopenharmony_ci .mst.state = state, 15078c2ecf20Sopenharmony_ci }; 15088c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev); 15098c2ecf20Sopenharmony_ci struct nvif_object *disp = &drm->display->disp.object; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci return nvif_mthd(disp, 0, &args, sizeof(args)); 15128c2ecf20Sopenharmony_ci} 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ciint 15158c2ecf20Sopenharmony_cinv50_mstm_detect(struct nouveau_encoder *outp) 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = outp->dp.mstm; 15188c2ecf20Sopenharmony_ci struct drm_dp_aux *aux; 15198c2ecf20Sopenharmony_ci int ret; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci if (!mstm || !mstm->can_mst) 15228c2ecf20Sopenharmony_ci return 0; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci aux = mstm->mgr.aux; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* Clear any leftover MST state we didn't set ourselves by first 15278c2ecf20Sopenharmony_ci * disabling MST if it was already enabled 15288c2ecf20Sopenharmony_ci */ 15298c2ecf20Sopenharmony_ci ret = drm_dp_dpcd_writeb(aux, DP_MSTM_CTRL, 0); 15308c2ecf20Sopenharmony_ci if (ret < 0) 15318c2ecf20Sopenharmony_ci return ret; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci /* And start enabling */ 15348c2ecf20Sopenharmony_ci ret = nv50_mstm_enable(mstm, true); 15358c2ecf20Sopenharmony_ci if (ret) 15368c2ecf20Sopenharmony_ci return ret; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, true); 15398c2ecf20Sopenharmony_ci if (ret) { 15408c2ecf20Sopenharmony_ci nv50_mstm_enable(mstm, false); 15418c2ecf20Sopenharmony_ci return ret; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci mstm->is_mst = true; 15458c2ecf20Sopenharmony_ci return 1; 15468c2ecf20Sopenharmony_ci} 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_cistatic void 15498c2ecf20Sopenharmony_cinv50_mstm_fini(struct nouveau_encoder *outp) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = outp->dp.mstm; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (!mstm) 15548c2ecf20Sopenharmony_ci return; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* Don't change the MST state of this connector until we've finished 15578c2ecf20Sopenharmony_ci * resuming, since we can't safely grab hpd_irq_lock in our resume 15588c2ecf20Sopenharmony_ci * path to protect mstm->is_mst without potentially deadlocking 15598c2ecf20Sopenharmony_ci */ 15608c2ecf20Sopenharmony_ci mutex_lock(&outp->dp.hpd_irq_lock); 15618c2ecf20Sopenharmony_ci mstm->suspended = true; 15628c2ecf20Sopenharmony_ci mutex_unlock(&outp->dp.hpd_irq_lock); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci if (mstm->is_mst) 15658c2ecf20Sopenharmony_ci drm_dp_mst_topology_mgr_suspend(&mstm->mgr); 15668c2ecf20Sopenharmony_ci} 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_cistatic void 15698c2ecf20Sopenharmony_cinv50_mstm_init(struct nouveau_encoder *outp, bool runtime) 15708c2ecf20Sopenharmony_ci{ 15718c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = outp->dp.mstm; 15728c2ecf20Sopenharmony_ci int ret = 0; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (!mstm) 15758c2ecf20Sopenharmony_ci return; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (mstm->is_mst) { 15788c2ecf20Sopenharmony_ci ret = drm_dp_mst_topology_mgr_resume(&mstm->mgr, !runtime); 15798c2ecf20Sopenharmony_ci if (ret == -1) 15808c2ecf20Sopenharmony_ci nv50_mstm_remove(mstm); 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci mutex_lock(&outp->dp.hpd_irq_lock); 15848c2ecf20Sopenharmony_ci mstm->suspended = false; 15858c2ecf20Sopenharmony_ci mutex_unlock(&outp->dp.hpd_irq_lock); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci if (ret == -1) 15888c2ecf20Sopenharmony_ci drm_kms_helper_hotplug_event(mstm->mgr.dev); 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic void 15928c2ecf20Sopenharmony_cinv50_mstm_del(struct nv50_mstm **pmstm) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci struct nv50_mstm *mstm = *pmstm; 15958c2ecf20Sopenharmony_ci if (mstm) { 15968c2ecf20Sopenharmony_ci drm_dp_mst_topology_mgr_destroy(&mstm->mgr); 15978c2ecf20Sopenharmony_ci kfree(*pmstm); 15988c2ecf20Sopenharmony_ci *pmstm = NULL; 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci} 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_cistatic int 16038c2ecf20Sopenharmony_cinv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max, 16048c2ecf20Sopenharmony_ci int conn_base_id, struct nv50_mstm **pmstm) 16058c2ecf20Sopenharmony_ci{ 16068c2ecf20Sopenharmony_ci const int max_payloads = hweight8(outp->dcb->heads); 16078c2ecf20Sopenharmony_ci struct drm_device *dev = outp->base.base.dev; 16088c2ecf20Sopenharmony_ci struct nv50_mstm *mstm; 16098c2ecf20Sopenharmony_ci int ret; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (!(mstm = *pmstm = kzalloc(sizeof(*mstm), GFP_KERNEL))) 16128c2ecf20Sopenharmony_ci return -ENOMEM; 16138c2ecf20Sopenharmony_ci mstm->outp = outp; 16148c2ecf20Sopenharmony_ci mstm->mgr.cbs = &nv50_mstm; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci ret = drm_dp_mst_topology_mgr_init(&mstm->mgr, dev, aux, aux_max, 16178c2ecf20Sopenharmony_ci max_payloads, conn_base_id); 16188c2ecf20Sopenharmony_ci if (ret) 16198c2ecf20Sopenharmony_ci return ret; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci return 0; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci/****************************************************************************** 16258c2ecf20Sopenharmony_ci * SOR 16268c2ecf20Sopenharmony_ci *****************************************************************************/ 16278c2ecf20Sopenharmony_cistatic void 16288c2ecf20Sopenharmony_cinv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head, 16298c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh, u8 proto, u8 depth) 16308c2ecf20Sopenharmony_ci{ 16318c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev); 16328c2ecf20Sopenharmony_ci struct nv50_core *core = disp->core; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci if (!asyh) { 16358c2ecf20Sopenharmony_ci nv_encoder->ctrl &= ~BIT(head); 16368c2ecf20Sopenharmony_ci if (NVDEF_TEST(nv_encoder->ctrl, NV507D, SOR_SET_CONTROL, OWNER, ==, NONE)) 16378c2ecf20Sopenharmony_ci nv_encoder->ctrl = 0; 16388c2ecf20Sopenharmony_ci } else { 16398c2ecf20Sopenharmony_ci nv_encoder->ctrl |= NVVAL(NV507D, SOR_SET_CONTROL, PROTOCOL, proto); 16408c2ecf20Sopenharmony_ci nv_encoder->ctrl |= BIT(head); 16418c2ecf20Sopenharmony_ci asyh->or.depth = depth; 16428c2ecf20Sopenharmony_ci } 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci core->func->sor->ctrl(core, nv_encoder->or, nv_encoder->ctrl, asyh); 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_cistatic void 16488c2ecf20Sopenharmony_cinv50_sor_disable(struct drm_encoder *encoder, 16498c2ecf20Sopenharmony_ci struct drm_atomic_state *state) 16508c2ecf20Sopenharmony_ci{ 16518c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 16528c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); 16538c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector = 16548c2ecf20Sopenharmony_ci nv50_outp_get_old_connector(nv_encoder, state); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci nv_encoder->crtc = NULL; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci if (nv_crtc) { 16598c2ecf20Sopenharmony_ci struct drm_dp_aux *aux = &nv_connector->aux; 16608c2ecf20Sopenharmony_ci u8 pwr; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { 16638c2ecf20Sopenharmony_ci int ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci if (ret == 0) { 16668c2ecf20Sopenharmony_ci pwr &= ~DP_SET_POWER_MASK; 16678c2ecf20Sopenharmony_ci pwr |= DP_SET_POWER_D3; 16688c2ecf20Sopenharmony_ci drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); 16738c2ecf20Sopenharmony_ci nv50_audio_disable(encoder, nv_crtc); 16748c2ecf20Sopenharmony_ci nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc); 16758c2ecf20Sopenharmony_ci nv50_outp_release(nv_encoder); 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci} 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_cistatic void 16808c2ecf20Sopenharmony_cinv50_sor_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) 16818c2ecf20Sopenharmony_ci{ 16828c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 16838c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 16848c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(nv_crtc->base.state); 16858c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &asyh->state.adjusted_mode; 16868c2ecf20Sopenharmony_ci struct { 16878c2ecf20Sopenharmony_ci struct nv50_disp_mthd_v1 base; 16888c2ecf20Sopenharmony_ci struct nv50_disp_sor_lvds_script_v0 lvds; 16898c2ecf20Sopenharmony_ci } lvds = { 16908c2ecf20Sopenharmony_ci .base.version = 1, 16918c2ecf20Sopenharmony_ci .base.method = NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT, 16928c2ecf20Sopenharmony_ci .base.hasht = nv_encoder->dcb->hasht, 16938c2ecf20Sopenharmony_ci .base.hashm = nv_encoder->dcb->hashm, 16948c2ecf20Sopenharmony_ci }; 16958c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(encoder->dev); 16968c2ecf20Sopenharmony_ci struct drm_device *dev = encoder->dev; 16978c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 16988c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector; 16998c2ecf20Sopenharmony_ci struct nvbios *bios = &drm->vbios; 17008c2ecf20Sopenharmony_ci bool hda = false; 17018c2ecf20Sopenharmony_ci u8 proto = NV507D_SOR_SET_CONTROL_PROTOCOL_CUSTOM; 17028c2ecf20Sopenharmony_ci u8 depth = NV837D_SOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci nv_connector = nv50_outp_get_new_connector(nv_encoder, state); 17058c2ecf20Sopenharmony_ci nv_encoder->crtc = encoder->crtc; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci if ((disp->disp->object.oclass == GT214_DISP || 17088c2ecf20Sopenharmony_ci disp->disp->object.oclass >= GF110_DISP) && 17098c2ecf20Sopenharmony_ci drm_detect_monitor_audio(nv_connector->edid)) 17108c2ecf20Sopenharmony_ci hda = true; 17118c2ecf20Sopenharmony_ci nv50_outp_acquire(nv_encoder, hda); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci switch (nv_encoder->dcb->type) { 17148c2ecf20Sopenharmony_ci case DCB_OUTPUT_TMDS: 17158c2ecf20Sopenharmony_ci if (nv_encoder->link & 1) { 17168c2ecf20Sopenharmony_ci proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_A; 17178c2ecf20Sopenharmony_ci /* Only enable dual-link if: 17188c2ecf20Sopenharmony_ci * - Need to (i.e. rate > 165MHz) 17198c2ecf20Sopenharmony_ci * - DCB says we can 17208c2ecf20Sopenharmony_ci * - Not an HDMI monitor, since there's no dual-link 17218c2ecf20Sopenharmony_ci * on HDMI. 17228c2ecf20Sopenharmony_ci */ 17238c2ecf20Sopenharmony_ci if (mode->clock >= 165000 && 17248c2ecf20Sopenharmony_ci nv_encoder->dcb->duallink_possible && 17258c2ecf20Sopenharmony_ci !drm_detect_hdmi_monitor(nv_connector->edid)) 17268c2ecf20Sopenharmony_ci proto = NV507D_SOR_SET_CONTROL_PROTOCOL_DUAL_TMDS; 17278c2ecf20Sopenharmony_ci } else { 17288c2ecf20Sopenharmony_ci proto = NV507D_SOR_SET_CONTROL_PROTOCOL_SINGLE_TMDS_B; 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci nv50_hdmi_enable(&nv_encoder->base.base, state, mode); 17328c2ecf20Sopenharmony_ci break; 17338c2ecf20Sopenharmony_ci case DCB_OUTPUT_LVDS: 17348c2ecf20Sopenharmony_ci proto = NV507D_SOR_SET_CONTROL_PROTOCOL_LVDS_CUSTOM; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci if (bios->fp_no_ddc) { 17378c2ecf20Sopenharmony_ci if (bios->fp.dual_link) 17388c2ecf20Sopenharmony_ci lvds.lvds.script |= 0x0100; 17398c2ecf20Sopenharmony_ci if (bios->fp.if_is_24bit) 17408c2ecf20Sopenharmony_ci lvds.lvds.script |= 0x0200; 17418c2ecf20Sopenharmony_ci } else { 17428c2ecf20Sopenharmony_ci if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { 17438c2ecf20Sopenharmony_ci if (((u8 *)nv_connector->edid)[121] == 2) 17448c2ecf20Sopenharmony_ci lvds.lvds.script |= 0x0100; 17458c2ecf20Sopenharmony_ci } else 17468c2ecf20Sopenharmony_ci if (mode->clock >= bios->fp.duallink_transition_clk) { 17478c2ecf20Sopenharmony_ci lvds.lvds.script |= 0x0100; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci if (lvds.lvds.script & 0x0100) { 17518c2ecf20Sopenharmony_ci if (bios->fp.strapless_is_24bit & 2) 17528c2ecf20Sopenharmony_ci lvds.lvds.script |= 0x0200; 17538c2ecf20Sopenharmony_ci } else { 17548c2ecf20Sopenharmony_ci if (bios->fp.strapless_is_24bit & 1) 17558c2ecf20Sopenharmony_ci lvds.lvds.script |= 0x0200; 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci if (asyh->or.bpc == 8) 17598c2ecf20Sopenharmony_ci lvds.lvds.script |= 0x0200; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci nvif_mthd(&disp->disp->object, 0, &lvds, sizeof(lvds)); 17638c2ecf20Sopenharmony_ci break; 17648c2ecf20Sopenharmony_ci case DCB_OUTPUT_DP: 17658c2ecf20Sopenharmony_ci depth = nv50_dp_bpc_to_depth(asyh->or.bpc); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci if (nv_encoder->link & 1) 17688c2ecf20Sopenharmony_ci proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_A; 17698c2ecf20Sopenharmony_ci else 17708c2ecf20Sopenharmony_ci proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci nv50_audio_enable(encoder, state, mode); 17738c2ecf20Sopenharmony_ci break; 17748c2ecf20Sopenharmony_ci default: 17758c2ecf20Sopenharmony_ci BUG(); 17768c2ecf20Sopenharmony_ci break; 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci nv_encoder->update(nv_encoder, nv_crtc->index, asyh, proto, depth); 17808c2ecf20Sopenharmony_ci} 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs 17838c2ecf20Sopenharmony_cinv50_sor_help = { 17848c2ecf20Sopenharmony_ci .atomic_check = nv50_outp_atomic_check, 17858c2ecf20Sopenharmony_ci .atomic_enable = nv50_sor_enable, 17868c2ecf20Sopenharmony_ci .atomic_disable = nv50_sor_disable, 17878c2ecf20Sopenharmony_ci}; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_cistatic void 17908c2ecf20Sopenharmony_cinv50_sor_destroy(struct drm_encoder *encoder) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 17938c2ecf20Sopenharmony_ci nv50_mstm_del(&nv_encoder->dp.mstm); 17948c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci if (nv_encoder->dcb->type == DCB_OUTPUT_DP) 17978c2ecf20Sopenharmony_ci mutex_destroy(&nv_encoder->dp.hpd_irq_lock); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci kfree(encoder); 18008c2ecf20Sopenharmony_ci} 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs 18038c2ecf20Sopenharmony_cinv50_sor_func = { 18048c2ecf20Sopenharmony_ci .destroy = nv50_sor_destroy, 18058c2ecf20Sopenharmony_ci}; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_cistatic bool nv50_has_mst(struct nouveau_drm *drm) 18088c2ecf20Sopenharmony_ci{ 18098c2ecf20Sopenharmony_ci struct nvkm_bios *bios = nvxx_bios(&drm->client.device); 18108c2ecf20Sopenharmony_ci u32 data; 18118c2ecf20Sopenharmony_ci u8 ver, hdr, cnt, len; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len); 18148c2ecf20Sopenharmony_ci return data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04); 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_cistatic int 18188c2ecf20Sopenharmony_cinv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) 18198c2ecf20Sopenharmony_ci{ 18208c2ecf20Sopenharmony_ci struct nouveau_connector *nv_connector = nouveau_connector(connector); 18218c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(connector->dev); 18228c2ecf20Sopenharmony_ci struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); 18238c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder; 18248c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 18258c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(connector->dev); 18268c2ecf20Sopenharmony_ci int type, ret; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci switch (dcbe->type) { 18298c2ecf20Sopenharmony_ci case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; 18308c2ecf20Sopenharmony_ci case DCB_OUTPUT_TMDS: 18318c2ecf20Sopenharmony_ci case DCB_OUTPUT_DP: 18328c2ecf20Sopenharmony_ci default: 18338c2ecf20Sopenharmony_ci type = DRM_MODE_ENCODER_TMDS; 18348c2ecf20Sopenharmony_ci break; 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); 18388c2ecf20Sopenharmony_ci if (!nv_encoder) 18398c2ecf20Sopenharmony_ci return -ENOMEM; 18408c2ecf20Sopenharmony_ci nv_encoder->dcb = dcbe; 18418c2ecf20Sopenharmony_ci nv_encoder->update = nv50_sor_update; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci encoder = to_drm_encoder(nv_encoder); 18448c2ecf20Sopenharmony_ci encoder->possible_crtcs = dcbe->heads; 18458c2ecf20Sopenharmony_ci encoder->possible_clones = 0; 18468c2ecf20Sopenharmony_ci drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type, 18478c2ecf20Sopenharmony_ci "sor-%04x-%04x", dcbe->hasht, dcbe->hashm); 18488c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &nv50_sor_help); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci disp->core->func->sor->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci if (dcbe->type == DCB_OUTPUT_DP) { 18558c2ecf20Sopenharmony_ci struct nvkm_i2c_aux *aux = 18568c2ecf20Sopenharmony_ci nvkm_i2c_aux_find(i2c, dcbe->i2c_index); 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci mutex_init(&nv_encoder->dp.hpd_irq_lock); 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci if (aux) { 18618c2ecf20Sopenharmony_ci if (disp->disp->object.oclass < GF110_DISP) { 18628c2ecf20Sopenharmony_ci /* HW has no support for address-only 18638c2ecf20Sopenharmony_ci * transactions, so we're required to 18648c2ecf20Sopenharmony_ci * use custom I2C-over-AUX code. 18658c2ecf20Sopenharmony_ci */ 18668c2ecf20Sopenharmony_ci nv_encoder->i2c = &aux->i2c; 18678c2ecf20Sopenharmony_ci } else { 18688c2ecf20Sopenharmony_ci nv_encoder->i2c = &nv_connector->aux.ddc; 18698c2ecf20Sopenharmony_ci } 18708c2ecf20Sopenharmony_ci nv_encoder->aux = aux; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (nv_connector->type != DCB_CONNECTOR_eDP && 18748c2ecf20Sopenharmony_ci nv50_has_mst(drm)) { 18758c2ecf20Sopenharmony_ci ret = nv50_mstm_new(nv_encoder, &nv_connector->aux, 18768c2ecf20Sopenharmony_ci 16, nv_connector->base.base.id, 18778c2ecf20Sopenharmony_ci &nv_encoder->dp.mstm); 18788c2ecf20Sopenharmony_ci if (ret) 18798c2ecf20Sopenharmony_ci return ret; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci } else { 18828c2ecf20Sopenharmony_ci struct nvkm_i2c_bus *bus = 18838c2ecf20Sopenharmony_ci nvkm_i2c_bus_find(i2c, dcbe->i2c_index); 18848c2ecf20Sopenharmony_ci if (bus) 18858c2ecf20Sopenharmony_ci nv_encoder->i2c = &bus->i2c; 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci return 0; 18898c2ecf20Sopenharmony_ci} 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci/****************************************************************************** 18928c2ecf20Sopenharmony_ci * PIOR 18938c2ecf20Sopenharmony_ci *****************************************************************************/ 18948c2ecf20Sopenharmony_cistatic int 18958c2ecf20Sopenharmony_cinv50_pior_atomic_check(struct drm_encoder *encoder, 18968c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 18978c2ecf20Sopenharmony_ci struct drm_connector_state *conn_state) 18988c2ecf20Sopenharmony_ci{ 18998c2ecf20Sopenharmony_ci int ret = nv50_outp_atomic_check(encoder, crtc_state, conn_state); 19008c2ecf20Sopenharmony_ci if (ret) 19018c2ecf20Sopenharmony_ci return ret; 19028c2ecf20Sopenharmony_ci crtc_state->adjusted_mode.clock *= 2; 19038c2ecf20Sopenharmony_ci return 0; 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic void 19078c2ecf20Sopenharmony_cinv50_pior_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) 19088c2ecf20Sopenharmony_ci{ 19098c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 19108c2ecf20Sopenharmony_ci struct nv50_core *core = nv50_disp(encoder->dev)->core; 19118c2ecf20Sopenharmony_ci const u32 ctrl = NVDEF(NV507D, PIOR_SET_CONTROL, OWNER, NONE); 19128c2ecf20Sopenharmony_ci if (nv_encoder->crtc) 19138c2ecf20Sopenharmony_ci core->func->pior->ctrl(core, nv_encoder->or, ctrl, NULL); 19148c2ecf20Sopenharmony_ci nv_encoder->crtc = NULL; 19158c2ecf20Sopenharmony_ci nv50_outp_release(nv_encoder); 19168c2ecf20Sopenharmony_ci} 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_cistatic void 19198c2ecf20Sopenharmony_cinv50_pior_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) 19208c2ecf20Sopenharmony_ci{ 19218c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); 19228c2ecf20Sopenharmony_ci struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); 19238c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(nv_crtc->base.state); 19248c2ecf20Sopenharmony_ci struct nv50_core *core = nv50_disp(encoder->dev)->core; 19258c2ecf20Sopenharmony_ci u32 ctrl = 0; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci switch (nv_crtc->index) { 19288c2ecf20Sopenharmony_ci case 0: ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, OWNER, HEAD0); break; 19298c2ecf20Sopenharmony_ci case 1: ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, OWNER, HEAD1); break; 19308c2ecf20Sopenharmony_ci default: 19318c2ecf20Sopenharmony_ci WARN_ON(1); 19328c2ecf20Sopenharmony_ci break; 19338c2ecf20Sopenharmony_ci } 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci nv50_outp_acquire(nv_encoder, false); 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci switch (asyh->or.bpc) { 19388c2ecf20Sopenharmony_ci case 10: asyh->or.depth = NV837D_PIOR_SET_CONTROL_PIXEL_DEPTH_BPP_30_444; break; 19398c2ecf20Sopenharmony_ci case 8: asyh->or.depth = NV837D_PIOR_SET_CONTROL_PIXEL_DEPTH_BPP_24_444; break; 19408c2ecf20Sopenharmony_ci case 6: asyh->or.depth = NV837D_PIOR_SET_CONTROL_PIXEL_DEPTH_BPP_18_444; break; 19418c2ecf20Sopenharmony_ci default: asyh->or.depth = NV837D_PIOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT; break; 19428c2ecf20Sopenharmony_ci } 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci switch (nv_encoder->dcb->type) { 19458c2ecf20Sopenharmony_ci case DCB_OUTPUT_TMDS: 19468c2ecf20Sopenharmony_ci case DCB_OUTPUT_DP: 19478c2ecf20Sopenharmony_ci ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC); 19488c2ecf20Sopenharmony_ci break; 19498c2ecf20Sopenharmony_ci default: 19508c2ecf20Sopenharmony_ci BUG(); 19518c2ecf20Sopenharmony_ci break; 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci core->func->pior->ctrl(core, nv_encoder->or, ctrl, asyh); 19558c2ecf20Sopenharmony_ci nv_encoder->crtc = &nv_crtc->base; 19568c2ecf20Sopenharmony_ci} 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_cistatic const struct drm_encoder_helper_funcs 19598c2ecf20Sopenharmony_cinv50_pior_help = { 19608c2ecf20Sopenharmony_ci .atomic_check = nv50_pior_atomic_check, 19618c2ecf20Sopenharmony_ci .atomic_enable = nv50_pior_enable, 19628c2ecf20Sopenharmony_ci .atomic_disable = nv50_pior_disable, 19638c2ecf20Sopenharmony_ci}; 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_cistatic void 19668c2ecf20Sopenharmony_cinv50_pior_destroy(struct drm_encoder *encoder) 19678c2ecf20Sopenharmony_ci{ 19688c2ecf20Sopenharmony_ci drm_encoder_cleanup(encoder); 19698c2ecf20Sopenharmony_ci kfree(encoder); 19708c2ecf20Sopenharmony_ci} 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs 19738c2ecf20Sopenharmony_cinv50_pior_func = { 19748c2ecf20Sopenharmony_ci .destroy = nv50_pior_destroy, 19758c2ecf20Sopenharmony_ci}; 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_cistatic int 19788c2ecf20Sopenharmony_cinv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) 19798c2ecf20Sopenharmony_ci{ 19808c2ecf20Sopenharmony_ci struct drm_device *dev = connector->dev; 19818c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 19828c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 19838c2ecf20Sopenharmony_ci struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); 19848c2ecf20Sopenharmony_ci struct nvkm_i2c_bus *bus = NULL; 19858c2ecf20Sopenharmony_ci struct nvkm_i2c_aux *aux = NULL; 19868c2ecf20Sopenharmony_ci struct i2c_adapter *ddc; 19878c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder; 19888c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 19898c2ecf20Sopenharmony_ci int type; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci switch (dcbe->type) { 19928c2ecf20Sopenharmony_ci case DCB_OUTPUT_TMDS: 19938c2ecf20Sopenharmony_ci bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_EXT(dcbe->extdev)); 19948c2ecf20Sopenharmony_ci ddc = bus ? &bus->i2c : NULL; 19958c2ecf20Sopenharmony_ci type = DRM_MODE_ENCODER_TMDS; 19968c2ecf20Sopenharmony_ci break; 19978c2ecf20Sopenharmony_ci case DCB_OUTPUT_DP: 19988c2ecf20Sopenharmony_ci aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbe->extdev)); 19998c2ecf20Sopenharmony_ci ddc = aux ? &aux->i2c : NULL; 20008c2ecf20Sopenharmony_ci type = DRM_MODE_ENCODER_TMDS; 20018c2ecf20Sopenharmony_ci break; 20028c2ecf20Sopenharmony_ci default: 20038c2ecf20Sopenharmony_ci return -ENODEV; 20048c2ecf20Sopenharmony_ci } 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); 20078c2ecf20Sopenharmony_ci if (!nv_encoder) 20088c2ecf20Sopenharmony_ci return -ENOMEM; 20098c2ecf20Sopenharmony_ci nv_encoder->dcb = dcbe; 20108c2ecf20Sopenharmony_ci nv_encoder->i2c = ddc; 20118c2ecf20Sopenharmony_ci nv_encoder->aux = aux; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci encoder = to_drm_encoder(nv_encoder); 20148c2ecf20Sopenharmony_ci encoder->possible_crtcs = dcbe->heads; 20158c2ecf20Sopenharmony_ci encoder->possible_clones = 0; 20168c2ecf20Sopenharmony_ci drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type, 20178c2ecf20Sopenharmony_ci "pior-%04x-%04x", dcbe->hasht, dcbe->hashm); 20188c2ecf20Sopenharmony_ci drm_encoder_helper_add(encoder, &nv50_pior_help); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, encoder); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci disp->core->func->pior->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci return 0; 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci/****************************************************************************** 20288c2ecf20Sopenharmony_ci * Atomic 20298c2ecf20Sopenharmony_ci *****************************************************************************/ 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_cistatic void 20328c2ecf20Sopenharmony_cinv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) 20338c2ecf20Sopenharmony_ci{ 20348c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(state->dev); 20358c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(drm->dev); 20368c2ecf20Sopenharmony_ci struct nv50_core *core = disp->core; 20378c2ecf20Sopenharmony_ci struct nv50_mstm *mstm; 20388c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "commit core %08x\n", interlock[NV50_DISP_INTERLOCK_BASE]); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci drm_for_each_encoder(encoder, drm->dev) { 20438c2ecf20Sopenharmony_ci if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { 20448c2ecf20Sopenharmony_ci mstm = nouveau_encoder(encoder)->dp.mstm; 20458c2ecf20Sopenharmony_ci if (mstm && mstm->modified) 20468c2ecf20Sopenharmony_ci nv50_mstm_prepare(mstm); 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci core->func->ntfy_init(disp->sync, NV50_DISP_CORE_NTFY); 20518c2ecf20Sopenharmony_ci core->func->update(core, interlock, true); 20528c2ecf20Sopenharmony_ci if (core->func->ntfy_wait_done(disp->sync, NV50_DISP_CORE_NTFY, 20538c2ecf20Sopenharmony_ci disp->core->chan.base.device)) 20548c2ecf20Sopenharmony_ci NV_ERROR(drm, "core notifier timeout\n"); 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci drm_for_each_encoder(encoder, drm->dev) { 20578c2ecf20Sopenharmony_ci if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { 20588c2ecf20Sopenharmony_ci mstm = nouveau_encoder(encoder)->dp.mstm; 20598c2ecf20Sopenharmony_ci if (mstm && mstm->modified) 20608c2ecf20Sopenharmony_ci nv50_mstm_cleanup(mstm); 20618c2ecf20Sopenharmony_ci } 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci} 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_cistatic void 20668c2ecf20Sopenharmony_cinv50_disp_atomic_commit_wndw(struct drm_atomic_state *state, u32 *interlock) 20678c2ecf20Sopenharmony_ci{ 20688c2ecf20Sopenharmony_ci struct drm_plane_state *new_plane_state; 20698c2ecf20Sopenharmony_ci struct drm_plane *plane; 20708c2ecf20Sopenharmony_ci int i; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci for_each_new_plane_in_state(state, plane, new_plane_state, i) { 20738c2ecf20Sopenharmony_ci struct nv50_wndw *wndw = nv50_wndw(plane); 20748c2ecf20Sopenharmony_ci if (interlock[wndw->interlock.type] & wndw->interlock.data) { 20758c2ecf20Sopenharmony_ci if (wndw->func->update) 20768c2ecf20Sopenharmony_ci wndw->func->update(wndw, interlock); 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci} 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_cistatic void 20828c2ecf20Sopenharmony_cinv50_disp_atomic_commit_tail(struct drm_atomic_state *state) 20838c2ecf20Sopenharmony_ci{ 20848c2ecf20Sopenharmony_ci struct drm_device *dev = state->dev; 20858c2ecf20Sopenharmony_ci struct drm_crtc_state *new_crtc_state, *old_crtc_state; 20868c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 20878c2ecf20Sopenharmony_ci struct drm_plane_state *new_plane_state; 20888c2ecf20Sopenharmony_ci struct drm_plane *plane; 20898c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 20908c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 20918c2ecf20Sopenharmony_ci struct nv50_atom *atom = nv50_atom(state); 20928c2ecf20Sopenharmony_ci struct nv50_core *core = disp->core; 20938c2ecf20Sopenharmony_ci struct nv50_outp_atom *outp, *outt; 20948c2ecf20Sopenharmony_ci u32 interlock[NV50_DISP_INTERLOCK__SIZE] = {}; 20958c2ecf20Sopenharmony_ci int i; 20968c2ecf20Sopenharmony_ci bool flushed = false; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "commit %d %d\n", atom->lock_core, atom->flush_disable); 20998c2ecf20Sopenharmony_ci nv50_crc_atomic_stop_reporting(state); 21008c2ecf20Sopenharmony_ci drm_atomic_helper_wait_for_fences(dev, state, false); 21018c2ecf20Sopenharmony_ci drm_atomic_helper_wait_for_dependencies(state); 21028c2ecf20Sopenharmony_ci drm_atomic_helper_update_legacy_modeset_state(dev, state); 21038c2ecf20Sopenharmony_ci drm_atomic_helper_calc_timestamping_constants(state); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci if (atom->lock_core) 21068c2ecf20Sopenharmony_ci mutex_lock(&disp->mutex); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci /* Disable head(s). */ 21098c2ecf20Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 21108c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 21118c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: clr %04x (set %04x)\n", crtc->name, 21148c2ecf20Sopenharmony_ci asyh->clr.mask, asyh->set.mask); 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci if (old_crtc_state->active && !new_crtc_state->active) { 21178c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev->dev); 21188c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci if (asyh->clr.mask) { 21228c2ecf20Sopenharmony_ci nv50_head_flush_clr(head, asyh, atom->flush_disable); 21238c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_CORE] |= 1; 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci /* Disable plane(s). */ 21288c2ecf20Sopenharmony_ci for_each_new_plane_in_state(state, plane, new_plane_state, i) { 21298c2ecf20Sopenharmony_ci struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state); 21308c2ecf20Sopenharmony_ci struct nv50_wndw *wndw = nv50_wndw(plane); 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: clr %02x (set %02x)\n", plane->name, 21338c2ecf20Sopenharmony_ci asyw->clr.mask, asyw->set.mask); 21348c2ecf20Sopenharmony_ci if (!asyw->clr.mask) 21358c2ecf20Sopenharmony_ci continue; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci nv50_wndw_flush_clr(wndw, interlock, atom->flush_disable, asyw); 21388c2ecf20Sopenharmony_ci } 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci /* Disable output path(s). */ 21418c2ecf20Sopenharmony_ci list_for_each_entry(outp, &atom->outp, head) { 21428c2ecf20Sopenharmony_ci const struct drm_encoder_helper_funcs *help; 21438c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci encoder = outp->encoder; 21468c2ecf20Sopenharmony_ci help = encoder->helper_private; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: clr %02x (set %02x)\n", encoder->name, 21498c2ecf20Sopenharmony_ci outp->clr.mask, outp->set.mask); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci if (outp->clr.mask) { 21528c2ecf20Sopenharmony_ci help->atomic_disable(encoder, state); 21538c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_CORE] |= 1; 21548c2ecf20Sopenharmony_ci if (outp->flush_disable) { 21558c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_wndw(state, interlock); 21568c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_core(state, interlock); 21578c2ecf20Sopenharmony_ci memset(interlock, 0x00, sizeof(interlock)); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci flushed = true; 21608c2ecf20Sopenharmony_ci } 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci } 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci /* Flush disable. */ 21658c2ecf20Sopenharmony_ci if (interlock[NV50_DISP_INTERLOCK_CORE]) { 21668c2ecf20Sopenharmony_ci if (atom->flush_disable) { 21678c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_wndw(state, interlock); 21688c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_core(state, interlock); 21698c2ecf20Sopenharmony_ci memset(interlock, 0x00, sizeof(interlock)); 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci flushed = true; 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci if (flushed) 21768c2ecf20Sopenharmony_ci nv50_crc_atomic_release_notifier_contexts(state); 21778c2ecf20Sopenharmony_ci nv50_crc_atomic_init_notifier_contexts(state); 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci /* Update output path(s). */ 21808c2ecf20Sopenharmony_ci list_for_each_entry_safe(outp, outt, &atom->outp, head) { 21818c2ecf20Sopenharmony_ci const struct drm_encoder_helper_funcs *help; 21828c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci encoder = outp->encoder; 21858c2ecf20Sopenharmony_ci help = encoder->helper_private; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: set %02x (clr %02x)\n", encoder->name, 21888c2ecf20Sopenharmony_ci outp->set.mask, outp->clr.mask); 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (outp->set.mask) { 21918c2ecf20Sopenharmony_ci help->atomic_enable(encoder, state); 21928c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_CORE] = 1; 21938c2ecf20Sopenharmony_ci } 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci list_del(&outp->head); 21968c2ecf20Sopenharmony_ci kfree(outp); 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci /* Update head(s). */ 22008c2ecf20Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 22018c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 22028c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: set %04x (clr %04x)\n", crtc->name, 22058c2ecf20Sopenharmony_ci asyh->set.mask, asyh->clr.mask); 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci if (asyh->set.mask) { 22088c2ecf20Sopenharmony_ci nv50_head_flush_set(head, asyh); 22098c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_CORE] = 1; 22108c2ecf20Sopenharmony_ci } 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci if (new_crtc_state->active) { 22138c2ecf20Sopenharmony_ci if (!old_crtc_state->active) { 22148c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 22158c2ecf20Sopenharmony_ci pm_runtime_get_noresume(dev->dev); 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci if (new_crtc_state->event) 22188c2ecf20Sopenharmony_ci drm_crtc_vblank_get(crtc); 22198c2ecf20Sopenharmony_ci } 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci /* Update window->head assignment. 22238c2ecf20Sopenharmony_ci * 22248c2ecf20Sopenharmony_ci * This has to happen in an update that's not interlocked with 22258c2ecf20Sopenharmony_ci * any window channels to avoid hitting HW error checks. 22268c2ecf20Sopenharmony_ci * 22278c2ecf20Sopenharmony_ci *TODO: Proper handling of window ownership (Turing apparently 22288c2ecf20Sopenharmony_ci * supports non-fixed mappings). 22298c2ecf20Sopenharmony_ci */ 22308c2ecf20Sopenharmony_ci if (core->assign_windows) { 22318c2ecf20Sopenharmony_ci core->func->wndw.owner(core); 22328c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_core(state, interlock); 22338c2ecf20Sopenharmony_ci core->assign_windows = false; 22348c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_CORE] = 0; 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci /* Finish updating head(s)... 22388c2ecf20Sopenharmony_ci * 22398c2ecf20Sopenharmony_ci * NVD is rather picky about both where window assignments can change, 22408c2ecf20Sopenharmony_ci * *and* about certain core and window channel states matching. 22418c2ecf20Sopenharmony_ci * 22428c2ecf20Sopenharmony_ci * The EFI GOP driver on newer GPUs configures window channels with a 22438c2ecf20Sopenharmony_ci * different output format to what we do, and the core channel update 22448c2ecf20Sopenharmony_ci * in the assign_windows case above would result in a state mismatch. 22458c2ecf20Sopenharmony_ci * 22468c2ecf20Sopenharmony_ci * Delay some of the head update until after that point to workaround 22478c2ecf20Sopenharmony_ci * the issue. This only affects the initial modeset. 22488c2ecf20Sopenharmony_ci * 22498c2ecf20Sopenharmony_ci * TODO: handle this better when adding flexible window mapping 22508c2ecf20Sopenharmony_ci */ 22518c2ecf20Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 22528c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); 22538c2ecf20Sopenharmony_ci struct nv50_head *head = nv50_head(crtc); 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: set %04x (clr %04x)\n", crtc->name, 22568c2ecf20Sopenharmony_ci asyh->set.mask, asyh->clr.mask); 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci if (asyh->set.mask) { 22598c2ecf20Sopenharmony_ci nv50_head_flush_set_wndw(head, asyh); 22608c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_CORE] = 1; 22618c2ecf20Sopenharmony_ci } 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci /* Update plane(s). */ 22658c2ecf20Sopenharmony_ci for_each_new_plane_in_state(state, plane, new_plane_state, i) { 22668c2ecf20Sopenharmony_ci struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state); 22678c2ecf20Sopenharmony_ci struct nv50_wndw *wndw = nv50_wndw(plane); 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci NV_ATOMIC(drm, "%s: set %02x (clr %02x)\n", plane->name, 22708c2ecf20Sopenharmony_ci asyw->set.mask, asyw->clr.mask); 22718c2ecf20Sopenharmony_ci if ( !asyw->set.mask && 22728c2ecf20Sopenharmony_ci (!asyw->clr.mask || atom->flush_disable)) 22738c2ecf20Sopenharmony_ci continue; 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci nv50_wndw_flush_set(wndw, interlock, asyw); 22768c2ecf20Sopenharmony_ci } 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci /* Flush update. */ 22798c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_wndw(state, interlock); 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci if (interlock[NV50_DISP_INTERLOCK_CORE]) { 22828c2ecf20Sopenharmony_ci if (interlock[NV50_DISP_INTERLOCK_BASE] || 22838c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_OVLY] || 22848c2ecf20Sopenharmony_ci interlock[NV50_DISP_INTERLOCK_WNDW] || 22858c2ecf20Sopenharmony_ci !atom->state.legacy_cursor_update) 22868c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_core(state, interlock); 22878c2ecf20Sopenharmony_ci else 22888c2ecf20Sopenharmony_ci disp->core->func->update(disp->core, interlock, false); 22898c2ecf20Sopenharmony_ci } 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci if (atom->lock_core) 22928c2ecf20Sopenharmony_ci mutex_unlock(&disp->mutex); 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci /* Wait for HW to signal completion. */ 22958c2ecf20Sopenharmony_ci for_each_new_plane_in_state(state, plane, new_plane_state, i) { 22968c2ecf20Sopenharmony_ci struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state); 22978c2ecf20Sopenharmony_ci struct nv50_wndw *wndw = nv50_wndw(plane); 22988c2ecf20Sopenharmony_ci int ret = nv50_wndw_wait_armed(wndw, asyw); 22998c2ecf20Sopenharmony_ci if (ret) 23008c2ecf20Sopenharmony_ci NV_ERROR(drm, "%s: timeout\n", plane->name); 23018c2ecf20Sopenharmony_ci } 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 23048c2ecf20Sopenharmony_ci if (new_crtc_state->event) { 23058c2ecf20Sopenharmony_ci unsigned long flags; 23068c2ecf20Sopenharmony_ci /* Get correct count/ts if racing with vblank irq */ 23078c2ecf20Sopenharmony_ci if (new_crtc_state->active) 23088c2ecf20Sopenharmony_ci drm_crtc_accurate_vblank_count(crtc); 23098c2ecf20Sopenharmony_ci spin_lock_irqsave(&crtc->dev->event_lock, flags); 23108c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, new_crtc_state->event); 23118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci new_crtc_state->event = NULL; 23148c2ecf20Sopenharmony_ci if (new_crtc_state->active) 23158c2ecf20Sopenharmony_ci drm_crtc_vblank_put(crtc); 23168c2ecf20Sopenharmony_ci } 23178c2ecf20Sopenharmony_ci } 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci nv50_crc_atomic_start_reporting(state); 23208c2ecf20Sopenharmony_ci if (!flushed) 23218c2ecf20Sopenharmony_ci nv50_crc_atomic_release_notifier_contexts(state); 23228c2ecf20Sopenharmony_ci drm_atomic_helper_commit_hw_done(state); 23238c2ecf20Sopenharmony_ci drm_atomic_helper_cleanup_planes(dev, state); 23248c2ecf20Sopenharmony_ci drm_atomic_helper_commit_cleanup_done(state); 23258c2ecf20Sopenharmony_ci drm_atomic_state_put(state); 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci /* Drop the RPM ref we got from nv50_disp_atomic_commit() */ 23288c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 23298c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 23308c2ecf20Sopenharmony_ci} 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_cistatic void 23338c2ecf20Sopenharmony_cinv50_disp_atomic_commit_work(struct work_struct *work) 23348c2ecf20Sopenharmony_ci{ 23358c2ecf20Sopenharmony_ci struct drm_atomic_state *state = 23368c2ecf20Sopenharmony_ci container_of(work, typeof(*state), commit_work); 23378c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_tail(state); 23388c2ecf20Sopenharmony_ci} 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_cistatic int 23418c2ecf20Sopenharmony_cinv50_disp_atomic_commit(struct drm_device *dev, 23428c2ecf20Sopenharmony_ci struct drm_atomic_state *state, bool nonblock) 23438c2ecf20Sopenharmony_ci{ 23448c2ecf20Sopenharmony_ci struct drm_plane_state *new_plane_state; 23458c2ecf20Sopenharmony_ci struct drm_plane *plane; 23468c2ecf20Sopenharmony_ci int ret, i; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev->dev); 23498c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 23508c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 23518c2ecf20Sopenharmony_ci return ret; 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci ret = drm_atomic_helper_setup_commit(state, nonblock); 23558c2ecf20Sopenharmony_ci if (ret) 23568c2ecf20Sopenharmony_ci goto done; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci INIT_WORK(&state->commit_work, nv50_disp_atomic_commit_work); 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci ret = drm_atomic_helper_prepare_planes(dev, state); 23618c2ecf20Sopenharmony_ci if (ret) 23628c2ecf20Sopenharmony_ci goto done; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci if (!nonblock) { 23658c2ecf20Sopenharmony_ci ret = drm_atomic_helper_wait_for_fences(dev, state, true); 23668c2ecf20Sopenharmony_ci if (ret) 23678c2ecf20Sopenharmony_ci goto err_cleanup; 23688c2ecf20Sopenharmony_ci } 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci ret = drm_atomic_helper_swap_state(state, true); 23718c2ecf20Sopenharmony_ci if (ret) 23728c2ecf20Sopenharmony_ci goto err_cleanup; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci for_each_new_plane_in_state(state, plane, new_plane_state, i) { 23758c2ecf20Sopenharmony_ci struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state); 23768c2ecf20Sopenharmony_ci struct nv50_wndw *wndw = nv50_wndw(plane); 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci if (asyw->set.image) 23798c2ecf20Sopenharmony_ci nv50_wndw_ntfy_enable(wndw, asyw); 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci drm_atomic_state_get(state); 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci /* 23858c2ecf20Sopenharmony_ci * Grab another RPM ref for the commit tail, which will release the 23868c2ecf20Sopenharmony_ci * ref when it's finished 23878c2ecf20Sopenharmony_ci */ 23888c2ecf20Sopenharmony_ci pm_runtime_get_noresume(dev->dev); 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci if (nonblock) 23918c2ecf20Sopenharmony_ci queue_work(system_unbound_wq, &state->commit_work); 23928c2ecf20Sopenharmony_ci else 23938c2ecf20Sopenharmony_ci nv50_disp_atomic_commit_tail(state); 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_cierr_cleanup: 23968c2ecf20Sopenharmony_ci if (ret) 23978c2ecf20Sopenharmony_ci drm_atomic_helper_cleanup_planes(dev, state); 23988c2ecf20Sopenharmony_cidone: 23998c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(dev->dev); 24008c2ecf20Sopenharmony_ci return ret; 24018c2ecf20Sopenharmony_ci} 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_cistatic struct nv50_outp_atom * 24048c2ecf20Sopenharmony_cinv50_disp_outp_atomic_add(struct nv50_atom *atom, struct drm_encoder *encoder) 24058c2ecf20Sopenharmony_ci{ 24068c2ecf20Sopenharmony_ci struct nv50_outp_atom *outp; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci list_for_each_entry(outp, &atom->outp, head) { 24098c2ecf20Sopenharmony_ci if (outp->encoder == encoder) 24108c2ecf20Sopenharmony_ci return outp; 24118c2ecf20Sopenharmony_ci } 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci outp = kzalloc(sizeof(*outp), GFP_KERNEL); 24148c2ecf20Sopenharmony_ci if (!outp) 24158c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci list_add(&outp->head, &atom->outp); 24188c2ecf20Sopenharmony_ci outp->encoder = encoder; 24198c2ecf20Sopenharmony_ci return outp; 24208c2ecf20Sopenharmony_ci} 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_cistatic int 24238c2ecf20Sopenharmony_cinv50_disp_outp_atomic_check_clr(struct nv50_atom *atom, 24248c2ecf20Sopenharmony_ci struct drm_connector_state *old_connector_state) 24258c2ecf20Sopenharmony_ci{ 24268c2ecf20Sopenharmony_ci struct drm_encoder *encoder = old_connector_state->best_encoder; 24278c2ecf20Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 24288c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 24298c2ecf20Sopenharmony_ci struct nv50_outp_atom *outp; 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci if (!(crtc = old_connector_state->crtc)) 24328c2ecf20Sopenharmony_ci return 0; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci old_crtc_state = drm_atomic_get_old_crtc_state(&atom->state, crtc); 24358c2ecf20Sopenharmony_ci new_crtc_state = drm_atomic_get_new_crtc_state(&atom->state, crtc); 24368c2ecf20Sopenharmony_ci if (old_crtc_state->active && drm_atomic_crtc_needs_modeset(new_crtc_state)) { 24378c2ecf20Sopenharmony_ci outp = nv50_disp_outp_atomic_add(atom, encoder); 24388c2ecf20Sopenharmony_ci if (IS_ERR(outp)) 24398c2ecf20Sopenharmony_ci return PTR_ERR(outp); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci if (outp->encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { 24428c2ecf20Sopenharmony_ci outp->flush_disable = true; 24438c2ecf20Sopenharmony_ci atom->flush_disable = true; 24448c2ecf20Sopenharmony_ci } 24458c2ecf20Sopenharmony_ci outp->clr.ctrl = true; 24468c2ecf20Sopenharmony_ci atom->lock_core = true; 24478c2ecf20Sopenharmony_ci } 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci return 0; 24508c2ecf20Sopenharmony_ci} 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_cistatic int 24538c2ecf20Sopenharmony_cinv50_disp_outp_atomic_check_set(struct nv50_atom *atom, 24548c2ecf20Sopenharmony_ci struct drm_connector_state *connector_state) 24558c2ecf20Sopenharmony_ci{ 24568c2ecf20Sopenharmony_ci struct drm_encoder *encoder = connector_state->best_encoder; 24578c2ecf20Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 24588c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 24598c2ecf20Sopenharmony_ci struct nv50_outp_atom *outp; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci if (!(crtc = connector_state->crtc)) 24628c2ecf20Sopenharmony_ci return 0; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci new_crtc_state = drm_atomic_get_new_crtc_state(&atom->state, crtc); 24658c2ecf20Sopenharmony_ci if (new_crtc_state->active && drm_atomic_crtc_needs_modeset(new_crtc_state)) { 24668c2ecf20Sopenharmony_ci outp = nv50_disp_outp_atomic_add(atom, encoder); 24678c2ecf20Sopenharmony_ci if (IS_ERR(outp)) 24688c2ecf20Sopenharmony_ci return PTR_ERR(outp); 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci outp->set.ctrl = true; 24718c2ecf20Sopenharmony_ci atom->lock_core = true; 24728c2ecf20Sopenharmony_ci } 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci return 0; 24758c2ecf20Sopenharmony_ci} 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_cistatic int 24788c2ecf20Sopenharmony_cinv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci struct nv50_atom *atom = nv50_atom(state); 24818c2ecf20Sopenharmony_ci struct nv50_core *core = nv50_disp(dev)->core; 24828c2ecf20Sopenharmony_ci struct drm_connector_state *old_connector_state, *new_connector_state; 24838c2ecf20Sopenharmony_ci struct drm_connector *connector; 24848c2ecf20Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 24858c2ecf20Sopenharmony_ci struct drm_crtc *crtc; 24868c2ecf20Sopenharmony_ci struct nv50_head *head; 24878c2ecf20Sopenharmony_ci struct nv50_head_atom *asyh; 24888c2ecf20Sopenharmony_ci int ret, i; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci if (core->assign_windows && core->func->head->static_wndw_map) { 24918c2ecf20Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 24928c2ecf20Sopenharmony_ci new_crtc_state = drm_atomic_get_crtc_state(state, 24938c2ecf20Sopenharmony_ci crtc); 24948c2ecf20Sopenharmony_ci if (IS_ERR(new_crtc_state)) 24958c2ecf20Sopenharmony_ci return PTR_ERR(new_crtc_state); 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci head = nv50_head(crtc); 24988c2ecf20Sopenharmony_ci asyh = nv50_head_atom(new_crtc_state); 24998c2ecf20Sopenharmony_ci core->func->head->static_wndw_map(head, asyh); 25008c2ecf20Sopenharmony_ci } 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci /* We need to handle colour management on a per-plane basis. */ 25048c2ecf20Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 25058c2ecf20Sopenharmony_ci if (new_crtc_state->color_mgmt_changed) { 25068c2ecf20Sopenharmony_ci ret = drm_atomic_add_affected_planes(state, crtc); 25078c2ecf20Sopenharmony_ci if (ret) 25088c2ecf20Sopenharmony_ci return ret; 25098c2ecf20Sopenharmony_ci } 25108c2ecf20Sopenharmony_ci } 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci ret = drm_atomic_helper_check(dev, state); 25138c2ecf20Sopenharmony_ci if (ret) 25148c2ecf20Sopenharmony_ci return ret; 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { 25178c2ecf20Sopenharmony_ci ret = nv50_disp_outp_atomic_check_clr(atom, old_connector_state); 25188c2ecf20Sopenharmony_ci if (ret) 25198c2ecf20Sopenharmony_ci return ret; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci ret = nv50_disp_outp_atomic_check_set(atom, new_connector_state); 25228c2ecf20Sopenharmony_ci if (ret) 25238c2ecf20Sopenharmony_ci return ret; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci ret = drm_dp_mst_atomic_check(state); 25278c2ecf20Sopenharmony_ci if (ret) 25288c2ecf20Sopenharmony_ci return ret; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci nv50_crc_atomic_check_outp(atom); 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_ci return 0; 25338c2ecf20Sopenharmony_ci} 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_cistatic void 25368c2ecf20Sopenharmony_cinv50_disp_atomic_state_clear(struct drm_atomic_state *state) 25378c2ecf20Sopenharmony_ci{ 25388c2ecf20Sopenharmony_ci struct nv50_atom *atom = nv50_atom(state); 25398c2ecf20Sopenharmony_ci struct nv50_outp_atom *outp, *outt; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci list_for_each_entry_safe(outp, outt, &atom->outp, head) { 25428c2ecf20Sopenharmony_ci list_del(&outp->head); 25438c2ecf20Sopenharmony_ci kfree(outp); 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci drm_atomic_state_default_clear(state); 25478c2ecf20Sopenharmony_ci} 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_cistatic void 25508c2ecf20Sopenharmony_cinv50_disp_atomic_state_free(struct drm_atomic_state *state) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci struct nv50_atom *atom = nv50_atom(state); 25538c2ecf20Sopenharmony_ci drm_atomic_state_default_release(&atom->state); 25548c2ecf20Sopenharmony_ci kfree(atom); 25558c2ecf20Sopenharmony_ci} 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_cistatic struct drm_atomic_state * 25588c2ecf20Sopenharmony_cinv50_disp_atomic_state_alloc(struct drm_device *dev) 25598c2ecf20Sopenharmony_ci{ 25608c2ecf20Sopenharmony_ci struct nv50_atom *atom; 25618c2ecf20Sopenharmony_ci if (!(atom = kzalloc(sizeof(*atom), GFP_KERNEL)) || 25628c2ecf20Sopenharmony_ci drm_atomic_state_init(dev, &atom->state) < 0) { 25638c2ecf20Sopenharmony_ci kfree(atom); 25648c2ecf20Sopenharmony_ci return NULL; 25658c2ecf20Sopenharmony_ci } 25668c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&atom->outp); 25678c2ecf20Sopenharmony_ci return &atom->state; 25688c2ecf20Sopenharmony_ci} 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs 25718c2ecf20Sopenharmony_cinv50_disp_func = { 25728c2ecf20Sopenharmony_ci .fb_create = nouveau_user_framebuffer_create, 25738c2ecf20Sopenharmony_ci .output_poll_changed = nouveau_fbcon_output_poll_changed, 25748c2ecf20Sopenharmony_ci .atomic_check = nv50_disp_atomic_check, 25758c2ecf20Sopenharmony_ci .atomic_commit = nv50_disp_atomic_commit, 25768c2ecf20Sopenharmony_ci .atomic_state_alloc = nv50_disp_atomic_state_alloc, 25778c2ecf20Sopenharmony_ci .atomic_state_clear = nv50_disp_atomic_state_clear, 25788c2ecf20Sopenharmony_ci .atomic_state_free = nv50_disp_atomic_state_free, 25798c2ecf20Sopenharmony_ci}; 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci/****************************************************************************** 25828c2ecf20Sopenharmony_ci * Init 25838c2ecf20Sopenharmony_ci *****************************************************************************/ 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_cistatic void 25868c2ecf20Sopenharmony_cinv50_display_fini(struct drm_device *dev, bool runtime, bool suspend) 25878c2ecf20Sopenharmony_ci{ 25888c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 25898c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 25908c2ecf20Sopenharmony_ci 25918c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 25928c2ecf20Sopenharmony_ci if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) 25938c2ecf20Sopenharmony_ci nv50_mstm_fini(nouveau_encoder(encoder)); 25948c2ecf20Sopenharmony_ci } 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci if (!runtime) 25978c2ecf20Sopenharmony_ci cancel_work_sync(&drm->hpd_work); 25988c2ecf20Sopenharmony_ci} 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_cistatic int 26018c2ecf20Sopenharmony_cinv50_display_init(struct drm_device *dev, bool resume, bool runtime) 26028c2ecf20Sopenharmony_ci{ 26038c2ecf20Sopenharmony_ci struct nv50_core *core = nv50_disp(dev)->core; 26048c2ecf20Sopenharmony_ci struct drm_encoder *encoder; 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci if (resume || runtime) 26078c2ecf20Sopenharmony_ci core->func->init(core); 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 26108c2ecf20Sopenharmony_ci if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { 26118c2ecf20Sopenharmony_ci struct nouveau_encoder *nv_encoder = 26128c2ecf20Sopenharmony_ci nouveau_encoder(encoder); 26138c2ecf20Sopenharmony_ci nv50_mstm_init(nv_encoder, runtime); 26148c2ecf20Sopenharmony_ci } 26158c2ecf20Sopenharmony_ci } 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci return 0; 26188c2ecf20Sopenharmony_ci} 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_cistatic void 26218c2ecf20Sopenharmony_cinv50_display_destroy(struct drm_device *dev) 26228c2ecf20Sopenharmony_ci{ 26238c2ecf20Sopenharmony_ci struct nv50_disp *disp = nv50_disp(dev); 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci nv50_audio_component_fini(nouveau_drm(dev)); 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci nvif_object_unmap(&disp->caps); 26288c2ecf20Sopenharmony_ci nvif_object_dtor(&disp->caps); 26298c2ecf20Sopenharmony_ci nv50_core_del(&disp->core); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci nouveau_bo_unmap(disp->sync); 26328c2ecf20Sopenharmony_ci if (disp->sync) 26338c2ecf20Sopenharmony_ci nouveau_bo_unpin(disp->sync); 26348c2ecf20Sopenharmony_ci nouveau_bo_ref(NULL, &disp->sync); 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci nouveau_display(dev)->priv = NULL; 26378c2ecf20Sopenharmony_ci kfree(disp); 26388c2ecf20Sopenharmony_ci} 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ciint 26418c2ecf20Sopenharmony_cinv50_display_create(struct drm_device *dev) 26428c2ecf20Sopenharmony_ci{ 26438c2ecf20Sopenharmony_ci struct nvif_device *device = &nouveau_drm(dev)->client.device; 26448c2ecf20Sopenharmony_ci struct nouveau_drm *drm = nouveau_drm(dev); 26458c2ecf20Sopenharmony_ci struct dcb_table *dcb = &drm->vbios.dcb; 26468c2ecf20Sopenharmony_ci struct drm_connector *connector, *tmp; 26478c2ecf20Sopenharmony_ci struct nv50_disp *disp; 26488c2ecf20Sopenharmony_ci struct dcb_output *dcbe; 26498c2ecf20Sopenharmony_ci int crtcs, ret, i; 26508c2ecf20Sopenharmony_ci bool has_mst = nv50_has_mst(drm); 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci disp = kzalloc(sizeof(*disp), GFP_KERNEL); 26538c2ecf20Sopenharmony_ci if (!disp) 26548c2ecf20Sopenharmony_ci return -ENOMEM; 26558c2ecf20Sopenharmony_ci 26568c2ecf20Sopenharmony_ci mutex_init(&disp->mutex); 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ci nouveau_display(dev)->priv = disp; 26598c2ecf20Sopenharmony_ci nouveau_display(dev)->dtor = nv50_display_destroy; 26608c2ecf20Sopenharmony_ci nouveau_display(dev)->init = nv50_display_init; 26618c2ecf20Sopenharmony_ci nouveau_display(dev)->fini = nv50_display_fini; 26628c2ecf20Sopenharmony_ci disp->disp = &nouveau_display(dev)->disp; 26638c2ecf20Sopenharmony_ci dev->mode_config.funcs = &nv50_disp_func; 26648c2ecf20Sopenharmony_ci dev->mode_config.quirk_addfb_prefer_xbgr_30bpp = true; 26658c2ecf20Sopenharmony_ci dev->mode_config.normalize_zpos = true; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci /* small shared memory area we use for notifiers and semaphores */ 26688c2ecf20Sopenharmony_ci ret = nouveau_bo_new(&drm->client, 4096, 0x1000, 26698c2ecf20Sopenharmony_ci NOUVEAU_GEM_DOMAIN_VRAM, 26708c2ecf20Sopenharmony_ci 0, 0x0000, NULL, NULL, &disp->sync); 26718c2ecf20Sopenharmony_ci if (!ret) { 26728c2ecf20Sopenharmony_ci ret = nouveau_bo_pin(disp->sync, NOUVEAU_GEM_DOMAIN_VRAM, true); 26738c2ecf20Sopenharmony_ci if (!ret) { 26748c2ecf20Sopenharmony_ci ret = nouveau_bo_map(disp->sync); 26758c2ecf20Sopenharmony_ci if (ret) 26768c2ecf20Sopenharmony_ci nouveau_bo_unpin(disp->sync); 26778c2ecf20Sopenharmony_ci } 26788c2ecf20Sopenharmony_ci if (ret) 26798c2ecf20Sopenharmony_ci nouveau_bo_ref(NULL, &disp->sync); 26808c2ecf20Sopenharmony_ci } 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci if (ret) 26838c2ecf20Sopenharmony_ci goto out; 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci /* allocate master evo channel */ 26868c2ecf20Sopenharmony_ci ret = nv50_core_new(drm, &disp->core); 26878c2ecf20Sopenharmony_ci if (ret) 26888c2ecf20Sopenharmony_ci goto out; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci disp->core->func->init(disp->core); 26918c2ecf20Sopenharmony_ci if (disp->core->func->caps_init) { 26928c2ecf20Sopenharmony_ci ret = disp->core->func->caps_init(drm, disp); 26938c2ecf20Sopenharmony_ci if (ret) 26948c2ecf20Sopenharmony_ci goto out; 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci /* Assign the correct format modifiers */ 26988c2ecf20Sopenharmony_ci if (disp->disp->object.oclass >= TU102_DISP) 26998c2ecf20Sopenharmony_ci nouveau_display(dev)->format_modifiers = wndwc57e_modifiers; 27008c2ecf20Sopenharmony_ci else 27018c2ecf20Sopenharmony_ci if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) 27028c2ecf20Sopenharmony_ci nouveau_display(dev)->format_modifiers = disp90xx_modifiers; 27038c2ecf20Sopenharmony_ci else 27048c2ecf20Sopenharmony_ci nouveau_display(dev)->format_modifiers = disp50xx_modifiers; 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci /* create crtc objects to represent the hw heads */ 27078c2ecf20Sopenharmony_ci if (disp->disp->object.oclass >= GV100_DISP) 27088c2ecf20Sopenharmony_ci crtcs = nvif_rd32(&device->object, 0x610060) & 0xff; 27098c2ecf20Sopenharmony_ci else 27108c2ecf20Sopenharmony_ci if (disp->disp->object.oclass >= GF110_DISP) 27118c2ecf20Sopenharmony_ci crtcs = nvif_rd32(&device->object, 0x612004) & 0xf; 27128c2ecf20Sopenharmony_ci else 27138c2ecf20Sopenharmony_ci crtcs = 0x3; 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci for (i = 0; i < fls(crtcs); i++) { 27168c2ecf20Sopenharmony_ci struct nv50_head *head; 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci if (!(crtcs & (1 << i))) 27198c2ecf20Sopenharmony_ci continue; 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci head = nv50_head_create(dev, i); 27228c2ecf20Sopenharmony_ci if (IS_ERR(head)) { 27238c2ecf20Sopenharmony_ci ret = PTR_ERR(head); 27248c2ecf20Sopenharmony_ci goto out; 27258c2ecf20Sopenharmony_ci } 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci if (has_mst) { 27288c2ecf20Sopenharmony_ci head->msto = nv50_msto_new(dev, head, i); 27298c2ecf20Sopenharmony_ci if (IS_ERR(head->msto)) { 27308c2ecf20Sopenharmony_ci ret = PTR_ERR(head->msto); 27318c2ecf20Sopenharmony_ci head->msto = NULL; 27328c2ecf20Sopenharmony_ci goto out; 27338c2ecf20Sopenharmony_ci } 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci /* 27368c2ecf20Sopenharmony_ci * FIXME: This is a hack to workaround the following 27378c2ecf20Sopenharmony_ci * issues: 27388c2ecf20Sopenharmony_ci * 27398c2ecf20Sopenharmony_ci * https://gitlab.gnome.org/GNOME/mutter/issues/759 27408c2ecf20Sopenharmony_ci * https://gitlab.freedesktop.org/xorg/xserver/merge_requests/277 27418c2ecf20Sopenharmony_ci * 27428c2ecf20Sopenharmony_ci * Once these issues are closed, this should be 27438c2ecf20Sopenharmony_ci * removed 27448c2ecf20Sopenharmony_ci */ 27458c2ecf20Sopenharmony_ci head->msto->encoder.possible_crtcs = crtcs; 27468c2ecf20Sopenharmony_ci } 27478c2ecf20Sopenharmony_ci } 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci /* create encoder/connector objects based on VBIOS DCB table */ 27508c2ecf20Sopenharmony_ci for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { 27518c2ecf20Sopenharmony_ci connector = nouveau_connector_create(dev, dcbe); 27528c2ecf20Sopenharmony_ci if (IS_ERR(connector)) 27538c2ecf20Sopenharmony_ci continue; 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_ci if (dcbe->location == DCB_LOC_ON_CHIP) { 27568c2ecf20Sopenharmony_ci switch (dcbe->type) { 27578c2ecf20Sopenharmony_ci case DCB_OUTPUT_TMDS: 27588c2ecf20Sopenharmony_ci case DCB_OUTPUT_LVDS: 27598c2ecf20Sopenharmony_ci case DCB_OUTPUT_DP: 27608c2ecf20Sopenharmony_ci ret = nv50_sor_create(connector, dcbe); 27618c2ecf20Sopenharmony_ci break; 27628c2ecf20Sopenharmony_ci case DCB_OUTPUT_ANALOG: 27638c2ecf20Sopenharmony_ci ret = nv50_dac_create(connector, dcbe); 27648c2ecf20Sopenharmony_ci break; 27658c2ecf20Sopenharmony_ci default: 27668c2ecf20Sopenharmony_ci ret = -ENODEV; 27678c2ecf20Sopenharmony_ci break; 27688c2ecf20Sopenharmony_ci } 27698c2ecf20Sopenharmony_ci } else { 27708c2ecf20Sopenharmony_ci ret = nv50_pior_create(connector, dcbe); 27718c2ecf20Sopenharmony_ci } 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci if (ret) { 27748c2ecf20Sopenharmony_ci NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", 27758c2ecf20Sopenharmony_ci dcbe->location, dcbe->type, 27768c2ecf20Sopenharmony_ci ffs(dcbe->or) - 1, ret); 27778c2ecf20Sopenharmony_ci ret = 0; 27788c2ecf20Sopenharmony_ci } 27798c2ecf20Sopenharmony_ci } 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci /* cull any connectors we created that don't have an encoder */ 27828c2ecf20Sopenharmony_ci list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { 27838c2ecf20Sopenharmony_ci if (connector->possible_encoders) 27848c2ecf20Sopenharmony_ci continue; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci NV_WARN(drm, "%s has no encoders, removing\n", 27878c2ecf20Sopenharmony_ci connector->name); 27888c2ecf20Sopenharmony_ci connector->funcs->destroy(connector); 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci /* Disable vblank irqs aggressively for power-saving, safe on nv50+ */ 27928c2ecf20Sopenharmony_ci dev->vblank_disable_immediate = true; 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci nv50_audio_component_init(drm); 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ciout: 27978c2ecf20Sopenharmony_ci if (ret) 27988c2ecf20Sopenharmony_ci nv50_display_destroy(dev); 27998c2ecf20Sopenharmony_ci return ret; 28008c2ecf20Sopenharmony_ci} 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci/****************************************************************************** 28038c2ecf20Sopenharmony_ci * Format modifiers 28048c2ecf20Sopenharmony_ci *****************************************************************************/ 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci/**************************************************************** 28078c2ecf20Sopenharmony_ci * Log2(block height) ----------------------------+ * 28088c2ecf20Sopenharmony_ci * Page Kind ----------------------------------+ | * 28098c2ecf20Sopenharmony_ci * Gob Height/Page Kind Generation ------+ | | * 28108c2ecf20Sopenharmony_ci * Sector layout -------+ | | | * 28118c2ecf20Sopenharmony_ci * Compression ------+ | | | | */ 28128c2ecf20Sopenharmony_ciconst u64 disp50xx_modifiers[] = { /* | | | | | */ 28138c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 0), 28148c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 1), 28158c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 2), 28168c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 3), 28178c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 4), 28188c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x7a, 5), 28198c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 0), 28208c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 1), 28218c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 2), 28228c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 3), 28238c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 4), 28248c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x78, 5), 28258c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 0), 28268c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 1), 28278c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 2), 28288c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 3), 28298c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 4), 28308c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 1, 0x70, 5), 28318c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 28328c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_INVALID 28338c2ecf20Sopenharmony_ci}; 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci/**************************************************************** 28368c2ecf20Sopenharmony_ci * Log2(block height) ----------------------------+ * 28378c2ecf20Sopenharmony_ci * Page Kind ----------------------------------+ | * 28388c2ecf20Sopenharmony_ci * Gob Height/Page Kind Generation ------+ | | * 28398c2ecf20Sopenharmony_ci * Sector layout -------+ | | | * 28408c2ecf20Sopenharmony_ci * Compression ------+ | | | | */ 28418c2ecf20Sopenharmony_ciconst u64 disp90xx_modifiers[] = { /* | | | | | */ 28428c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 0), 28438c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 1), 28448c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 2), 28458c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 3), 28468c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 4), 28478c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 0, 0xfe, 5), 28488c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 28498c2ecf20Sopenharmony_ci DRM_FORMAT_MOD_INVALID 28508c2ecf20Sopenharmony_ci}; 2851