1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/types.h>
23#include <media/cec.h>
24
25
26
27
28
29
30
31
32static unsigned int cec_get_edid_spa_location(const u8 *edid, unsigned int size)
33{
34 unsigned int blocks = size / 128;
35 unsigned int block;
36 u8 d;
37
38
39 if (blocks < 2 || size % 128)
40 return 0;
41
42
43
44
45
46
47
48
49 if (edid[0x7e] + 1 < blocks)
50 blocks = edid[0x7e] + 1;
51
52 for (block = 1; block < blocks; block++) {
53 unsigned int offset = block * 128;
54
55
56 if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
57 continue;
58
59
60 d = edid[offset + 2] & 0x7f;
61
62 if (d <= 4)
63 continue;
64 if (d > 4) {
65 unsigned int i = offset + 4;
66 unsigned int end = offset + d;
67
68
69 do {
70 u8 tag = edid[i] >> 5;
71 u8 len = edid[i] & 0x1f;
72
73 if (tag == 3 && len >= 5 && i + len <= end &&
74 edid[i + 1] == 0x03 &&
75 edid[i + 2] == 0x0c &&
76 edid[i + 3] == 0x00)
77 return i + 4;
78 i += len + 1;
79 } while (i < end);
80 }
81 }
82 return 0;
83}
84
85u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
86 unsigned int *offset)
87{
88 unsigned int loc = cec_get_edid_spa_location(edid, size);
89
90 if (offset)
91 *offset = loc;
92 if (loc == 0)
93 return CEC_PHYS_ADDR_INVALID;
94 return (edid[loc] << 8) | edid[loc + 1];
95}
96EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
97
98void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
99{
100 unsigned int loc = cec_get_edid_spa_location(edid, size);
101 u8 sum = 0;
102 unsigned int i;
103
104 if (loc == 0)
105 return;
106 edid[loc] = phys_addr >> 8;
107 edid[loc + 1] = phys_addr & 0xff;
108 loc &= ~0x7f;
109
110
111 for (i = loc; i < loc + 127; i++)
112 sum += edid[i];
113 edid[i] = 256 - sum;
114}
115EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
116
117u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
118{
119
120 if (WARN_ON(input == 0 || input > 0xf))
121 return CEC_PHYS_ADDR_INVALID;
122
123 if (phys_addr == 0)
124 return input << 12;
125
126 if ((phys_addr & 0x0fff) == 0)
127 return phys_addr | (input << 8);
128
129 if ((phys_addr & 0x00ff) == 0)
130 return phys_addr | (input << 4);
131
132 if ((phys_addr & 0x000f) == 0)
133 return phys_addr | input;
134
135
136
137
138
139 return CEC_PHYS_ADDR_INVALID;
140}
141EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
142
143int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
144{
145 int i;
146
147 if (parent)
148 *parent = phys_addr;
149 if (port)
150 *port = 0;
151 if (phys_addr == CEC_PHYS_ADDR_INVALID)
152 return 0;
153 for (i = 0; i < 16; i += 4)
154 if (phys_addr & (0xf << i))
155 break;
156 if (i == 16)
157 return 0;
158 if (parent)
159 *parent = phys_addr & (0xfff0 << i);
160 if (port)
161 *port = (phys_addr >> i) & 0xf;
162 for (i += 4; i < 16; i += 4)
163 if ((phys_addr & (0xf << i)) == 0)
164 return -EINVAL;
165 return 0;
166}
167EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
168