summaryrefslogtreecommitdiffstats
path: root/drivers/media/cec-edid.c
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2016-06-17 08:13:08 -0400
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2016-06-28 08:45:51 -0400
commit29fb44a58ac12bffe4c225f95afdc22364db070e (patch)
tree2e71bc0fbc164eaa0edc3627df7b0bf5c7ede445 /drivers/media/cec-edid.c
parentefe2938dd64cb990338a1f8a92e642f893e06c8d (diff)
[media] cec-edid: add module for EDID CEC helper functions
The cec-edid module contains helper functions to find and manipulate the CEC physical address inside an EDID. Even if the CEC support itself is disabled, drivers will still need these functions. Which is the reason this is module is separate from the upcoming CEC framework. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers/media/cec-edid.c')
-rw-r--r--drivers/media/cec-edid.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/drivers/media/cec-edid.c b/drivers/media/cec-edid.c
new file mode 100644
index 000000000000..70018247bdda
--- /dev/null
+++ b/drivers/media/cec-edid.c
@@ -0,0 +1,168 @@
1/*
2 * cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
3 *
4 * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
5 *
6 * This program is free software; you may redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
13 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
14 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
15 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17 * SOFTWARE.
18 */
19
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/types.h>
23#include <media/cec-edid.h>
24
25/*
26 * This EDID is expected to be a CEA-861 compliant, which means that there are
27 * at least two blocks and one or more of the extensions blocks are CEA-861
28 * blocks.
29 *
30 * The returned location is guaranteed to be < size - 1.
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 /* Sanity check: at least 2 blocks and a multiple of the block size */
39 if (blocks < 2 || size % 128)
40 return 0;
41
42 /*
43 * If there are fewer extension blocks than the size, then update
44 * 'blocks'. It is allowed to have more extension blocks than the size,
45 * since some hardware can only read e.g. 256 bytes of the EDID, even
46 * though more blocks are present. The first CEA-861 extension block
47 * should normally be in block 1 anyway.
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 /* Skip any non-CEA-861 extension blocks */
56 if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
57 continue;
58
59 /* search Vendor Specific Data Block (tag 3) */
60 d = edid[offset + 2] & 0x7f;
61 /* Check if there are Data Blocks */
62 if (d <= 4)
63 continue;
64 if (d > 4) {
65 unsigned int i = offset + 4;
66 unsigned int end = offset + d;
67
68 /* Note: 'end' is always < 'size' */
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 return i + 4;
75 i += len + 1;
76 } while (i < end);
77 }
78 }
79 return 0;
80}
81
82u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
83 unsigned int *offset)
84{
85 unsigned int loc = cec_get_edid_spa_location(edid, size);
86
87 if (offset)
88 *offset = loc;
89 if (loc == 0)
90 return CEC_PHYS_ADDR_INVALID;
91 return (edid[loc] << 8) | edid[loc + 1];
92}
93EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
94
95void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
96{
97 unsigned int loc = cec_get_edid_spa_location(edid, size);
98 u8 sum = 0;
99 unsigned int i;
100
101 if (loc == 0)
102 return;
103 edid[loc] = phys_addr >> 8;
104 edid[loc + 1] = phys_addr & 0xff;
105 loc &= ~0x7f;
106
107 /* update the checksum */
108 for (i = loc; i < loc + 127; i++)
109 sum += edid[i];
110 edid[i] = 256 - sum;
111}
112EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
113
114u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
115{
116 /* Check if input is sane */
117 if (WARN_ON(input == 0 || input > 0xf))
118 return CEC_PHYS_ADDR_INVALID;
119
120 if (phys_addr == 0)
121 return input << 12;
122
123 if ((phys_addr & 0x0fff) == 0)
124 return phys_addr | (input << 8);
125
126 if ((phys_addr & 0x00ff) == 0)
127 return phys_addr | (input << 4);
128
129 if ((phys_addr & 0x000f) == 0)
130 return phys_addr | input;
131
132 /*
133 * All nibbles are used so no valid physical addresses can be assigned
134 * to the input.
135 */
136 return CEC_PHYS_ADDR_INVALID;
137}
138EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
139
140int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
141{
142 int i;
143
144 if (parent)
145 *parent = phys_addr;
146 if (port)
147 *port = 0;
148 if (phys_addr == CEC_PHYS_ADDR_INVALID)
149 return 0;
150 for (i = 0; i < 16; i += 4)
151 if (phys_addr & (0xf << i))
152 break;
153 if (i == 16)
154 return 0;
155 if (parent)
156 *parent = phys_addr & (0xfff0 << i);
157 if (port)
158 *port = (phys_addr >> i) & 0xf;
159 for (i += 4; i < 16; i += 4)
160 if ((phys_addr & (0xf << i)) == 0)
161 return -EINVAL;
162 return 0;
163}
164EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
165
166MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
167MODULE_DESCRIPTION("CEC EDID helper functions");
168MODULE_LICENSE("GPL");