root/drivers/media/cec/cec-notifier.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cec_notifier_get_conn
  2. cec_notifier_release
  3. cec_notifier_put
  4. cec_notifier_conn_register
  5. cec_notifier_conn_unregister
  6. cec_notifier_cec_adap_register
  7. cec_notifier_cec_adap_unregister
  8. cec_notifier_set_phys_addr
  9. cec_notifier_set_phys_addr_from_edid
  10. cec_notifier_register
  11. cec_notifier_unregister
  12. cec_notifier_parse_hdmi_phandle

   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 
  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 *conn_name;
  27         struct cec_adapter *cec_adap;
  28         void (*callback)(struct cec_adapter *adap, u16 pa);
  29 
  30         u16 phys_addr;
  31 };
  32 
  33 static LIST_HEAD(cec_notifiers);
  34 static DEFINE_MUTEX(cec_notifiers_lock);
  35 
  36 struct cec_notifier *
  37 cec_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);
  68 unlock:
  69         mutex_unlock(&cec_notifiers_lock);
  70         return n;
  71 }
  72 EXPORT_SYMBOL_GPL(cec_notifier_get_conn);
  73 
  74 static 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 
  84 void 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 }
  90 EXPORT_SYMBOL_GPL(cec_notifier_put);
  91 
  92 struct cec_notifier *
  93 cec_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 }
 114 EXPORT_SYMBOL_GPL(cec_notifier_conn_register);
 115 
 116 void 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 }
 131 EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister);
 132 
 133 struct cec_notifier *
 134 cec_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 }
 154 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_register);
 155 
 156 void 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 }
 168 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_unregister);
 169 
 170 void 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 }
 183 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
 184 
 185 void 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 }
 198 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
 199 
 200 void 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 }
 211 EXPORT_SYMBOL_GPL(cec_notifier_register);
 212 
 213 void 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         n->cec_adap->notifier = NULL;
 222         n->cec_adap = NULL;
 223         mutex_unlock(&n->lock);
 224         cec_notifier_put(n);
 225 }
 226 EXPORT_SYMBOL_GPL(cec_notifier_unregister);
 227 
 228 struct device *cec_notifier_parse_hdmi_phandle(struct device *dev)
 229 {
 230         struct platform_device *hdmi_pdev;
 231         struct device *hdmi_dev = NULL;
 232         struct device_node *np;
 233 
 234         np = of_parse_phandle(dev->of_node, "hdmi-phandle", 0);
 235 
 236         if (!np) {
 237                 dev_err(dev, "Failed to find HDMI node in device tree\n");
 238                 return ERR_PTR(-ENODEV);
 239         }
 240         hdmi_pdev = of_find_device_by_node(np);
 241         of_node_put(np);
 242         if (hdmi_pdev) {
 243                 hdmi_dev = &hdmi_pdev->dev;
 244                 /*
 245                  * Note that the device struct is only used as a key into the
 246                  * cec_notifiers list, it is never actually accessed.
 247                  * So we decrement the reference here so we don't leak
 248                  * memory.
 249                  */
 250                 put_device(hdmi_dev);
 251                 return hdmi_dev;
 252         }
 253         return ERR_PTR(-EPROBE_DEFER);
 254 }
 255 EXPORT_SYMBOL_GPL(cec_notifier_parse_hdmi_phandle);

/* [<][>][^][v][top][bottom][index][help] */