diff options
-rw-r--r-- | drivers/media/Kconfig | 3 | ||||
-rw-r--r-- | drivers/media/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/cec-edid.c | 168 | ||||
-rw-r--r-- | include/media/cec-edid.h | 104 |
4 files changed, 277 insertions, 0 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index a8518fb3bca7..052dcf77174b 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig | |||
@@ -80,6 +80,9 @@ config MEDIA_RC_SUPPORT | |||
80 | 80 | ||
81 | Say Y when you have a TV or an IR device. | 81 | Say Y when you have a TV or an IR device. |
82 | 82 | ||
83 | config MEDIA_CEC_EDID | ||
84 | tristate | ||
85 | |||
83 | # | 86 | # |
84 | # Media controller | 87 | # Media controller |
85 | # Selectable only for webcam/grabbers, as other drivers don't use it | 88 | # Selectable only for webcam/grabbers, as other drivers don't use it |
diff --git a/drivers/media/Makefile b/drivers/media/Makefile index e608bbce0c35..b56f013b78c3 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile | |||
@@ -2,6 +2,8 @@ | |||
2 | # Makefile for the kernel multimedia device drivers. | 2 | # Makefile for the kernel multimedia device drivers. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_MEDIA_CEC_EDID) += cec-edid.o | ||
6 | |||
5 | media-objs := media-device.o media-devnode.o media-entity.o | 7 | media-objs := media-device.o media-devnode.o media-entity.o |
6 | 8 | ||
7 | # | 9 | # |
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 | */ | ||
32 | static 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 | |||
82 | u16 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 | } | ||
93 | EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr); | ||
94 | |||
95 | void 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 | } | ||
112 | EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr); | ||
113 | |||
114 | u16 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 | } | ||
138 | EXPORT_SYMBOL_GPL(cec_phys_addr_for_input); | ||
139 | |||
140 | int 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 | } | ||
164 | EXPORT_SYMBOL_GPL(cec_phys_addr_validate); | ||
165 | |||
166 | MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); | ||
167 | MODULE_DESCRIPTION("CEC EDID helper functions"); | ||
168 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/media/cec-edid.h b/include/media/cec-edid.h new file mode 100644 index 000000000000..bdf731ecba1a --- /dev/null +++ b/include/media/cec-edid.h | |||
@@ -0,0 +1,104 @@ | |||
1 | /* | ||
2 | * cec-edid - HDMI Consumer Electronics Control & EDID helpers | ||
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 | #ifndef _MEDIA_CEC_EDID_H | ||
21 | #define _MEDIA_CEC_EDID_H | ||
22 | |||
23 | #include <linux/types.h> | ||
24 | |||
25 | #define CEC_PHYS_ADDR_INVALID 0xffff | ||
26 | #define cec_phys_addr_exp(pa) \ | ||
27 | ((pa) >> 12), ((pa) >> 8) & 0xf, ((pa) >> 4) & 0xf, (pa) & 0xf | ||
28 | |||
29 | /** | ||
30 | * cec_get_edid_phys_addr() - find and return the physical address | ||
31 | * | ||
32 | * @edid: pointer to the EDID data | ||
33 | * @size: size in bytes of the EDID data | ||
34 | * @offset: If not %NULL then the location of the physical address | ||
35 | * bytes in the EDID will be returned here. This is set to 0 | ||
36 | * if there is no physical address found. | ||
37 | * | ||
38 | * Return: the physical address or CEC_PHYS_ADDR_INVALID if there is none. | ||
39 | */ | ||
40 | u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size, | ||
41 | unsigned int *offset); | ||
42 | |||
43 | /** | ||
44 | * cec_set_edid_phys_addr() - find and set the physical address | ||
45 | * | ||
46 | * @edid: pointer to the EDID data | ||
47 | * @size: size in bytes of the EDID data | ||
48 | * @phys_addr: the new physical address | ||
49 | * | ||
50 | * This function finds the location of the physical address in the EDID | ||
51 | * and fills in the given physical address and updates the checksum | ||
52 | * at the end of the EDID block. It does nothing if the EDID doesn't | ||
53 | * contain a physical address. | ||
54 | */ | ||
55 | void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr); | ||
56 | |||
57 | /** | ||
58 | * cec_phys_addr_for_input() - calculate the PA for an input | ||
59 | * | ||
60 | * @phys_addr: the physical address of the parent | ||
61 | * @input: the number of the input port, must be between 1 and 15 | ||
62 | * | ||
63 | * This function calculates a new physical address based on the input | ||
64 | * port number. For example: | ||
65 | * | ||
66 | * PA = 0.0.0.0 and input = 2 becomes 2.0.0.0 | ||
67 | * | ||
68 | * PA = 3.0.0.0 and input = 1 becomes 3.1.0.0 | ||
69 | * | ||
70 | * PA = 3.2.1.0 and input = 5 becomes 3.2.1.5 | ||
71 | * | ||
72 | * PA = 3.2.1.3 and input = 5 becomes f.f.f.f since it maxed out the depth. | ||
73 | * | ||
74 | * Return: the new physical address or CEC_PHYS_ADDR_INVALID. | ||
75 | */ | ||
76 | u16 cec_phys_addr_for_input(u16 phys_addr, u8 input); | ||
77 | |||
78 | /** | ||
79 | * cec_phys_addr_validate() - validate a physical address from an EDID | ||
80 | * | ||
81 | * @phys_addr: the physical address to validate | ||
82 | * @parent: if not %NULL, then this is filled with the parents PA. | ||
83 | * @port: if not %NULL, then this is filled with the input port. | ||
84 | * | ||
85 | * This validates a physical address as read from an EDID. If the | ||
86 | * PA is invalid (such as 1.0.1.0 since '0' is only allowed at the end), | ||
87 | * then it will return -EINVAL. | ||
88 | * | ||
89 | * The parent PA is passed into %parent and the input port is passed into | ||
90 | * %port. For example: | ||
91 | * | ||
92 | * PA = 0.0.0.0: has parent 0.0.0.0 and input port 0. | ||
93 | * | ||
94 | * PA = 1.0.0.0: has parent 0.0.0.0 and input port 1. | ||
95 | * | ||
96 | * PA = 3.2.0.0: has parent 3.0.0.0 and input port 2. | ||
97 | * | ||
98 | * PA = f.f.f.f: has parent f.f.f.f and input port 0. | ||
99 | * | ||
100 | * Return: 0 if the PA is valid, -EINVAL if not. | ||
101 | */ | ||
102 | int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port); | ||
103 | |||
104 | #endif /* _MEDIA_CEC_EDID_H */ | ||