Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * cec-notifier.c - notify CEC drivers of physical address changes
0004  *
0005  * Copyright 2016 Russell King.
0006  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
0007  */
0008 
0009 #include <linux/export.h>
0010 #include <linux/string.h>
0011 #include <linux/slab.h>
0012 #include <linux/i2c.h>
0013 #include <linux/list.h>
0014 #include <linux/kref.h>
0015 #include <linux/of_platform.h>
0016 
0017 #include <media/cec.h>
0018 #include <media/cec-notifier.h>
0019 #include <drm/drm_edid.h>
0020 
0021 struct cec_notifier {
0022     struct mutex lock;
0023     struct list_head head;
0024     struct kref kref;
0025     struct device *hdmi_dev;
0026     struct cec_connector_info conn_info;
0027     const char *port_name;
0028     struct cec_adapter *cec_adap;
0029 
0030     u16 phys_addr;
0031 };
0032 
0033 static LIST_HEAD(cec_notifiers);
0034 static DEFINE_MUTEX(cec_notifiers_lock);
0035 
0036 /**
0037  * cec_notifier_get_conn - find or create a new cec_notifier for the given
0038  * device and connector tuple.
0039  * @hdmi_dev: device that sends the events.
0040  * @port_name: the connector name from which the event occurs
0041  *
0042  * If a notifier for device @dev already exists, then increase the refcount
0043  * and return that notifier.
0044  *
0045  * If it doesn't exist, then allocate a new notifier struct and return a
0046  * pointer to that new struct.
0047  *
0048  * Return NULL if the memory could not be allocated.
0049  */
0050 static struct cec_notifier *
0051 cec_notifier_get_conn(struct device *hdmi_dev, const char *port_name)
0052 {
0053     struct cec_notifier *n;
0054 
0055     mutex_lock(&cec_notifiers_lock);
0056     list_for_each_entry(n, &cec_notifiers, head) {
0057         if (n->hdmi_dev == hdmi_dev &&
0058             (!port_name ||
0059              (n->port_name && !strcmp(n->port_name, port_name)))) {
0060             kref_get(&n->kref);
0061             mutex_unlock(&cec_notifiers_lock);
0062             return n;
0063         }
0064     }
0065     n = kzalloc(sizeof(*n), GFP_KERNEL);
0066     if (!n)
0067         goto unlock;
0068     n->hdmi_dev = hdmi_dev;
0069     if (port_name) {
0070         n->port_name = kstrdup(port_name, GFP_KERNEL);
0071         if (!n->port_name) {
0072             kfree(n);
0073             n = NULL;
0074             goto unlock;
0075         }
0076     }
0077     n->phys_addr = CEC_PHYS_ADDR_INVALID;
0078 
0079     mutex_init(&n->lock);
0080     kref_init(&n->kref);
0081     list_add_tail(&n->head, &cec_notifiers);
0082 unlock:
0083     mutex_unlock(&cec_notifiers_lock);
0084     return n;
0085 }
0086 
0087 static void cec_notifier_release(struct kref *kref)
0088 {
0089     struct cec_notifier *n =
0090         container_of(kref, struct cec_notifier, kref);
0091 
0092     list_del(&n->head);
0093     kfree(n->port_name);
0094     kfree(n);
0095 }
0096 
0097 static void cec_notifier_put(struct cec_notifier *n)
0098 {
0099     mutex_lock(&cec_notifiers_lock);
0100     kref_put(&n->kref, cec_notifier_release);
0101     mutex_unlock(&cec_notifiers_lock);
0102 }
0103 
0104 struct cec_notifier *
0105 cec_notifier_conn_register(struct device *hdmi_dev, const char *port_name,
0106                const struct cec_connector_info *conn_info)
0107 {
0108     struct cec_notifier *n = cec_notifier_get_conn(hdmi_dev, port_name);
0109 
0110     if (!n)
0111         return n;
0112 
0113     mutex_lock(&n->lock);
0114     n->phys_addr = CEC_PHYS_ADDR_INVALID;
0115     if (conn_info)
0116         n->conn_info = *conn_info;
0117     else
0118         memset(&n->conn_info, 0, sizeof(n->conn_info));
0119     if (n->cec_adap) {
0120         if (!n->cec_adap->adap_controls_phys_addr)
0121             cec_phys_addr_invalidate(n->cec_adap);
0122         cec_s_conn_info(n->cec_adap, conn_info);
0123     }
0124     mutex_unlock(&n->lock);
0125     return n;
0126 }
0127 EXPORT_SYMBOL_GPL(cec_notifier_conn_register);
0128 
0129 void cec_notifier_conn_unregister(struct cec_notifier *n)
0130 {
0131     if (!n)
0132         return;
0133 
0134     mutex_lock(&n->lock);
0135     memset(&n->conn_info, 0, sizeof(n->conn_info));
0136     n->phys_addr = CEC_PHYS_ADDR_INVALID;
0137     if (n->cec_adap) {
0138         if (!n->cec_adap->adap_controls_phys_addr)
0139             cec_phys_addr_invalidate(n->cec_adap);
0140         cec_s_conn_info(n->cec_adap, NULL);
0141     }
0142     mutex_unlock(&n->lock);
0143     cec_notifier_put(n);
0144 }
0145 EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister);
0146 
0147 struct cec_notifier *
0148 cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *port_name,
0149                    struct cec_adapter *adap)
0150 {
0151     struct cec_notifier *n;
0152 
0153     if (WARN_ON(!adap))
0154         return NULL;
0155 
0156     n = cec_notifier_get_conn(hdmi_dev, port_name);
0157     if (!n)
0158         return n;
0159 
0160     mutex_lock(&n->lock);
0161     n->cec_adap = adap;
0162     adap->conn_info = n->conn_info;
0163     adap->notifier = n;
0164     if (!adap->adap_controls_phys_addr)
0165         cec_s_phys_addr(adap, n->phys_addr, false);
0166     mutex_unlock(&n->lock);
0167     return n;
0168 }
0169 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_register);
0170 
0171 void cec_notifier_cec_adap_unregister(struct cec_notifier *n,
0172                       struct cec_adapter *adap)
0173 {
0174     if (!n)
0175         return;
0176 
0177     mutex_lock(&n->lock);
0178     adap->notifier = NULL;
0179     n->cec_adap = NULL;
0180     mutex_unlock(&n->lock);
0181     cec_notifier_put(n);
0182 }
0183 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_unregister);
0184 
0185 void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
0186 {
0187     if (n == NULL)
0188         return;
0189 
0190     mutex_lock(&n->lock);
0191     n->phys_addr = pa;
0192     if (n->cec_adap && !n->cec_adap->adap_controls_phys_addr)
0193         cec_s_phys_addr(n->cec_adap, n->phys_addr, false);
0194     mutex_unlock(&n->lock);
0195 }
0196 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
0197 
0198 void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
0199                       const struct edid *edid)
0200 {
0201     u16 pa = CEC_PHYS_ADDR_INVALID;
0202 
0203     if (n == NULL)
0204         return;
0205 
0206     if (edid && edid->extensions)
0207         pa = cec_get_edid_phys_addr((const u8 *)edid,
0208                 EDID_LENGTH * (edid->extensions + 1), NULL);
0209     cec_notifier_set_phys_addr(n, pa);
0210 }
0211 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
0212 
0213 struct device *cec_notifier_parse_hdmi_phandle(struct device *dev)
0214 {
0215     struct platform_device *hdmi_pdev;
0216     struct device *hdmi_dev = NULL;
0217     struct device_node *np;
0218 
0219     np = of_parse_phandle(dev->of_node, "hdmi-phandle", 0);
0220 
0221     if (!np) {
0222         dev_err(dev, "Failed to find HDMI node in device tree\n");
0223         return ERR_PTR(-ENODEV);
0224     }
0225 
0226     hdmi_pdev = of_find_device_by_node(np);
0227     if (hdmi_pdev)
0228         hdmi_dev = &hdmi_pdev->dev;
0229 #if IS_REACHABLE(CONFIG_I2C)
0230     if (!hdmi_dev) {
0231         struct i2c_client *hdmi_client = of_find_i2c_device_by_node(np);
0232 
0233         if (hdmi_client)
0234             hdmi_dev = &hdmi_client->dev;
0235     }
0236 #endif
0237     of_node_put(np);
0238     if (!hdmi_dev)
0239         return ERR_PTR(-EPROBE_DEFER);
0240 
0241     /*
0242      * Note that the device struct is only used as a key into the
0243      * cec_notifiers list, it is never actually accessed.
0244      * So we decrement the reference here so we don't leak
0245      * memory.
0246      */
0247     put_device(hdmi_dev);
0248     return hdmi_dev;
0249 }
0250 EXPORT_SYMBOL_GPL(cec_notifier_parse_hdmi_phandle);