Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | /*
* cec-edid - HDMI Consumer Electronics Control EDID & CEC helper functions
*
* Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* This program is free software; you may redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <media/cec-edid.h>
/*
* This EDID is expected to be a CEA-861 compliant, which means that there are
* at least two blocks and one or more of the extensions blocks are CEA-861
* blocks.
*
* The returned location is guaranteed to be < size - 1.
*/
static unsigned int cec_get_edid_spa_location(const u8 *edid, unsigned int size)
{
unsigned int blocks = size / 128;
unsigned int block;
u8 d;
/* Sanity check: at least 2 blocks and a multiple of the block size */
if (blocks < 2 || size % 128)
return 0;
/*
* If there are fewer extension blocks than the size, then update
* 'blocks'. It is allowed to have more extension blocks than the size,
* since some hardware can only read e.g. 256 bytes of the EDID, even
* though more blocks are present. The first CEA-861 extension block
* should normally be in block 1 anyway.
*/
if (edid[0x7e] + 1 < blocks)
blocks = edid[0x7e] + 1;
for (block = 1; block < blocks; block++) {
unsigned int offset = block * 128;
/* Skip any non-CEA-861 extension blocks */
if (edid[offset] != 0x02 || edid[offset + 1] != 0x03)
continue;
/* search Vendor Specific Data Block (tag 3) */
d = edid[offset + 2] & 0x7f;
/* Check if there are Data Blocks */
if (d <= 4)
continue;
if (d > 4) {
unsigned int i = offset + 4;
unsigned int end = offset + d;
/* Note: 'end' is always < 'size' */
do {
u8 tag = edid[i] >> 5;
u8 len = edid[i] & 0x1f;
if (tag == 3 && len >= 5 && i + len <= end &&
edid[i + 1] == 0x03 &&
edid[i + 2] == 0x0c &&
edid[i + 3] == 0x00)
return i + 4;
i += len + 1;
} while (i < end);
}
}
return 0;
}
u16 cec_get_edid_phys_addr(const u8 *edid, unsigned int size,
unsigned int *offset)
{
unsigned int loc = cec_get_edid_spa_location(edid, size);
if (offset)
*offset = loc;
if (loc == 0)
return CEC_PHYS_ADDR_INVALID;
return (edid[loc] << 8) | edid[loc + 1];
}
EXPORT_SYMBOL_GPL(cec_get_edid_phys_addr);
void cec_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr)
{
unsigned int loc = cec_get_edid_spa_location(edid, size);
u8 sum = 0;
unsigned int i;
if (loc == 0)
return;
edid[loc] = phys_addr >> 8;
edid[loc + 1] = phys_addr & 0xff;
loc &= ~0x7f;
/* update the checksum */
for (i = loc; i < loc + 127; i++)
sum += edid[i];
edid[i] = 256 - sum;
}
EXPORT_SYMBOL_GPL(cec_set_edid_phys_addr);
u16 cec_phys_addr_for_input(u16 phys_addr, u8 input)
{
/* Check if input is sane */
if (WARN_ON(input == 0 || input > 0xf))
return CEC_PHYS_ADDR_INVALID;
if (phys_addr == 0)
return input << 12;
if ((phys_addr & 0x0fff) == 0)
return phys_addr | (input << 8);
if ((phys_addr & 0x00ff) == 0)
return phys_addr | (input << 4);
if ((phys_addr & 0x000f) == 0)
return phys_addr | input;
/*
* All nibbles are used so no valid physical addresses can be assigned
* to the input.
*/
return CEC_PHYS_ADDR_INVALID;
}
EXPORT_SYMBOL_GPL(cec_phys_addr_for_input);
int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port)
{
int i;
if (parent)
*parent = phys_addr;
if (port)
*port = 0;
if (phys_addr == CEC_PHYS_ADDR_INVALID)
return 0;
for (i = 0; i < 16; i += 4)
if (phys_addr & (0xf << i))
break;
if (i == 16)
return 0;
if (parent)
*parent = phys_addr & (0xfff0 << i);
if (port)
*port = (phys_addr >> i) & 0xf;
for (i += 4; i < 16; i += 4)
if ((phys_addr & (0xf << i)) == 0)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL_GPL(cec_phys_addr_validate);
MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
MODULE_DESCRIPTION("CEC EDID helper functions");
MODULE_LICENSE("GPL");
|