blob: 19a31d4c8603540a4c709d9c8310092af26dd6c9 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
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
25u16 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}
36EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
37
38void 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}
55EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
56
57u16 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}
81EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
82
83int 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}
107EXPORT_SYMBOL_GPL(cec_phys_addr_validate);