blob: 5719b991e340d3df77cdbbedee48e36c67463273 [file] [log] [blame]
Hans Verkuil29fb44a2016-06-17 09:13:08 -03001/*
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
Hans Verkuil8ac6a1a2016-08-18 04:13:42 -030073 if (tag == 3 && len >= 5 && i + len <= end &&
74 edid[i + 1] == 0x03 &&
75 edid[i + 2] == 0x0c &&
76 edid[i + 3] == 0x00)
Hans Verkuil29fb44a2016-06-17 09:13:08 -030077 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 /* update the checksum */
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 /* Check if input is sane */
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 * All nibbles are used so no valid physical addresses can be assigned
137 * to the input.
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
169MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
170MODULE_DESCRIPTION("CEC EDID helper functions");
171MODULE_LICENSE("GPL");