Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright © 2021 Intel Corporation
4 */
5
6#include <drm/drm_edid.h>
7#include <drm/drm_print.h>
8
9#include "drm_crtc_internal.h"
10#include "drm_displayid_internal.h"
11
12static const struct displayid_header *
13displayid_get_header(const u8 *displayid, int length, int index)
14{
15 const struct displayid_header *base;
16
17 if (sizeof(*base) > length - index)
18 return ERR_PTR(-EINVAL);
19
20 base = (const struct displayid_header *)&displayid[index];
21
22 return base;
23}
24
25static const struct displayid_header *
26validate_displayid(const u8 *displayid, int length, int idx)
27{
28 int i, dispid_length;
29 u8 csum = 0;
30 const struct displayid_header *base;
31
32 base = displayid_get_header(displayid, length, idx);
33 if (IS_ERR(base))
34 return base;
35
36 DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
37 base->rev, base->bytes, base->prod_id, base->ext_count);
38
39 /* +1 for DispID checksum */
40 dispid_length = sizeof(*base) + base->bytes + 1;
41 if (dispid_length > length - idx)
42 return ERR_PTR(-EINVAL);
43
44 for (i = 0; i < dispid_length; i++)
45 csum += displayid[idx + i];
46 if (csum) {
47 DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum);
48 return ERR_PTR(-EINVAL);
49 }
50
51 return base;
52}
53
54static const u8 *drm_find_displayid_extension(const struct drm_edid *drm_edid,
55 int *length, int *idx,
56 int *ext_index)
57{
58 const struct displayid_header *base;
59 const u8 *displayid;
60
61 displayid = drm_edid_find_extension(drm_edid, DISPLAYID_EXT, ext_index);
62 if (!displayid)
63 return NULL;
64
65 /* EDID extensions block checksum isn't for us */
66 *length = EDID_LENGTH - 1;
67 *idx = 1;
68
69 base = validate_displayid(displayid, *length, *idx);
70 if (IS_ERR(base))
71 return NULL;
72
73 *length = *idx + sizeof(*base) + base->bytes;
74
75 return displayid;
76}
77
78void displayid_iter_edid_begin(const struct drm_edid *drm_edid,
79 struct displayid_iter *iter)
80{
81 memset(iter, 0, sizeof(*iter));
82
83 iter->drm_edid = drm_edid;
84}
85
86static const struct displayid_block *
87displayid_iter_block(const struct displayid_iter *iter)
88{
89 const struct displayid_block *block;
90
91 if (!iter->section)
92 return NULL;
93
94 block = (const struct displayid_block *)&iter->section[iter->idx];
95
96 if (iter->idx + sizeof(*block) <= iter->length &&
97 iter->idx + sizeof(*block) + block->num_bytes <= iter->length)
98 return block;
99
100 return NULL;
101}
102
103const struct displayid_block *
104__displayid_iter_next(struct displayid_iter *iter)
105{
106 const struct displayid_block *block;
107
108 if (!iter->drm_edid)
109 return NULL;
110
111 if (iter->section) {
112 /* current block should always be valid */
113 block = displayid_iter_block(iter);
114 if (WARN_ON(!block)) {
115 iter->section = NULL;
116 iter->drm_edid = NULL;
117 return NULL;
118 }
119
120 /* next block in section */
121 iter->idx += sizeof(*block) + block->num_bytes;
122
123 block = displayid_iter_block(iter);
124 if (block)
125 return block;
126 }
127
128 for (;;) {
129 /* The first section we encounter is the base section */
130 bool base_section = !iter->section;
131
132 iter->section = drm_find_displayid_extension(iter->drm_edid,
133 &iter->length,
134 &iter->idx,
135 &iter->ext_index);
136 if (!iter->section) {
137 iter->drm_edid = NULL;
138 return NULL;
139 }
140
141 /* Save the structure version and primary use case. */
142 if (base_section) {
143 const struct displayid_header *base;
144
145 base = displayid_get_header(iter->section, iter->length,
146 iter->idx);
147 if (!IS_ERR(base)) {
148 iter->version = base->rev;
149 iter->primary_use = base->prod_id;
150 }
151 }
152
153 iter->idx += sizeof(struct displayid_header);
154
155 block = displayid_iter_block(iter);
156 if (block)
157 return block;
158 }
159}
160
161void displayid_iter_end(struct displayid_iter *iter)
162{
163 memset(iter, 0, sizeof(*iter));
164}
165
166/* DisplayID Structure Version/Revision from the Base Section. */
167u8 displayid_version(const struct displayid_iter *iter)
168{
169 return iter->version;
170}
171
172/*
173 * DisplayID Primary Use Case (2.0+) or Product Type Identifier (1.0-1.3) from
174 * the Base Section.
175 */
176u8 displayid_primary_use(const struct displayid_iter *iter)
177{
178 return iter->primary_use;
179}