1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24
25 #include "nvkms-utils.h"
26 #include "nvkms-dpy-override.h"
27
28 #include "nv_list.h"
29
30 static NVListRec dpyOverrideList = NV_LIST_INIT(&dpyOverrideList);
31
32 #define FOR_ALL_DPY_OVERRIDES(_pDpyOverride) \
33 nvListForEachEntry(_pDpyOverride, &dpyOverrideList, entry)
34 #define FOR_ALL_DPY_OVERRIDES_SAFE(_pDpyOverride, _next) \
35 nvListForEachEntry_safe(_pDpyOverride, _next, &dpyOverrideList, entry)
36
37 #define DPY_OVERRIDE_MATCHES(_pDpyOverride, _gpuId, _name) \
38 ((_pDpyOverride->gpuId == _gpuId) && \
39 !nvkms_strcmp(_pDpyOverride->name, _name))
40
DpyOverrideReadEdid(NVDpyOverrideRec * dpy,const char * buffer,size_t size)41 static NvBool DpyOverrideReadEdid(NVDpyOverrideRec *dpy,
42 const char *buffer,
43 size_t size)
44 {
45 if ((dpy->edid.length != size) || (dpy->edid.buffer == NULL)) {
46 NvU8 *newbuf = nvRealloc(dpy->edid.buffer, size);
47 if (newbuf == NULL) {
48 return FALSE;
49 }
50 dpy->edid.buffer = newbuf;
51 dpy->edid.length = size;
52 }
53
54 nvkms_memcpy(dpy->edid.buffer, buffer, size);
55 return TRUE;
56 }
57
58
59 /*
60 * Creates a display override on the given GPU for the given display name.
61 * If the override already exists it will be overwritten.
62 *
63 * \param[in] gpuId The ID of the GPU on which to create the display
64 * override, as returned in nvkms_enumerate_gpus()
65 * \param[in] name The name of the display to override in
66 * PROTOCOL-Index format, e.g. HDMI-0.
67 * \param[in] edid A buffer containing EDID data for the override.
68 * \param[in] edidSize The size of the edid buffer.
69 *
70 * \return A pointer to the created or edited NVDpyOverrideRec, or NULL if
71 * creation failed.
72 */
nvCreateDpyOverride(NvU32 gpuId,const char * name,NvBool connected,const char * edid,size_t edidSize)73 NVDpyOverrideRec *nvCreateDpyOverride(NvU32 gpuId,
74 const char *name,
75 NvBool connected,
76 const char *edid,
77 size_t edidSize)
78 {
79 NVDpyOverridePtr pDpyOverride;
80 size_t namelen, cpsz;
81 NvBool found = FALSE;
82
83 /* if such a display override already exists, let it be changed */
84 FOR_ALL_DPY_OVERRIDES(pDpyOverride) {
85 if (DPY_OVERRIDE_MATCHES(pDpyOverride, gpuId, name)) {
86
87 found = TRUE;
88 break;
89 }
90 }
91
92 /* if such a display override doesn't exist, create a new one */
93 if (!found) {
94 pDpyOverride = nvCalloc(1, sizeof(*pDpyOverride));
95 if (pDpyOverride == NULL) {
96 nvEvoLog(EVO_LOG_WARN, "Failed allocating data for display override");
97 return NULL;
98 }
99
100 nvListAdd(&pDpyOverride->entry, &dpyOverrideList);
101
102 namelen = nvkms_strlen(name);
103 cpsz = namelen > NVKMS_DPY_NAME_SIZE - 1 ? NVKMS_DPY_NAME_SIZE - 1
104 : namelen;
105
106 nvkms_memcpy(pDpyOverride->name, name, cpsz);
107 pDpyOverride->gpuId = gpuId;
108 }
109
110 pDpyOverride->connected = connected;
111 if (connected && !DpyOverrideReadEdid(pDpyOverride, edid, edidSize)) {
112 nvEvoLog(EVO_LOG_WARN, "Failed reading EDID");
113 nvListDel(&pDpyOverride->entry);
114 nvFree(pDpyOverride);
115 return NULL;
116 }
117
118 return pDpyOverride;
119 }
120
121 /*
122 * Deletes a display override on the given GPU for the given display name.
123 *
124 * \param[in] gpuId The ID of the GPU on which to delete the display
125 * override, as returned in nvkms_enumerate_gpus()
126 * \param[in] name The name of the display whose override to delete in
127 * PROTOCOL-Index format, e.g. HDMI-0.
128 */
nvDeleteDpyOverride(NvU32 gpuId,const char * name)129 void nvDeleteDpyOverride(NvU32 gpuId, const char *name)
130 {
131 NVDpyOverridePtr pDpyOverride;
132
133 /* If such a display override already exists, delete it */
134 FOR_ALL_DPY_OVERRIDES(pDpyOverride) {
135 if (DPY_OVERRIDE_MATCHES(pDpyOverride, gpuId, name)) {
136
137 nvListDel(&pDpyOverride->entry);
138 nvFree(pDpyOverride);
139 return; /* This makes using nvListForEachEntry safe. */
140 }
141 }
142 }
143
144 /*
145 * Logs a list of currently active override names to pInfoStr for a given
146 * GPU.
147 *
148 * \param[in] gpuId The ID of the GPU whose overrides to print, as
149 * returned in nvkms_enumerate_gpus()
150 * \param[in] pInfoStr A pointer to the NVEvoInfoString to log to.
151 */
nvLogDpyOverrides(NvU32 gpuId,NVEvoInfoStringPtr pInfoStr)152 void nvLogDpyOverrides(NvU32 gpuId, NVEvoInfoStringPtr pInfoStr)
153 {
154 NVDpyOverridePtr pDpyOverride;
155 FOR_ALL_DPY_OVERRIDES(pDpyOverride) {
156 if (pDpyOverride->gpuId == gpuId) {
157 nvEvoLogInfoString(pInfoStr, "%s", pDpyOverride->name);
158 }
159 }
160 }
161
162 /*
163 * Checks if there is a matching, valid, and enabled NVDpyOverrideRec for the
164 * pDpyEvo in the global display override list and returns it if it is found.
165 * O(N) in length of display override list
166 *
167 * \param[in] pDpyEvo The display to check for an override.
168 *
169 * \return The NVDpyOverrideRec override for the pDpyEvo, or NULL if it isn't
170 * found.
171 */
nvDpyEvoGetOverride(const NVDpyEvoRec * pDpyEvo)172 NVDpyOverridePtr nvDpyEvoGetOverride(const NVDpyEvoRec *pDpyEvo)
173 {
174 NVDevEvoPtr pDevEvo;
175 NVDispEvoPtr pDispEvo;
176 NVSubDeviceEvoPtr pSubDevice;
177 NVDpyOverridePtr it;
178
179 if (pDpyEvo == NULL) {
180 return NULL;
181 }
182
183 /*
184 * Don't override DP MST displays, because there could be multiple attached
185 * to the single connector, which would result in the number of displays
186 * this override creates being dependent on the number of plugged in
187 * displays, which seems incorrect for this feature
188 */
189 if (nvDpyEvoIsDPMST(pDpyEvo)) {
190 return NULL;
191 }
192 pDispEvo = pDpyEvo->pDispEvo;
193 pDevEvo = pDispEvo->pDevEvo;
194
195 pSubDevice = pDevEvo->pSubDevices[pDispEvo->displayOwner];
196
197 FOR_ALL_DPY_OVERRIDES(it) {
198 /* Ensure valid and enabled override */
199 if ((it->edid.length == 0) || (it->edid.buffer == NULL)) {
200 continue;
201 }
202
203 /*
204 * Both NVDpyOverrideRec.gpuId and NVSubDeviceEvoRec.gpuId ultimately
205 * derive from nvRmApiControl(NV2080_CTRL_CMD_GPU_GET_ID), so we can
206 * use them to match GPUs. Additionally, NVConnectorEvo.name is of the
207 * format TYPE-N, e.g. HDMI-0, but pDpyEvo.name may have additional
208 * qualifiers (e.g., an existing EDID-derived name).
209 */
210 if (DPY_OVERRIDE_MATCHES(it, pSubDevice->gpuId, pDpyEvo->pConnectorEvo->name)) {
211
212 nvEvoLogDebug(EVO_LOG_INFO, "NVDpyOverrideRec found: %s\n",
213 it->name);
214 return it;
215 }
216 }
217
218 return NULL;
219 }
220
221 /*
222 * Reads the EDID data from a given NVDpyOverrideRec into the buffer buff.
223 * Does not write to the buffer if the operation fails.
224 *
225 * \param[in] pDpyOverride The override from which to read the EDID data.
226 * \param[out] buff A pointer to a buffer into which to read
227 * the override's EDID data.
228 * \param[in] len The length of the buffer.
229 *
230 * \return The number of bytes written into the buffer, or 0 if the operation
231 * failed.
232 */
nvReadDpyOverrideEdid(const NVDpyOverrideRec * pDpyOverride,NvU8 * buff,size_t len)233 size_t nvReadDpyOverrideEdid(const NVDpyOverrideRec *pDpyOverride,
234 NvU8 *buff, size_t len)
235 {
236 if ((pDpyOverride == NULL) ||
237 (buff == NULL) ||
238 (pDpyOverride->edid.length == 0) ||
239 (pDpyOverride->edid.length > len)) {
240 return 0;
241 }
242
243 nvkms_memcpy(buff, pDpyOverride->edid.buffer,
244 pDpyOverride->edid.length);
245 return pDpyOverride->edid.length;
246 }
247
248 /*
249 * Delete all display overrides. This should only ever be called during shutdown
250 * of NVKMS or to cleanup when display override initialization fails.
251 */
nvClearDpyOverrides(void)252 void nvClearDpyOverrides(void)
253 {
254 NVDpyOverridePtr pDpyOverride, tmp;
255 FOR_ALL_DPY_OVERRIDES_SAFE(pDpyOverride, tmp) {
256 nvListDel(&pDpyOverride->entry);
257 if (pDpyOverride->edid.buffer != NULL) {
258 nvFree(pDpyOverride->edid.buffer);
259 }
260 nvFree(pDpyOverride);
261 }
262
263 nvAssert(nvListIsEmpty(&dpyOverrideList));
264 }
265