Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.13-rc2 167 lines 4.4 kB view raw
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/* 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 73 if (tag == 3 && len >= 5 && i + len <= end && 74 edid[i + 1] == 0x03 && 75 edid[i + 2] == 0x0c && 76 edid[i + 3] == 0x00) 77 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);