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. 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 20 struct 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 *port_name; 27 struct cec_adapter *cec_adap; 28 29 u16 phys_addr; 30 }; 31 32 static LIST_HEAD(cec_notifiers); 33 static DEFINE_MUTEX(cec_notifiers_lock); 34 35 /** 36 * cec_notifier_get_conn - find or create a new cec_notifier for the given 37 * device and connector tuple. 38 * @hdmi_dev: device that sends the events. 39 * @port_name: the connector name from which the event occurs 40 * 41 * If a notifier for device @dev already exists, then increase the refcount 42 * and return that notifier. 43 * 44 * If it doesn't exist, then allocate a new notifier struct and return a 45 * pointer to that new struct. 46 * 47 * Return NULL if the memory could not be allocated. 48 */ 49 static struct cec_notifier * 50 cec_notifier_get_conn(struct device *hdmi_dev, const char *port_name) 51 { 52 struct cec_notifier *n; 53 54 mutex_lock(&cec_notifiers_lock); 55 list_for_each_entry(n, &cec_notifiers, head) { 56 if (n->hdmi_dev == hdmi_dev && 57 (!port_name || 58 (n->port_name && !strcmp(n->port_name, port_name)))) { 59 kref_get(&n->kref); 60 mutex_unlock(&cec_notifiers_lock); 61 return n; 62 } 63 } 64 n = kzalloc(sizeof(*n), GFP_KERNEL); 65 if (!n) 66 goto unlock; 67 n->hdmi_dev = hdmi_dev; 68 if (port_name) { 69 n->port_name = kstrdup(port_name, GFP_KERNEL); 70 if (!n->port_name) { 71 kfree(n); 72 n = NULL; 73 goto unlock; 74 } 75 } 76 n->phys_addr = CEC_PHYS_ADDR_INVALID; 77 78 mutex_init(&n->lock); 79 kref_init(&n->kref); 80 list_add_tail(&n->head, &cec_notifiers); 81 unlock: 82 mutex_unlock(&cec_notifiers_lock); 83 return n; 84 } 85 86 static void cec_notifier_release(struct kref *kref) 87 { 88 struct cec_notifier *n = 89 container_of(kref, struct cec_notifier, kref); 90 91 list_del(&n->head); 92 kfree(n->port_name); 93 kfree(n); 94 } 95 96 static void cec_notifier_put(struct cec_notifier *n) 97 { 98 mutex_lock(&cec_notifiers_lock); 99 kref_put(&n->kref, cec_notifier_release); 100 mutex_unlock(&cec_notifiers_lock); 101 } 102 103 struct cec_notifier * 104 cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name, 105 const struct cec_connector_info *conn_info) 106 { 107 struct cec_notifier *n = cec_notifier_get_conn(hdmi_dev, port_name); 108 109 if (!n) 110 return n; 111 112 mutex_lock(&n->lock); 113 n->phys_addr = CEC_PHYS_ADDR_INVALID; 114 if (conn_info) 115 n->conn_info = *conn_info; 116 else 117 memset(&n->conn_info, 0, sizeof(n->conn_info)); 118 if (n->cec_adap) { 119 if (!n->cec_adap->adap_controls_phys_addr) 120 cec_phys_addr_invalidate(n->cec_adap); 121 cec_s_conn_info(n->cec_adap, conn_info); 122 } 123 mutex_unlock(&n->lock); 124 return n; 125 } 126 EXPORT_SYMBOL_GPL(cec_notifier_conn_register); 127 128 void cec_notifier_conn_unregister(struct cec_notifier *n) 129 { 130 if (!n) 131 return; 132 133 mutex_lock(&n->lock); 134 memset(&n->conn_info, 0, sizeof(n->conn_info)); 135 n->phys_addr = CEC_PHYS_ADDR_INVALID; 136 if (n->cec_adap) { 137 if (!n->cec_adap->adap_controls_phys_addr) 138 cec_phys_addr_invalidate(n->cec_adap); 139 cec_s_conn_info(n->cec_adap, NULL); 140 } 141 mutex_unlock(&n->lock); 142 cec_notifier_put(n); 143 } 144 EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister); 145 146 struct cec_notifier * 147 cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name, 148 struct cec_adapter *adap) 149 { 150 struct cec_notifier *n; 151 152 if (WARN_ON(!adap)) 153 return NULL; 154 155 n = cec_notifier_get_conn(hdmi_dev, port_name); 156 if (!n) 157 return n; 158 159 mutex_lock(&n->lock); 160 n->cec_adap = adap; 161 adap->conn_info = n->conn_info; 162 adap->notifier = n; 163 if (!adap->adap_controls_phys_addr) 164 cec_s_phys_addr(adap, n->phys_addr, false); 165 mutex_unlock(&n->lock); 166 return n; 167 } 168 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_register); 169 170 void cec_notifier_cec_adap_unregister(struct cec_notifier *n, 171 struct cec_adapter *adap) 172 { 173 if (!n) 174 return; 175 176 mutex_lock(&n->lock); 177 adap->notifier = NULL; 178 n->cec_adap = NULL; 179 mutex_unlock(&n->lock); 180 cec_notifier_put(n); 181 } 182 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_unregister); 183 184 void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) 185 { 186 if (n == NULL) 187 return; 188 189 mutex_lock(&n->lock); 190 n->phys_addr = pa; 191 if (n->cec_adap && !n->cec_adap->adap_controls_phys_addr) 192 cec_s_phys_addr(n->cec_adap, n->phys_addr, false); 193 mutex_unlock(&n->lock); 194 } 195 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr); 196 197 void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, 198 const struct edid *edid) 199 { 200 u16 pa = CEC_PHYS_ADDR_INVALID; 201 202 if (n == NULL) 203 return; 204 205 if (edid && edid->extensions) 206 pa = cec_get_edid_phys_addr((const u8 *)edid, 207 EDID_LENGTH * (edid->extensions + 1), NULL); 208 cec_notifier_set_phys_addr(n, pa); 209 } 210 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid); 211 212 struct device *cec_notifier_parse_hdmi_phandle(struct device *dev) 213 { 214 struct platform_device *hdmi_pdev; 215 struct device *hdmi_dev = NULL; 216 struct device_node *np; 217 218 np = of_parse_phandle(dev->of_node, "hdmi-phandle", 0); 219 220 if (!np) { 221 dev_err(dev, "Failed to find HDMI node in device tree\n"); 222 return ERR_PTR(-ENODEV); 223 } 224 hdmi_pdev = of_find_device_by_node(np); 225 of_node_put(np); 226 if (hdmi_pdev) { 227 hdmi_dev = &hdmi_pdev->dev; 228 /* 229 * Note that the device struct is only used as a key into the 230 * cec_notifiers list, it is never actually accessed. 231 * So we decrement the reference here so we don't leak 232 * memory. 233 */ 234 put_device(hdmi_dev); 235 return hdmi_dev; 236 } 237 return ERR_PTR(-EPROBE_DEFER); 238 } 239 EXPORT_SYMBOL_GPL(cec_notifier_parse_hdmi_phandle); 240