GNU Linux-libre 4.14.290-gnu1
[releases.git] / drivers / media / cec / cec-edid.c
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.h>
24
25 u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
26                            unsigned int *offset)
27 {
28         unsigned int loc = cec_get_edid_spa_location(edid, size);
29
30         if (offset)
31                 *offset = loc;
32         if (loc == 0)
33                 return CEC_PHYS_ADDR_INVALID;
34         return (edid[loc] << 8) | edid[loc + 1];
35 }
36 EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
37
38 void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
39 {
40         unsigned int loc = cec_get_edid_spa_location(edid, size);
41         u8 sum = 0;
42         unsigned int i;
43
44         if (loc == 0)
45                 return;
46         edid[loc] = phys_addr >> 8;
47         edid[loc + 1] = phys_addr & 0xff;
48         loc &= ~0x7f;
49
50         /* update the checksum */
51         for (i = loc; i < loc + 127; i++)
52                 sum += edid[i];
53         edid[i] = 256 - sum;
54 }
55 EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
56
57 u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
58 {
59         /* Check if input is sane */
60         if (WARN_ON(input == 0 || input > 0xf))
61                 return CEC_PHYS_ADDR_INVALID;
62
63         if (phys_addr == 0)
64                 return input << 12;
65
66         if ((phys_addr & 0x0fff) == 0)
67                 return phys_addr | (input << 8);
68
69         if ((phys_addr & 0x00ff) == 0)
70                 return phys_addr | (input << 4);
71
72         if ((phys_addr & 0x000f) == 0)
73                 return phys_addr | input;
74
75         /*
76          * All nibbles are used so no valid physical addresses can be assigned
77          * to the input.
78          */
79         return CEC_PHYS_ADDR_INVALID;
80 }
81 EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
82
83 int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
84 {
85         int i;
86
87         if (parent)
88                 *parent = phys_addr;
89         if (port)
90                 *port = 0;
91         if (phys_addr == CEC_PHYS_ADDR_INVALID)
92                 return 0;
93         for (i = 0; i < 16; i += 4)
94                 if (phys_addr & (0xf << i))
95                         break;
96         if (i == 16)
97                 return 0;
98         if (parent)
99                 *parent = phys_addr & (0xfff0 << i);
100         if (port)
101                 *port = (phys_addr >> i) & 0xf;
102         for (i += 4; i < 16; i += 4)
103                 if ((phys_addr & (0xf << i)) == 0)
104                         return -EINVAL;
105         return 0;
106 }
107 EXPORT_SYMBOL_GPL(cec_phys_addr_validate);