Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * CEC driver for ChromeOS Embedded Controller
0004  *
0005  * Copyright (c) 2018 BayLibre, SAS
0006  * Author: Neil Armstrong <narmstrong@baylibre.com>
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/dmi.h>
0013 #include <linux/pci.h>
0014 #include <linux/cec.h>
0015 #include <linux/slab.h>
0016 #include <linux/interrupt.h>
0017 #include <linux/platform_data/cros_ec_commands.h>
0018 #include <linux/platform_data/cros_ec_proto.h>
0019 #include <media/cec.h>
0020 #include <media/cec-notifier.h>
0021 
0022 #define DRV_NAME    "cros-ec-cec"
0023 
0024 /**
0025  * struct cros_ec_cec - Driver data for EC CEC
0026  *
0027  * @cros_ec: Pointer to EC device
0028  * @notifier: Notifier info for responding to EC events
0029  * @adap: CEC adapter
0030  * @notify: CEC notifier pointer
0031  * @rx_msg: storage for a received message
0032  */
0033 struct cros_ec_cec {
0034     struct cros_ec_device *cros_ec;
0035     struct notifier_block notifier;
0036     struct cec_adapter *adap;
0037     struct cec_notifier *notify;
0038     struct cec_msg rx_msg;
0039 };
0040 
0041 static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
0042 {
0043     struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
0044     uint8_t *cec_message = cros_ec->event_data.data.cec_message;
0045     unsigned int len = cros_ec->event_size;
0046 
0047     cros_ec_cec->rx_msg.len = len;
0048     memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
0049 
0050     cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg);
0051 }
0052 
0053 static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
0054 {
0055     struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
0056     uint32_t events = cros_ec->event_data.data.cec_events;
0057 
0058     if (events & EC_MKBP_CEC_SEND_OK)
0059         cec_transmit_attempt_done(cros_ec_cec->adap,
0060                       CEC_TX_STATUS_OK);
0061 
0062     /* FW takes care of all retries, tell core to avoid more retries */
0063     if (events & EC_MKBP_CEC_SEND_FAILED)
0064         cec_transmit_attempt_done(cros_ec_cec->adap,
0065                       CEC_TX_STATUS_MAX_RETRIES |
0066                       CEC_TX_STATUS_NACK);
0067 }
0068 
0069 static int cros_ec_cec_event(struct notifier_block *nb,
0070                  unsigned long queued_during_suspend,
0071                  void *_notify)
0072 {
0073     struct cros_ec_cec *cros_ec_cec;
0074     struct cros_ec_device *cros_ec;
0075 
0076     cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
0077     cros_ec = cros_ec_cec->cros_ec;
0078 
0079     if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
0080         handle_cec_event(cros_ec_cec);
0081         return NOTIFY_OK;
0082     }
0083 
0084     if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
0085         handle_cec_message(cros_ec_cec);
0086         return NOTIFY_OK;
0087     }
0088 
0089     return NOTIFY_DONE;
0090 }
0091 
0092 static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
0093 {
0094     struct cros_ec_cec *cros_ec_cec = adap->priv;
0095     struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
0096     struct {
0097         struct cros_ec_command msg;
0098         struct ec_params_cec_set data;
0099     } __packed msg = {};
0100     int ret;
0101 
0102     msg.msg.command = EC_CMD_CEC_SET;
0103     msg.msg.outsize = sizeof(msg.data);
0104     msg.data.cmd = CEC_CMD_LOGICAL_ADDRESS;
0105     msg.data.val = logical_addr;
0106 
0107     ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
0108     if (ret < 0) {
0109         dev_err(cros_ec->dev,
0110             "error setting CEC logical address on EC: %d\n", ret);
0111         return ret;
0112     }
0113 
0114     return 0;
0115 }
0116 
0117 static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
0118                 u32 signal_free_time, struct cec_msg *cec_msg)
0119 {
0120     struct cros_ec_cec *cros_ec_cec = adap->priv;
0121     struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
0122     struct {
0123         struct cros_ec_command msg;
0124         struct ec_params_cec_write data;
0125     } __packed msg = {};
0126     int ret;
0127 
0128     msg.msg.command = EC_CMD_CEC_WRITE_MSG;
0129     msg.msg.outsize = cec_msg->len;
0130     memcpy(msg.data.msg, cec_msg->msg, cec_msg->len);
0131 
0132     ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
0133     if (ret < 0) {
0134         dev_err(cros_ec->dev,
0135             "error writing CEC msg on EC: %d\n", ret);
0136         return ret;
0137     }
0138 
0139     return 0;
0140 }
0141 
0142 static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
0143 {
0144     struct cros_ec_cec *cros_ec_cec = adap->priv;
0145     struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
0146     struct {
0147         struct cros_ec_command msg;
0148         struct ec_params_cec_set data;
0149     } __packed msg = {};
0150     int ret;
0151 
0152     msg.msg.command = EC_CMD_CEC_SET;
0153     msg.msg.outsize = sizeof(msg.data);
0154     msg.data.cmd = CEC_CMD_ENABLE;
0155     msg.data.val = enable;
0156 
0157     ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg);
0158     if (ret < 0) {
0159         dev_err(cros_ec->dev,
0160             "error %sabling CEC on EC: %d\n",
0161             (enable ? "en" : "dis"), ret);
0162         return ret;
0163     }
0164 
0165     return 0;
0166 }
0167 
0168 static const struct cec_adap_ops cros_ec_cec_ops = {
0169     .adap_enable = cros_ec_cec_adap_enable,
0170     .adap_log_addr = cros_ec_cec_set_log_addr,
0171     .adap_transmit = cros_ec_cec_transmit,
0172 };
0173 
0174 #ifdef CONFIG_PM_SLEEP
0175 static int cros_ec_cec_suspend(struct device *dev)
0176 {
0177     struct platform_device *pdev = to_platform_device(dev);
0178     struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
0179 
0180     if (device_may_wakeup(dev))
0181         enable_irq_wake(cros_ec_cec->cros_ec->irq);
0182 
0183     return 0;
0184 }
0185 
0186 static int cros_ec_cec_resume(struct device *dev)
0187 {
0188     struct platform_device *pdev = to_platform_device(dev);
0189     struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
0190 
0191     if (device_may_wakeup(dev))
0192         disable_irq_wake(cros_ec_cec->cros_ec->irq);
0193 
0194     return 0;
0195 }
0196 #endif
0197 
0198 static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
0199     cros_ec_cec_suspend, cros_ec_cec_resume);
0200 
0201 #if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
0202 
0203 /*
0204  * The Firmware only handles a single CEC interface tied to a single HDMI
0205  * connector we specify along with the DRM device name handling the HDMI output
0206  */
0207 
0208 struct cec_dmi_match {
0209     const char *sys_vendor;
0210     const char *product_name;
0211     const char *devname;
0212     const char *conn;
0213 };
0214 
0215 static const struct cec_dmi_match cec_dmi_match_table[] = {
0216     /* Google Fizz */
0217     { "Google", "Fizz", "0000:00:02.0", "Port B" },
0218     /* Google Brask */
0219     { "Google", "Brask", "0000:00:02.0", "Port B" },
0220     /* Google Moli */
0221     { "Google", "Moli", "0000:00:02.0", "Port B" },
0222     /* Google Kinox */
0223     { "Google", "Kinox", "0000:00:02.0", "Port B" },
0224 };
0225 
0226 static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
0227                         const char **conn)
0228 {
0229     int i;
0230 
0231     for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
0232         const struct cec_dmi_match *m = &cec_dmi_match_table[i];
0233 
0234         if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
0235             dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
0236             struct device *d;
0237 
0238             /* Find the device, bail out if not yet registered */
0239             d = bus_find_device_by_name(&pci_bus_type, NULL,
0240                             m->devname);
0241             if (!d)
0242                 return ERR_PTR(-EPROBE_DEFER);
0243             put_device(d);
0244             *conn = m->conn;
0245             return d;
0246         }
0247     }
0248 
0249     /* Hardware support must be added in the cec_dmi_match_table */
0250     dev_warn(dev, "CEC notifier not configured for this hardware\n");
0251 
0252     return ERR_PTR(-ENODEV);
0253 }
0254 
0255 #else
0256 
0257 static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
0258                         const char **conn)
0259 {
0260     return ERR_PTR(-ENODEV);
0261 }
0262 
0263 #endif
0264 
0265 static int cros_ec_cec_probe(struct platform_device *pdev)
0266 {
0267     struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
0268     struct cros_ec_device *cros_ec = ec_dev->ec_dev;
0269     struct cros_ec_cec *cros_ec_cec;
0270     struct device *hdmi_dev;
0271     const char *conn = NULL;
0272     int ret;
0273 
0274     hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conn);
0275     if (IS_ERR(hdmi_dev))
0276         return PTR_ERR(hdmi_dev);
0277 
0278     cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
0279                    GFP_KERNEL);
0280     if (!cros_ec_cec)
0281         return -ENOMEM;
0282 
0283     platform_set_drvdata(pdev, cros_ec_cec);
0284     cros_ec_cec->cros_ec = cros_ec;
0285 
0286     device_init_wakeup(&pdev->dev, 1);
0287 
0288     cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec,
0289                          DRV_NAME,
0290                          CEC_CAP_DEFAULTS |
0291                          CEC_CAP_CONNECTOR_INFO, 1);
0292     if (IS_ERR(cros_ec_cec->adap))
0293         return PTR_ERR(cros_ec_cec->adap);
0294 
0295     cros_ec_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, conn,
0296                                  cros_ec_cec->adap);
0297     if (!cros_ec_cec->notify) {
0298         ret = -ENOMEM;
0299         goto out_probe_adapter;
0300     }
0301 
0302     /* Get CEC events from the EC. */
0303     cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
0304     ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
0305                            &cros_ec_cec->notifier);
0306     if (ret) {
0307         dev_err(&pdev->dev, "failed to register notifier\n");
0308         goto out_probe_notify;
0309     }
0310 
0311     ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev);
0312     if (ret < 0)
0313         goto out_probe_notify;
0314 
0315     return 0;
0316 
0317 out_probe_notify:
0318     cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
0319                      cros_ec_cec->adap);
0320 out_probe_adapter:
0321     cec_delete_adapter(cros_ec_cec->adap);
0322     return ret;
0323 }
0324 
0325 static int cros_ec_cec_remove(struct platform_device *pdev)
0326 {
0327     struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
0328     struct device *dev = &pdev->dev;
0329     int ret;
0330 
0331     ret = blocking_notifier_chain_unregister(
0332             &cros_ec_cec->cros_ec->event_notifier,
0333             &cros_ec_cec->notifier);
0334 
0335     if (ret) {
0336         dev_err(dev, "failed to unregister notifier\n");
0337         return ret;
0338     }
0339 
0340     cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
0341                      cros_ec_cec->adap);
0342     cec_unregister_adapter(cros_ec_cec->adap);
0343 
0344     return 0;
0345 }
0346 
0347 static struct platform_driver cros_ec_cec_driver = {
0348     .probe = cros_ec_cec_probe,
0349     .remove  = cros_ec_cec_remove,
0350     .driver = {
0351         .name = DRV_NAME,
0352         .pm = &cros_ec_cec_pm_ops,
0353     },
0354 };
0355 
0356 module_platform_driver(cros_ec_cec_driver);
0357 
0358 MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
0359 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
0360 MODULE_LICENSE("GPL");
0361 MODULE_ALIAS("platform:" DRV_NAME);