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