media: cec-gpio: add notifier support
[sfrench/cifs-2.6.git] / drivers / media / platform / cec-gpio / cec-gpio.c
index 5b17d3a3189689eb46cfab22050544640fcf76e3..7be91e712c4ae20f18c1e66d63a952b4ff130ee0 100644 (file)
@@ -8,10 +8,12 @@
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/gpio/consumer.h>
+#include <media/cec-notifier.h>
 #include <media/cec-pin.h>
 
 struct cec_gpio {
        struct cec_adapter      *adap;
+       struct cec_notifier     *notifier;
        struct device           *dev;
 
        struct gpio_desc        *cec_gpio;
@@ -173,9 +175,17 @@ static const struct cec_pin_ops cec_gpio_pin_ops = {
 static int cec_gpio_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
+       struct device *hdmi_dev;
        struct cec_gpio *cec;
+       u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN;
        int ret;
 
+       hdmi_dev = cec_notifier_parse_hdmi_phandle(dev);
+       if (PTR_ERR(hdmi_dev) == -EPROBE_DEFER)
+               return PTR_ERR(hdmi_dev);
+       if (IS_ERR(hdmi_dev))
+               caps |= CEC_CAP_PHYS_ADDR;
+
        cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
        if (!cec)
                return -ENOMEM;
@@ -196,8 +206,7 @@ static int cec_gpio_probe(struct platform_device *pdev)
                return PTR_ERR(cec->v5_gpio);
 
        cec->adap = cec_pin_allocate_adapter(&cec_gpio_pin_ops,
-               cec, pdev->name, CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR |
-                                CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN);
+                                            cec, pdev->name, caps);
        if (IS_ERR(cec->adap))
                return PTR_ERR(cec->adap);
 
@@ -205,7 +214,7 @@ static int cec_gpio_probe(struct platform_device *pdev)
                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
                               cec->adap->name, cec);
        if (ret)
-               return ret;
+               goto del_adap;
 
        cec_gpio_disable_irq(cec->adap);
 
@@ -218,7 +227,7 @@ static int cec_gpio_probe(struct platform_device *pdev)
                        IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                        "hpd-gpio", cec);
                if (ret)
-                       return ret;
+                       goto del_adap;
        }
 
        if (cec->v5_gpio) {
@@ -230,23 +239,37 @@ static int cec_gpio_probe(struct platform_device *pdev)
                        IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
                        "v5-gpio", cec);
                if (ret)
-                       return ret;
+                       goto del_adap;
        }
 
-       ret = cec_register_adapter(cec->adap, &pdev->dev);
-       if (ret) {
-               cec_delete_adapter(cec->adap);
-               return ret;
+       if (!IS_ERR(hdmi_dev)) {
+               cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL,
+                                                              cec->adap);
+               if (!cec->notifier) {
+                       ret = -ENOMEM;
+                       goto del_adap;
+               }
        }
 
+       ret = cec_register_adapter(cec->adap, &pdev->dev);
+       if (ret)
+               goto unreg_notifier;
+
        platform_set_drvdata(pdev, cec);
        return 0;
+
+unreg_notifier:
+       cec_notifier_cec_adap_unregister(cec->notifier);
+del_adap:
+       cec_delete_adapter(cec->adap);
+       return ret;
 }
 
 static int cec_gpio_remove(struct platform_device *pdev)
 {
        struct cec_gpio *cec = platform_get_drvdata(pdev);
 
+       cec_notifier_cec_adap_unregister(cec->notifier);
        cec_unregister_adapter(cec->adap);
        return 0;
 }