cec-notifier.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. #include <linux/export.h>
  9. #include <linux/string.h>
  10. #include <linux/slab.h>
  11. #include <linux/list.h>
  12. #include <linux/kref.h>
  13. #include <media/cec.h>
  14. #include <media/cec-notifier.h>
  15. #include <drm/drm_edid.h>
  16. struct cec_notifier {
  17. struct mutex lock;
  18. struct list_head head;
  19. struct kref kref;
  20. struct device *dev;
  21. const char *conn;
  22. struct cec_adapter *cec_adap;
  23. void (*callback)(struct cec_adapter *adap, u16 pa);
  24. u16 phys_addr;
  25. };
  26. static LIST_HEAD(cec_notifiers);
  27. static DEFINE_MUTEX(cec_notifiers_lock);
  28. struct cec_notifier *cec_notifier_get_conn(struct device *dev, const char *conn)
  29. {
  30. struct cec_notifier *n;
  31. mutex_lock(&cec_notifiers_lock);
  32. list_for_each_entry(n, &cec_notifiers, head) {
  33. if (n->dev == dev &&
  34. (!conn || !strcmp(n->conn, conn))) {
  35. kref_get(&n->kref);
  36. mutex_unlock(&cec_notifiers_lock);
  37. return n;
  38. }
  39. }
  40. n = kzalloc(sizeof(*n), GFP_KERNEL);
  41. if (!n)
  42. goto unlock;
  43. n->dev = dev;
  44. if (conn)
  45. n->conn = kstrdup(conn, GFP_KERNEL);
  46. n->phys_addr = CEC_PHYS_ADDR_INVALID;
  47. mutex_init(&n->lock);
  48. kref_init(&n->kref);
  49. list_add_tail(&n->head, &cec_notifiers);
  50. unlock:
  51. mutex_unlock(&cec_notifiers_lock);
  52. return n;
  53. }
  54. EXPORT_SYMBOL_GPL(cec_notifier_get_conn);
  55. static void cec_notifier_release(struct kref *kref)
  56. {
  57. struct cec_notifier *n =
  58. container_of(kref, struct cec_notifier, kref);
  59. list_del(&n->head);
  60. kfree(n->conn);
  61. kfree(n);
  62. }
  63. void cec_notifier_put(struct cec_notifier *n)
  64. {
  65. mutex_lock(&cec_notifiers_lock);
  66. kref_put(&n->kref, cec_notifier_release);
  67. mutex_unlock(&cec_notifiers_lock);
  68. }
  69. EXPORT_SYMBOL_GPL(cec_notifier_put);
  70. void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
  71. {
  72. if (n == NULL)
  73. return;
  74. mutex_lock(&n->lock);
  75. n->phys_addr = pa;
  76. if (n->callback)
  77. n->callback(n->cec_adap, n->phys_addr);
  78. mutex_unlock(&n->lock);
  79. }
  80. EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
  81. void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
  82. const struct edid *edid)
  83. {
  84. u16 pa = CEC_PHYS_ADDR_INVALID;
  85. if (n == NULL)
  86. return;
  87. if (edid && edid->extensions)
  88. pa = cec_get_edid_phys_addr((const u8 *)edid,
  89. EDID_LENGTH * (edid->extensions + 1), NULL);
  90. cec_notifier_set_phys_addr(n, pa);
  91. }
  92. EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
  93. void cec_notifier_register(struct cec_notifier *n,
  94. struct cec_adapter *adap,
  95. void (*callback)(struct cec_adapter *adap, u16 pa))
  96. {
  97. kref_get(&n->kref);
  98. mutex_lock(&n->lock);
  99. n->cec_adap = adap;
  100. n->callback = callback;
  101. n->callback(adap, n->phys_addr);
  102. mutex_unlock(&n->lock);
  103. }
  104. EXPORT_SYMBOL_GPL(cec_notifier_register);
  105. void cec_notifier_unregister(struct cec_notifier *n)
  106. {
  107. mutex_lock(&n->lock);
  108. n->callback = NULL;
  109. n->cec_adap->notifier = NULL;
  110. n->cec_adap = NULL;
  111. mutex_unlock(&n->lock);
  112. cec_notifier_put(n);
  113. }
  114. EXPORT_SYMBOL_GPL(cec_notifier_unregister);