Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp1864361imm; Thu, 24 May 2018 01:57:25 -0700 (PDT) X-Google-Smtp-Source: AB8JxZodc7wVPJFuhjFGSqb/7d8fD2ls5vu2c79vhaxQr3DAdX3an90ghHqLWj4BBjZ0ZaH2PG7l X-Received: by 2002:a62:d508:: with SMTP id d8-v6mr6243904pfg.234.1527152245588; Thu, 24 May 2018 01:57:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527152245; cv=none; d=google.com; s=arc-20160816; b=uA/Q1wsGvVSknsr1KFswll1f5baeR4vORJFj0u7bpiNp+VKYcls7RmmUX2rXFWBQn2 ctzTtKXUxfpcuZstEM75UhdvDZ8K6IWiv1svbkEU3GX4vVqK2PDnYkB2h75p46eNcG9Z zfwCnmweI7mWYGTlRHI7hc94SrHYUBDtn78mp5HMzTju35DKWFoy3Y2S0edvU1rY4w16 +Yrhun9/YJ+53imOBzsuIl7uAFqU50s9JN/2gQrnw4NOQqs68aBxe0ihth/mEkN7Wq4A FoYtR1ESkC1vyctH0nWOy/H0nWKB+gWgO6/hqX2OvrNONZto1vXo+ILc9dYn5Y7YnmiN P3Qg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=qtUBjjy8sTng9+clojH2Oz9L6rVpXKhW8Fh0ZjMcA6k=; b=tTUYzVPknk3IFF0c7FCB7rrCc8qaUBKYSYWK3STKo+WGlZAuytQcUnA6jvQjc8ayTB 3LP9JMtGJyzf+ZknpcIepZ2kVSuKfbaiGnaIPugN3iurH2l7wzJ2+UyCQz1PoAjWOZcd 4N0dsAxLWi8nzbF2//SwAsad3h90Y4kyx3PHhiJ+8Ke8oYMUfiH5IGi407mzFI31aMRA 7DfOVZU8e90JHLUgn5bSQaG/I6QlXrvTxurM4TQtSpP+MVRvrLxpYOzYiKMQAhVSTtv8 NKIxxsxPxdaOVO+2LLQzYg8b41B4B3B45feYVhjtHxZkv2Cn0zxdsXJAzztW244KLdVy JlMQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=s04DnA+b; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id w3-v6si16621744pge.89.2018.05.24.01.57.10; Thu, 24 May 2018 01:57:25 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=s04DnA+b; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965607AbeEXIz5 (ORCPT + 99 others); Thu, 24 May 2018 04:55:57 -0400 Received: from mail-wr0-f196.google.com ([209.85.128.196]:38532 "EHLO mail-wr0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965608AbeEXIzQ (ORCPT ); Thu, 24 May 2018 04:55:16 -0400 Received: by mail-wr0-f196.google.com with SMTP id 94-v6so1613325wrf.5 for ; Thu, 24 May 2018 01:55:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=qtUBjjy8sTng9+clojH2Oz9L6rVpXKhW8Fh0ZjMcA6k=; b=s04DnA+bxsY9fICPb59YEad8JO6C4Ulw5ursO8imD5GboggWdjAH5IlKKh00EBpeKR w8U4Ur+Ow4O//I7gReLIKyZeLSbYhtl9fkqOwT6IpCC4MEPiU476TowkaAZUK/kVmL4N ggvLMoGQAduIdjIDUteYQ2cLfyUh2SwBuPkn5lq76h0dRBnd74b3pt6Hc7MVkPmcCBDT nrMqixoEB6Aov1suqLx6bHrR8XSXmGDc2t6KMtg7CRM1xAj58bzshvS4TvBNTnDbMzqu dP9IpR9CREI9EJYMiUe9s0tUjUvvSRGEcpBTDSpGGORWoPjNSJ0qu8QmogBypl5eTAk0 oazg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=qtUBjjy8sTng9+clojH2Oz9L6rVpXKhW8Fh0ZjMcA6k=; b=Awaq70dkVhSRK7YbXxGYbcs30eCg+dAOigGh/6BSqJmeX2ejJy079uF1Iku3JOK7So A14YreZJhrbxe1oDctike5a/63RAhBSwqZLy6Md95876oCxT/DegxWnoHDOJeY6DIrT1 U8DFDrPKnOY+4zfvBI/QrnjDbZDOGgL5wnuv0ycQQiWfFXPmsRB/miEtkC8LWx5UFkLd eBcjhh6I5js2NDrH/eJVWUdY92R2CJqn8YgK/5/USzhv2HvYGQ/KJosGLFGZF8bEuNou +363HUtyIceqSx9cwfgisaBrN5Z8hhh3Byx6/RWzlTArOpjAz0GkSyOzeGjZpyA7jPuo f+Bw== X-Gm-Message-State: ALKqPweeEMH7TDaL24NS6oT8B7WfNeYYKAe4iNUrpg5mzIz+1q5jLzuA 4rxU+0n8q15yiZyTCP3q1kx3kg== X-Received: by 2002:adf:8756:: with SMTP id 22-v6mr4764210wrz.117.1527152115074; Thu, 24 May 2018 01:55:15 -0700 (PDT) Received: from bender.baylibre.local ([90.63.244.31]) by smtp.gmail.com with ESMTPSA id z192-v6sm3348462wmc.10.2018.05.24.01.55.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 24 May 2018 01:55:14 -0700 (PDT) From: Neil Armstrong To: airlied@linux.ie, hans.verkuil@cisco.com, lee.jones@linaro.org, olof@lixom.net, seanpaul@google.com Cc: Neil Armstrong , sadolfsson@google.com, felixe@google.com, bleung@google.com, darekm@google.com, marcheu@chromium.org, fparent@baylibre.com, dri-devel@lists.freedesktop.org, linux-media@vger.kernel.org, intel-gfx@lists.freedesktop.org, linux-kernel@vger.kernel.org, eballetbo@gmail.com Subject: [PATCH v5 6/6] media: platform: Add ChromeOS EC CEC driver Date: Thu, 24 May 2018 10:55:01 +0200 Message-Id: <1527152101-17278-7-git-send-email-narmstrong@baylibre.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1527152101-17278-1-git-send-email-narmstrong@baylibre.com> References: <1527152101-17278-1-git-send-email-narmstrong@baylibre.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The ChromeOS Embedded Controller can expose a CEC bus, this patch add the driver for such feature of the Embedded Controller. This driver is part of the cros-ec MFD and will be add as a sub-device when the feature bit is exposed by the EC. The controller will only handle a single logical address and handles all the messages retries and will only expose Success or Error. The controller will be tied to the HDMI CEC notifier by using the platform DMI Data and the i915 device name and connector name. Signed-off-by: Neil Armstrong Reviewed-by: Enric Balletbo i Serra --- drivers/media/platform/Kconfig | 11 + drivers/media/platform/Makefile | 2 + drivers/media/platform/cros-ec-cec/Makefile | 1 + drivers/media/platform/cros-ec-cec/cros-ec-cec.c | 347 +++++++++++++++++++++++ 4 files changed, 361 insertions(+) create mode 100644 drivers/media/platform/cros-ec-cec/Makefile create mode 100644 drivers/media/platform/cros-ec-cec/cros-ec-cec.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c7a1cf8..bc37ecf 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -546,6 +546,17 @@ menuconfig CEC_PLATFORM_DRIVERS if CEC_PLATFORM_DRIVERS +config VIDEO_CROS_EC_CEC + tristate "ChromeOS EC CEC driver" + depends on MFD_CROS_EC || COMPILE_TEST + select CEC_CORE + select CEC_NOTIFIER + ---help--- + If you say yes here you will get support for the + ChromeOS Embedded Controller's CEC. + The CEC bus is present in the HDMI connector and enables communication + between compatible devices. + config VIDEO_MESON_AO_CEC tristate "Amlogic Meson AO CEC driver" depends on ARCH_MESON || COMPILE_TEST diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 932515d..830696f 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -92,3 +92,5 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-y += meson/ + +obj-y += cros-ec-cec/ diff --git a/drivers/media/platform/cros-ec-cec/Makefile b/drivers/media/platform/cros-ec-cec/Makefile new file mode 100644 index 0000000..9ce97f9 --- /dev/null +++ b/drivers/media/platform/cros-ec-cec/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_CROS_EC_CEC) += cros-ec-cec.o diff --git a/drivers/media/platform/cros-ec-cec/cros-ec-cec.c b/drivers/media/platform/cros-ec-cec/cros-ec-cec.c new file mode 100644 index 0000000..7f897a2 --- /dev/null +++ b/drivers/media/platform/cros-ec-cec/cros-ec-cec.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * CEC driver for ChromeOS Embedded Controller + * + * Copyright (c) 2018 BayLibre, SAS + * Author: Neil Armstrong + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-ec-cec" + +/** + * struct cros_ec_cec - Driver data for EC CEC + * + * @cros_ec: Pointer to EC device + * @notifier: Notifier info for responding to EC events + * @adap: CEC adapter + * @notify: CEC notifier pointer + * @rx_msg: storage for a received message + */ +struct cros_ec_cec { + struct cros_ec_device *cros_ec; + struct notifier_block notifier; + struct cec_adapter *adap; + struct cec_notifier *notify; + struct cec_msg rx_msg; +}; + +static void handle_cec_message(struct cros_ec_cec *cros_ec_cec) +{ + struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; + uint8_t *cec_message = cros_ec->event_data.data.cec_message; + unsigned int len = cros_ec->event_size; + + cros_ec_cec->rx_msg.len = len; + memcpy(cros_ec_cec->rx_msg.msg, cec_message, len); + + cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg); +} + +static void handle_cec_event(struct cros_ec_cec *cros_ec_cec) +{ + struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; + uint32_t events = cros_ec->event_data.data.cec_events; + + if (events & EC_MKBP_CEC_SEND_OK) + cec_transmit_attempt_done(cros_ec_cec->adap, + CEC_TX_STATUS_OK); + + /* FW takes care of all retries, tell core to avoid more retries */ + if (events & EC_MKBP_CEC_SEND_FAILED) + cec_transmit_attempt_done(cros_ec_cec->adap, + CEC_TX_STATUS_MAX_RETRIES | + CEC_TX_STATUS_NACK); +} + +static int cros_ec_cec_event(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct cros_ec_cec *cros_ec_cec; + struct cros_ec_device *cros_ec; + + cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier); + cros_ec = cros_ec_cec->cros_ec; + + if (cros_ec->event_data.event_type == EC_MKBP_CEC_EVENT) { + handle_cec_event(cros_ec_cec); + return NOTIFY_OK; + } + + if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) { + handle_cec_message(cros_ec_cec); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr) +{ + struct cros_ec_cec *cros_ec_cec = adap->priv; + struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; + struct { + struct cros_ec_command msg; + struct ec_params_cec_set data; + } __packed msg = {}; + int ret; + + msg.msg.command = EC_CMD_CEC_SET; + msg.msg.outsize = sizeof(msg.data); + msg.data.cmd = CEC_CMD_LOGICAL_ADDRESS; + msg.data.address = logical_addr; + + ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); + if (ret < 0) { + dev_err(cros_ec->dev, + "error setting CEC logical address on EC: %d\n", ret); + return ret; + } + + return 0; +} + +static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *cec_msg) +{ + struct cros_ec_cec *cros_ec_cec = adap->priv; + struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; + struct { + struct cros_ec_command msg; + struct ec_params_cec_write data; + } __packed msg = {}; + int ret; + + msg.msg.command = EC_CMD_CEC_WRITE_MSG; + msg.msg.outsize = cec_msg->len; + memcpy(msg.data.msg, cec_msg->msg, cec_msg->len); + + ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); + if (ret < 0) { + dev_err(cros_ec->dev, + "error writing CEC msg on EC: %d\n", ret); + return ret; + } + + return 0; +} + +static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct cros_ec_cec *cros_ec_cec = adap->priv; + struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec; + struct { + struct cros_ec_command msg; + struct ec_params_cec_set data; + } __packed msg = {}; + int ret; + + msg.msg.command = EC_CMD_CEC_SET; + msg.msg.outsize = sizeof(msg.data); + msg.data.cmd = CEC_CMD_ENABLE; + msg.data.enable = enable; + + ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); + if (ret < 0) { + dev_err(cros_ec->dev, + "error %sabling CEC on EC: %d\n", + (enable ? "en" : "dis"), ret); + return ret; + } + + return 0; +} + +static const struct cec_adap_ops cros_ec_cec_ops = { + .adap_enable = cros_ec_cec_adap_enable, + .adap_log_addr = cros_ec_cec_set_log_addr, + .adap_transmit = cros_ec_cec_transmit, +}; + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_cec_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(cros_ec_cec->cros_ec->irq); + + return 0; +} + +static int cros_ec_cec_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(cros_ec_cec->cros_ec->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops, + cros_ec_cec_suspend, cros_ec_cec_resume); + +#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI) + +/* + * The Firmware only handles a single CEC interface tied to a single HDMI + * connector we specify along with the DRM device name handling the HDMI output + */ + +struct cec_dmi_match { + char *sys_vendor; + char *product_name; + char *devname; + char *conn; +}; + +static const struct cec_dmi_match cec_dmi_match_table[] = { + /* Google Fizz */ + { "Google", "Fizz", "0000:00:02.0", "Port B" }, +}; + +static int cros_ec_cec_get_notifier(struct device *dev, + struct cec_notifier **notify) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) { + const struct cec_dmi_match *m = &cec_dmi_match_table[i]; + + if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) && + dmi_match(DMI_PRODUCT_NAME, m->product_name)) { + struct device *d; + + /* Find the device, bail out if not yet registered */ + d = bus_find_device_by_name(&pci_bus_type, NULL, + m->devname); + if (!d) + return -EPROBE_DEFER; + + *notify = cec_notifier_get_conn(d, m->conn); + return 0; + } + } + + /* Hardware support must be added in the cec_dmi_match_table */ + dev_warn(dev, "CEC notifier not configured for this hardware\n"); + + return -ENODEV; +} + +#else + +static int cros_ec_cec_get_notifier(struct device *dev, + struct cec_notifier **notify) +{ + return -ENODEV; +} + +#endif + +static int cros_ec_cec_probe(struct platform_device *pdev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct cros_ec_cec *cros_ec_cec; + int ret; + + cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec), + GFP_KERNEL); + if (!cros_ec_cec) + return -ENOMEM; + + platform_set_drvdata(pdev, cros_ec_cec); + cros_ec_cec->cros_ec = cros_ec; + + ret = cros_ec_cec_get_notifier(&pdev->dev, &cros_ec_cec->notify); + if (ret) + return ret; + + ret = device_init_wakeup(&pdev->dev, 1); + if (ret) { + dev_err(&pdev->dev, "failed to initialize wakeup\n"); + return ret; + } + + cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec, + DRV_NAME, CEC_CAP_DEFAULTS, 1); + if (IS_ERR(cros_ec_cec->adap)) + return PTR_ERR(cros_ec_cec->adap); + + /* Get CEC events from the EC. */ + cros_ec_cec->notifier.notifier_call = cros_ec_cec_event; + ret = blocking_notifier_chain_register(&cros_ec->event_notifier, + &cros_ec_cec->notifier); + if (ret) { + dev_err(&pdev->dev, "failed to register notifier\n"); + cec_delete_adapter(cros_ec_cec->adap); + return ret; + } + + ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev); + if (ret < 0) { + cec_delete_adapter(cros_ec_cec->adap); + return ret; + } + + cec_register_cec_notifier(cros_ec_cec->adap, cros_ec_cec->notify); + + return 0; +} + +static int cros_ec_cec_remove(struct platform_device *pdev) +{ + struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + ret = blocking_notifier_chain_unregister( + &cros_ec_cec->cros_ec->event_notifier, + &cros_ec_cec->notifier); + + if (ret) { + dev_err(dev, "failed to unregister notifier\n"); + return ret; + } + + cec_unregister_adapter(cros_ec_cec->adap); + + if (cros_ec_cec->notify) + cec_notifier_put(cros_ec_cec->notify); + + return 0; +} + +static struct platform_driver cros_ec_cec_driver = { + .probe = cros_ec_cec_probe, + .remove = cros_ec_cec_remove, + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_cec_pm_ops, + }, +}; + +module_platform_driver(cros_ec_cec_driver); + +MODULE_DESCRIPTION("CEC driver for ChromeOS ECs"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- 2.7.4