diff options
| -rw-r--r-- | Documentation/devicetree/bindings/display/msm/cec.txt | 2 | ||||
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi | 34 | ||||
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8998-sde.dtsi | 4 | ||||
| -rw-r--r-- | arch/arm64/configs/msmcortex_mediabox-perf_defconfig | 1 | ||||
| -rw-r--r-- | arch/arm64/configs/msmcortex_mediabox_defconfig | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c | 47 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h | 3 | ||||
| -rw-r--r-- | drivers/media/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/media/Makefile | 4 | ||||
| -rw-r--r-- | drivers/media/cec-notifier.c | 129 | ||||
| -rw-r--r-- | drivers/media/cec/cec-core.c | 22 | ||||
| -rw-r--r-- | drivers/media/platform/msm/sde/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c | 40 | ||||
| -rw-r--r-- | include/media/cec-notifier.h | 111 | ||||
| -rw-r--r-- | include/media/cec.h | 10 |
15 files changed, 403 insertions, 10 deletions
diff --git a/Documentation/devicetree/bindings/display/msm/cec.txt b/Documentation/devicetree/bindings/display/msm/cec.txt index ba51b0d1dd18..e5282f1f5e9e 100644 --- a/Documentation/devicetree/bindings/display/msm/cec.txt +++ b/Documentation/devicetree/bindings/display/msm/cec.txt @@ -5,6 +5,7 @@ various audiovisual products in a user environment. Required properties: - compatible: Must be "qcom,hdmi-cec". +- qcom,hdmi-dev: Phandle for the hdmi device node. - interrupt-parent: Must be the hdmi interrupt controller. - interrupts: Interrupt associated with cec. - reg: Physical base address and length of the controller's registers. @@ -41,6 +42,7 @@ Example: sde_hdmi_cec: qcom,hdmi-cec@c9a0000 { compatible = "qcom,hdmi-cec"; label = "sde_hdmi_cec"; + qcom,hdmi-dev = <&sde_hdmi>; interrupt-parent = <&sde_hdmi_tx>; interrupts = <1 0>; diff --git a/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi index 6098a96db206..6e19c77995c1 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde-display.dtsi @@ -34,6 +34,40 @@ qcom,msm_ext_disp = <&msm_ext_disp>; }; + sde_hdmi_cec: qcom,hdmi-cec@c9a0000 { + compatible = "qcom,hdmi-cec"; + label = "sde_hdmi_cec"; + qcom,hdmi-dev = <&sde_hdmi>; + interrupt-parent = <&sde_hdmi_tx>; + interrupts = <1 0>; + + reg = <0xc9a0000 0x50c>; + reg-names = "hdmi_cec"; + + clocks = <&clock_mmss clk_mmss_mnoc_ahb_clk>, + <&clock_mmss clk_mmss_mdss_ahb_clk>, + <&clock_mmss clk_mmss_mdss_hdmi_clk>; + clock-names = "cec_mnoc_clk", "cec_iface_clk", "cec_core_clk"; + + pinctrl-names = "cec_active", "cec_sleep"; + pinctrl-0 = <&mdss_hdmi_cec_active>; + pinctrl-1 = <&mdss_hdmi_cec_suspend>; + + cec-gdsc-supply = <&gdsc_mdss>; + qcom,platform-supply-entries { + #address-cells = <1>; + #size-cells = <0>; + + qcom,platform-supply-entry@0 { + reg = <0>; + qcom,supply-name = "cec-gdsc"; + qcom,supply-min-voltage = <0>; + qcom,supply-max-voltage = <0>; + qcom,supply-enable-load = <0>; + qcom,supply-disable-load = <0>; + }; + }; + }; }; &sde_kms { diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi index f7dbda515643..795635d8d13d 100644 --- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi @@ -175,6 +175,8 @@ reg-names = "core_physical", "qfprom_physical", "hdcp_physical"; interrupt-parent = <&sde_kms>; interrupts = <8 0>; + interrupt-controller; + #interrupt-cells = <1>; qcom,hdmi-tx-ddc-clk-gpio = <&tlmm 32 0>; qcom,hdmi-tx-ddc-data-gpio = <&tlmm 33 0>; qcom,hdmi-tx-hpd-gpio = <&tlmm 34 0>; @@ -182,11 +184,9 @@ pinctrl-names = "default", "sleep"; pinctrl-0 = <&mdss_hdmi_hpd_active &mdss_hdmi_ddc_active - &mdss_hdmi_cec_active &mdss_hdmi_5v_active>; pinctrl-1 = <&mdss_hdmi_hpd_suspend &mdss_hdmi_ddc_suspend - &mdss_hdmi_cec_suspend &mdss_hdmi_5v_suspend>; hpd-gdsc-supply = <&gdsc_mdss>; qcom,supply-names = "hpd-gdsc"; diff --git a/arch/arm64/configs/msmcortex_mediabox-perf_defconfig b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig index 953d700c20cc..b359c78a0d29 100644 --- a/arch/arm64/configs/msmcortex_mediabox-perf_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig @@ -405,6 +405,7 @@ CONFIG_MSM_VIDC_VMEM=y CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_MSM_SDE_HDMI_CEC=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index d81c10d11318..994b0f230968 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -407,6 +407,7 @@ CONFIG_MSM_VIDC_VMEM=y CONFIG_MSM_VIDC_GOVERNORS=y CONFIG_MSM_SDE_ROTATOR=y CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y +CONFIG_MSM_SDE_HDMI_CEC=y CONFIG_DVB_MPQ=m CONFIG_DVB_MPQ_DEMUX=m CONFIG_DVB_MPQ_MEDIA_BOX_DEMUX=y diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 4c70472bd338..acc417737558 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -907,6 +907,17 @@ static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) } } +static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display) +{ + struct edid *edid = display->edid_ctrl->edid; + + if (edid) + cec_notifier_set_phys_addr_from_edid(display->notifier, edid); + else + cec_notifier_set_phys_addr(display->notifier, + CEC_PHYS_ADDR_INVALID); +} + static void _sde_hdmi_hotplug_work(struct work_struct *work) { struct sde_hdmi *sde_hdmi = @@ -936,6 +947,7 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) sde_free_edid((void **)&sde_hdmi->edid_ctrl); drm_helper_hpd_irq_event(connector->dev); + _sde_hdmi_cec_update_phys_addr(sde_hdmi); } static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) @@ -1673,10 +1685,28 @@ int sde_hdmi_dev_deinit(struct sde_hdmi *display) SDE_ERROR("Invalid params\n"); return -EINVAL; } + return 0; +} + +static int _sde_hdmi_cec_init(struct sde_hdmi *display) +{ + struct platform_device *pdev = display->pdev; + + display->notifier = cec_notifier_get(&pdev->dev); + if (!display->notifier) { + SDE_ERROR("CEC notifier get failed\n"); + return -ENOMEM; + } return 0; } +static void _sde_hdmi_cec_deinit(struct sde_hdmi *display) +{ + cec_notifier_set_phys_addr(display->notifier, CEC_PHYS_ADDR_INVALID); + cec_notifier_put(display->notifier); +} + static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) { int rc = 0; @@ -1715,7 +1745,14 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) if (rc) { SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", display->name, rc); - goto error; + goto ext_error; + } + + rc = _sde_hdmi_cec_init(display); + if (rc) { + SDE_ERROR("[%s]CEC init failed, rc=%d\n", + display->name, rc); + goto ext_error; } display->edid_ctrl = sde_edid_init(); @@ -1723,7 +1760,7 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) SDE_ERROR("[%s]sde edid init failed\n", display->name); rc = -ENOMEM; - goto error; + goto cec_error; } display_ctrl = &display->ctrl; @@ -1732,7 +1769,10 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) mutex_unlock(&display->display_lock); return rc; -error: + +cec_error: + (void)_sde_hdmi_cec_deinit(display); +ext_error: (void)_sde_hdmi_debugfs_deinit(display); debug_error: mutex_unlock(&display->display_lock); @@ -1758,6 +1798,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master, mutex_lock(&display->display_lock); (void)_sde_hdmi_debugfs_deinit(display); (void)sde_edid_deinit((void **)&display->edid_ctrl); + (void)_sde_hdmi_cec_deinit(display); display->drm_dev = NULL; mutex_unlock(&display->display_lock); } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 54506da4f9b0..dff245dec764 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -23,6 +23,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <media/cec-notifier.h> #include "hdmi.h" #include "sde_edid_parser.h" @@ -87,6 +88,7 @@ struct sde_hdmi_ctrl { * @codec_ready: If audio codec is ready. * @client_notify_pending: If there is client notification pending. * @irq_domain: IRQ domain structure. + * @notifier: CEC notifider to convey physical address information. * @root: Debug fs root entry. */ struct sde_hdmi { @@ -116,6 +118,7 @@ struct sde_hdmi { bool client_notify_pending; struct irq_domain *irq_domain; + struct cec_notifier *notifier; /* DEBUG FS */ struct dentry *root; diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 0b219a81e8a2..c1b999c6cff2 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -99,6 +99,10 @@ config MEDIA_CEC_DEBUG config MEDIA_CEC_EDID bool +config MEDIA_CEC_NOTIFIER + bool + select MEDIA_CEC_EDID + # # Media controller # Selectable only for webcam/grabbers, as other drivers don't use it diff --git a/drivers/media/Makefile b/drivers/media/Makefile index ba516dcbc6aa..6c7801919b98 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -10,6 +10,10 @@ ifeq ($(CONFIG_MEDIA_CEC_SUPPORT),y) obj-$(CONFIG_MEDIA_SUPPORT) += cec/ endif +ifeq ($(CONFIG_MEDIA_CEC_NOTIFIER),y) + obj-$(CONFIG_MEDIA_SUPPORT) += cec-notifier.o +endif + media-objs := media-device.o media-devnode.o media-entity.o # diff --git a/drivers/media/cec-notifier.c b/drivers/media/cec-notifier.c new file mode 100644 index 000000000000..5f5209a73665 --- /dev/null +++ b/drivers/media/cec-notifier.c @@ -0,0 +1,129 @@ +/* + * cec-notifier.c - notify CEC drivers of physical address changes + * + * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk> + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/export.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/kref.h> + +#include <media/cec-notifier.h> +#include <drm/drm_edid.h> + +struct cec_notifier { + struct mutex lock; + struct list_head head; + struct kref kref; + struct device *dev; + struct cec_adapter *cec_adap; + void (*callback)(struct cec_adapter *adap, u16 pa); + + u16 phys_addr; +}; + +static LIST_HEAD(cec_notifiers); +static DEFINE_MUTEX(cec_notifiers_lock); + +struct cec_notifier *cec_notifier_get(struct device *dev) +{ + struct cec_notifier *n; + + mutex_lock(&cec_notifiers_lock); + list_for_each_entry(n, &cec_notifiers, head) { + if (n->dev == dev) { + kref_get(&n->kref); + mutex_unlock(&cec_notifiers_lock); + return n; + } + } + n = kzalloc(sizeof(*n), GFP_KERNEL); + if (!n) + goto unlock; + n->dev = dev; + n->phys_addr = CEC_PHYS_ADDR_INVALID; + mutex_init(&n->lock); + kref_init(&n->kref); + list_add_tail(&n->head, &cec_notifiers); +unlock: + mutex_unlock(&cec_notifiers_lock); + return n; +} +EXPORT_SYMBOL_GPL(cec_notifier_get); + +static void cec_notifier_release(struct kref *kref) +{ + struct cec_notifier *n = + container_of(kref, struct cec_notifier, kref); + + list_del(&n->head); + kfree(n); +} + +void cec_notifier_put(struct cec_notifier *n) +{ + mutex_lock(&cec_notifiers_lock); + kref_put(&n->kref, cec_notifier_release); + mutex_unlock(&cec_notifiers_lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_put); + +void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) +{ + mutex_lock(&n->lock); + n->phys_addr = pa; + if (n->callback) + n->callback(n->cec_adap, n->phys_addr); + mutex_unlock(&n->lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr); + +void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, + const struct edid *edid) +{ + u16 pa = CEC_PHYS_ADDR_INVALID; + + if (edid && edid->extensions) + pa = cec_get_edid_phys_addr((const u8 *)edid, + EDID_LENGTH * (edid->extensions + 1), NULL); + cec_notifier_set_phys_addr(n, pa); +} +EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid); + +void cec_notifier_register(struct cec_notifier *n, + struct cec_adapter *adap, + void (*callback)(struct cec_adapter *adap, u16 pa)) +{ + kref_get(&n->kref); + mutex_lock(&n->lock); + n->cec_adap = adap; + n->callback = callback; + n->callback(adap, n->phys_addr); + mutex_unlock(&n->lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_register); + +void cec_notifier_unregister(struct cec_notifier *n) +{ + mutex_lock(&n->lock); + n->callback = NULL; + mutex_unlock(&n->lock); + cec_notifier_put(n); +} +EXPORT_SYMBOL_GPL(cec_notifier_unregister); diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index aca3ab83a8a1..61adc28ec8ec 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -195,6 +195,24 @@ static void cec_devnode_unregister(struct cec_devnode *devnode) put_device(&devnode->dev); } +#ifdef CONFIG_MEDIA_CEC_NOTIFIER +static void cec_cec_notify(struct cec_adapter *adap, u16 pa) +{ + cec_s_phys_addr(adap, pa, false); +} + +void cec_register_cec_notifier(struct cec_adapter *adap, + struct cec_notifier *notifier) +{ + if (WARN_ON(!adap->devnode.registered)) + return; + + adap->notifier = notifier; + cec_notifier_register(adap->notifier, adap, cec_cec_notify); +} +EXPORT_SYMBOL_GPL(cec_register_cec_notifier); +#endif + struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, void *priv, const char *name, u32 caps, u8 available_las) @@ -344,6 +362,10 @@ void cec_unregister_adapter(struct cec_adapter *adap) adap->rc = NULL; #endif debugfs_remove_recursive(adap->cec_dir); +#ifdef CONFIG_MEDIA_CEC_NOTIFIER + if (adap->notifier) + cec_notifier_unregister(adap->notifier); +#endif cec_devnode_unregister(&adap->devnode); } EXPORT_SYMBOL_GPL(cec_unregister_adapter); diff --git a/drivers/media/platform/msm/sde/Kconfig b/drivers/media/platform/msm/sde/Kconfig index c0e73813ab06..d2c2e90f9de9 100644 --- a/drivers/media/platform/msm/sde/Kconfig +++ b/drivers/media/platform/msm/sde/Kconfig @@ -21,6 +21,7 @@ config MSM_SDE_HDMI_CEC depends on DRM_SDE_HDMI select MEDIA_CEC_SUPPORT select MEDIA_CEC_EDID + select MEDIA_CEC_NOTIFIER ---help--- The HDMI CEC driver provides support to enable HDMI CEC features which allows various audiovisual products to communicate using HDMI diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c index 0fed19a01d5b..b3798e8a9d24 100644 --- a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c +++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c @@ -18,11 +18,14 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/cec.h> #include <media/cec.h> +#include <media/cec-edid.h> +#include <media/cec-notifier.h> #include "sde_hdmi_cec_util.h" @@ -63,6 +66,7 @@ struct sde_hdmi_cec { struct cec_hw_resource hw_res; int irq; enum cec_irq_status irq_status; + struct cec_notifier *notifier; }; static int sde_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) @@ -287,6 +291,8 @@ static int sde_hdmi_cec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct sde_hdmi_cec *cec; + struct device_node *np; + struct platform_device *hdmi_dev; int ret; cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); @@ -295,6 +301,15 @@ static int sde_hdmi_cec_probe(struct platform_device *pdev) cec->dev = dev; + np = of_parse_phandle(pdev->dev.of_node, "qcom,hdmi-dev", 0); + if (!np) { + pr_err("failed to find hdmi node in device tree\n"); + return -ENODEV; + } + hdmi_dev = of_find_device_by_node(np); + if (hdmi_dev == NULL) + return -EPROBE_DEFER; + cec->irq = of_irq_get(dev->of_node, 0); if (cec->irq < 0) { pr_err("failed to get irq\n"); @@ -314,24 +329,38 @@ static int sde_hdmi_cec_probe(struct platform_device *pdev) cec->adap = cec_allocate_adapter(&sde_hdmi_cec_adap_ops, cec, CEC_NAME, CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | - CEC_CAP_PHYS_ADDR | CEC_CAP_TRANSMIT, 1); + CEC_CAP_TRANSMIT, 1); ret = PTR_ERR_OR_ZERO(cec->adap); if (ret) return ret; ret = cec_register_adapter(cec->adap, &pdev->dev); - if (ret) { - cec_delete_adapter(cec->adap); - return ret; + if (ret) + goto err_del_adap; + + cec->notifier = cec_notifier_get(&hdmi_dev->dev); + if (!cec->notifier) { + pr_err("failed to get cec notifier\n"); + goto err_del_adap; } platform_set_drvdata(pdev, cec); pm_runtime_enable(dev); + /* + * cec_register_cec_notifier has to be later than pm_runtime_enable + * because it calls adap_enable. + */ + cec_register_cec_notifier(cec->adap, cec->notifier); + pr_debug("probe done\n"); - return 0; + return ret; + +err_del_adap: + cec_delete_adapter(cec->adap); + return ret; } static int sde_hdmi_cec_remove(struct platform_device *pdev) @@ -341,6 +370,7 @@ static int sde_hdmi_cec_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); cec_unregister_adapter(cec->adap); + cec_notifier_put(cec->notifier); devm_free_irq(&pdev->dev, cec->irq, cec); sde_hdmi_cec_deinit_resource(pdev, &cec->hw_res); diff --git a/include/media/cec-notifier.h b/include/media/cec-notifier.h new file mode 100644 index 000000000000..035712e0993d --- /dev/null +++ b/include/media/cec-notifier.h @@ -0,0 +1,111 @@ +/* + * cec-notifier.h - notify CEC drivers of physical address changes + * + * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk> + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LINUX_CEC_NOTIFIER_H +#define LINUX_CEC_NOTIFIER_H + +#include <linux/types.h> +#include <media/cec-edid.h> + +struct device; +struct edid; +struct cec_adapter; +struct cec_notifier; + +#ifdef CONFIG_MEDIA_CEC_NOTIFIER + +/** + * cec_notifier_get - find or create a new cec_notifier for the given device. + * @dev: device that sends the events. + * + * If a notifier for device @dev already exists, then increase the refcount + * and return that notifier. + * + * If it doesn't exist, then allocate a new notifier struct and return a + * pointer to that new struct. + * + * Return NULL if the memory could not be allocated. + */ +struct cec_notifier *cec_notifier_get(struct device *dev); + +/** + * cec_notifier_put - decrease refcount and delete when the refcount reaches 0. + * @n: notifier + */ +void cec_notifier_put(struct cec_notifier *n); + +/** + * cec_notifier_set_phys_addr - set a new physical address. + * @n: the CEC notifier + * @pa: the CEC physical address + * + * Set a new CEC physical address. + */ +void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa); + +/** + * cec_notifier_set_phys_addr_from_edid - set parse the PA from the EDID. + * @n: the CEC notifier + * @edid: the struct edid pointer + * + * Parses the EDID to obtain the new CEC physical address and set it. + */ +void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, + const struct edid *edid); + +/** + * cec_notifier_register - register a callback with the notifier + * @n: the CEC notifier + * @adap: the CEC adapter, passed as argument to the callback function + * @callback: the callback function + */ +void cec_notifier_register(struct cec_notifier *n, + struct cec_adapter *adap, + void (*callback)(struct cec_adapter *adap, u16 pa)); + +/** + * cec_notifier_unregister - unregister the callback from the notifier. + * @n: the CEC notifier + */ +void cec_notifier_unregister(struct cec_notifier *n); + +#else +static inline struct cec_notifier *cec_notifier_get(struct device *dev) +{ + /* A non-NULL pointer is expected on success */ + return (struct cec_notifier *)0xdeadfeed; +} + +static inline void cec_notifier_put(struct cec_notifier *n) +{ +} + +static inline void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) +{ +} + +static inline void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, + const struct edid *edid) +{ +} + +#endif + +#endif diff --git a/include/media/cec.h b/include/media/cec.h index 96a0aa770d61..307f5dcaf034 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -30,6 +30,7 @@ #include <linux/cec-funcs.h> #include <media/rc-core.h> #include <media/cec-edid.h> +#include <media/cec-notifier.h> /** * struct cec_devnode - cec device node @@ -173,6 +174,10 @@ struct cec_adapter { bool passthrough; struct cec_log_addrs log_addrs; +#ifdef CONFIG_MEDIA_CEC_NOTIFIER + struct cec_notifier *notifier; +#endif + struct dentry *cec_dir; struct dentry *status_file; @@ -213,6 +218,11 @@ void cec_transmit_done(struct cec_adapter *adap, u8 status, u8 arb_lost_cnt, u8 nack_cnt, u8 low_drive_cnt, u8 error_cnt); void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg); +#ifdef CONFIG_MEDIA_CEC_NOTIFIER +void cec_register_cec_notifier(struct cec_adapter *adap, + struct cec_notifier *notifier); +#endif + #else static inline int cec_register_adapter(struct cec_adapter *adap, |
