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