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 v5.3-rc4 253 lines 6.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * cec-notifier.c - notify CEC drivers of physical address changes 4 * 5 * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk> 6 * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. 7 */ 8 9#include <linux/export.h> 10#include <linux/string.h> 11#include <linux/slab.h> 12#include <linux/list.h> 13#include <linux/kref.h> 14#include <linux/of_platform.h> 15 16#include <media/cec.h> 17#include <media/cec-notifier.h> 18#include <drm/drm_edid.h> 19 20struct cec_notifier { 21 struct mutex lock; 22 struct list_head head; 23 struct kref kref; 24 struct device *hdmi_dev; 25 struct cec_connector_info conn_info; 26 const char *conn_name; 27 struct cec_adapter *cec_adap; 28 void (*callback)(struct cec_adapter *adap, u16 pa); 29 30 u16 phys_addr; 31}; 32 33static LIST_HEAD(cec_notifiers); 34static DEFINE_MUTEX(cec_notifiers_lock); 35 36struct cec_notifier * 37cec_notifier_get_conn(struct device *hdmi_dev, const char *conn_name) 38{ 39 struct cec_notifier *n; 40 41 mutex_lock(&cec_notifiers_lock); 42 list_for_each_entry(n, &cec_notifiers, head) { 43 if (n->hdmi_dev == hdmi_dev && 44 (!conn_name || 45 (n->conn_name && !strcmp(n->conn_name, conn_name)))) { 46 kref_get(&n->kref); 47 mutex_unlock(&cec_notifiers_lock); 48 return n; 49 } 50 } 51 n = kzalloc(sizeof(*n), GFP_KERNEL); 52 if (!n) 53 goto unlock; 54 n->hdmi_dev = hdmi_dev; 55 if (conn_name) { 56 n->conn_name = kstrdup(conn_name, GFP_KERNEL); 57 if (!n->conn_name) { 58 kfree(n); 59 n = NULL; 60 goto unlock; 61 } 62 } 63 n->phys_addr = CEC_PHYS_ADDR_INVALID; 64 65 mutex_init(&n->lock); 66 kref_init(&n->kref); 67 list_add_tail(&n->head, &cec_notifiers); 68unlock: 69 mutex_unlock(&cec_notifiers_lock); 70 return n; 71} 72EXPORT_SYMBOL_GPL(cec_notifier_get_conn); 73 74static void cec_notifier_release(struct kref *kref) 75{ 76 struct cec_notifier *n = 77 container_of(kref, struct cec_notifier, kref); 78 79 list_del(&n->head); 80 kfree(n->conn_name); 81 kfree(n); 82} 83 84void cec_notifier_put(struct cec_notifier *n) 85{ 86 mutex_lock(&cec_notifiers_lock); 87 kref_put(&n->kref, cec_notifier_release); 88 mutex_unlock(&cec_notifiers_lock); 89} 90EXPORT_SYMBOL_GPL(cec_notifier_put); 91 92struct cec_notifier * 93cec_notifier_conn_register(struct device *hdmi_dev, const char *conn_name, 94 const struct cec_connector_info *conn_info) 95{ 96 struct cec_notifier *n = cec_notifier_get_conn(hdmi_dev, conn_name); 97 98 if (!n) 99 return n; 100 101 mutex_lock(&n->lock); 102 n->phys_addr = CEC_PHYS_ADDR_INVALID; 103 if (conn_info) 104 n->conn_info = *conn_info; 105 else 106 memset(&n->conn_info, 0, sizeof(n->conn_info)); 107 if (n->cec_adap) { 108 cec_phys_addr_invalidate(n->cec_adap); 109 cec_s_conn_info(n->cec_adap, conn_info); 110 } 111 mutex_unlock(&n->lock); 112 return n; 113} 114EXPORT_SYMBOL_GPL(cec_notifier_conn_register); 115 116void cec_notifier_conn_unregister(struct cec_notifier *n) 117{ 118 if (!n) 119 return; 120 121 mutex_lock(&n->lock); 122 memset(&n->conn_info, 0, sizeof(n->conn_info)); 123 n->phys_addr = CEC_PHYS_ADDR_INVALID; 124 if (n->cec_adap) { 125 cec_phys_addr_invalidate(n->cec_adap); 126 cec_s_conn_info(n->cec_adap, NULL); 127 } 128 mutex_unlock(&n->lock); 129 cec_notifier_put(n); 130} 131EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister); 132 133struct cec_notifier * 134cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name, 135 struct cec_adapter *adap) 136{ 137 struct cec_notifier *n; 138 139 if (WARN_ON(!adap)) 140 return NULL; 141 142 n = cec_notifier_get_conn(hdmi_dev, conn_name); 143 if (!n) 144 return n; 145 146 mutex_lock(&n->lock); 147 n->cec_adap = adap; 148 adap->conn_info = n->conn_info; 149 adap->notifier = n; 150 cec_s_phys_addr(adap, n->phys_addr, false); 151 mutex_unlock(&n->lock); 152 return n; 153} 154EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_register); 155 156void cec_notifier_cec_adap_unregister(struct cec_notifier *n) 157{ 158 if (!n) 159 return; 160 161 mutex_lock(&n->lock); 162 n->cec_adap->notifier = NULL; 163 n->cec_adap = NULL; 164 n->callback = NULL; 165 mutex_unlock(&n->lock); 166 cec_notifier_put(n); 167} 168EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_unregister); 169 170void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) 171{ 172 if (n == NULL) 173 return; 174 175 mutex_lock(&n->lock); 176 n->phys_addr = pa; 177 if (n->callback) 178 n->callback(n->cec_adap, n->phys_addr); 179 else if (n->cec_adap) 180 cec_s_phys_addr(n->cec_adap, n->phys_addr, false); 181 mutex_unlock(&n->lock); 182} 183EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr); 184 185void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, 186 const struct edid *edid) 187{ 188 u16 pa = CEC_PHYS_ADDR_INVALID; 189 190 if (n == NULL) 191 return; 192 193 if (edid && edid->extensions) 194 pa = cec_get_edid_phys_addr((const u8 *)edid, 195 EDID_LENGTH * (edid->extensions + 1), NULL); 196 cec_notifier_set_phys_addr(n, pa); 197} 198EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid); 199 200void cec_notifier_register(struct cec_notifier *n, 201 struct cec_adapter *adap, 202 void (*callback)(struct cec_adapter *adap, u16 pa)) 203{ 204 kref_get(&n->kref); 205 mutex_lock(&n->lock); 206 n->cec_adap = adap; 207 n->callback = callback; 208 n->callback(adap, n->phys_addr); 209 mutex_unlock(&n->lock); 210} 211EXPORT_SYMBOL_GPL(cec_notifier_register); 212 213void cec_notifier_unregister(struct cec_notifier *n) 214{ 215 /* Do nothing unless cec_notifier_register was called first */ 216 if (!n->callback) 217 return; 218 219 mutex_lock(&n->lock); 220 n->callback = NULL; 221 mutex_unlock(&n->lock); 222 cec_notifier_put(n); 223} 224EXPORT_SYMBOL_GPL(cec_notifier_unregister); 225 226struct device *cec_notifier_parse_hdmi_phandle(struct device *dev) 227{ 228 struct platform_device *hdmi_pdev; 229 struct device *hdmi_dev = NULL; 230 struct device_node *np; 231 232 np = of_parse_phandle(dev->of_node, "hdmi-phandle", 0); 233 234 if (!np) { 235 dev_err(dev, "Failed to find HDMI node in device tree\n"); 236 return ERR_PTR(-ENODEV); 237 } 238 hdmi_pdev = of_find_device_by_node(np); 239 of_node_put(np); 240 if (hdmi_pdev) { 241 hdmi_dev = &hdmi_pdev->dev; 242 /* 243 * Note that the device struct is only used as a key into the 244 * cec_notifiers list, it is never actually accessed. 245 * So we decrement the reference here so we don't leak 246 * memory. 247 */ 248 put_device(hdmi_dev); 249 return hdmi_dev; 250 } 251 return ERR_PTR(-EPROBE_DEFER); 252} 253EXPORT_SYMBOL_GPL(cec_notifier_parse_hdmi_phandle);