Merge greybus driver tree into 4.8-rc6
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Sep 2016 10:29:33 +0000 (12:29 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Sep 2016 10:29:33 +0000 (12:29 +0200)
This pulls the external greybus driver tree into 4.8-rc6 as it should be
part of the main kernel tree and not live outside in some lonely github
repo, never to be reunited with it's true love...

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
148 files changed:
drivers/staging/greybus/.gitignore [new file with mode: 0644]
drivers/staging/greybus/Documentation/es1_ap_desc.c [new file with mode: 0644]
drivers/staging/greybus/Documentation/firmware/authenticate.c [new file with mode: 0644]
drivers/staging/greybus/Documentation/firmware/firmware-management [new file with mode: 0644]
drivers/staging/greybus/Documentation/firmware/firmware.c [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs-bus-greybus [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject [new file with mode: 0644]
drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id [new file with mode: 0644]
drivers/staging/greybus/LICENSE [new file with mode: 0644]
drivers/staging/greybus/Makefile [new file with mode: 0644]
drivers/staging/greybus/README [new file with mode: 0644]
drivers/staging/greybus/arche-apb-ctrl.c [new file with mode: 0644]
drivers/staging/greybus/arche-platform.c [new file with mode: 0644]
drivers/staging/greybus/arche_platform.h [new file with mode: 0644]
drivers/staging/greybus/arpc.h [new file with mode: 0644]
drivers/staging/greybus/audio_apbridgea.c [new file with mode: 0644]
drivers/staging/greybus/audio_apbridgea.h [new file with mode: 0644]
drivers/staging/greybus/audio_codec.c [new file with mode: 0644]
drivers/staging/greybus/audio_codec.h [new file with mode: 0644]
drivers/staging/greybus/audio_gb.c [new file with mode: 0644]
drivers/staging/greybus/audio_manager.c [new file with mode: 0644]
drivers/staging/greybus/audio_manager.h [new file with mode: 0644]
drivers/staging/greybus/audio_manager_module.c [new file with mode: 0644]
drivers/staging/greybus/audio_manager_private.h [new file with mode: 0644]
drivers/staging/greybus/audio_manager_sysfs.c [new file with mode: 0644]
drivers/staging/greybus/audio_module.c [new file with mode: 0644]
drivers/staging/greybus/audio_topology.c [new file with mode: 0644]
drivers/staging/greybus/authentication.c [new file with mode: 0644]
drivers/staging/greybus/bootrom.c [new file with mode: 0644]
drivers/staging/greybus/bundle.c [new file with mode: 0644]
drivers/staging/greybus/bundle.h [new file with mode: 0644]
drivers/staging/greybus/camera.c [new file with mode: 0644]
drivers/staging/greybus/connection.c [new file with mode: 0644]
drivers/staging/greybus/connection.h [new file with mode: 0644]
drivers/staging/greybus/control.c [new file with mode: 0644]
drivers/staging/greybus/control.h [new file with mode: 0644]
drivers/staging/greybus/core.c [new file with mode: 0644]
drivers/staging/greybus/debugfs.c [new file with mode: 0644]
drivers/staging/greybus/devices [new file with mode: 0644]
drivers/staging/greybus/es2.c [new file with mode: 0644]
drivers/staging/greybus/firmware.h [new file with mode: 0644]
drivers/staging/greybus/fw-core.c [new file with mode: 0644]
drivers/staging/greybus/fw-download.c [new file with mode: 0644]
drivers/staging/greybus/fw-management.c [new file with mode: 0644]
drivers/staging/greybus/gb-camera.h [new file with mode: 0644]
drivers/staging/greybus/gbphy.c [new file with mode: 0644]
drivers/staging/greybus/gbphy.h [new file with mode: 0644]
drivers/staging/greybus/gpio.c [new file with mode: 0644]
drivers/staging/greybus/greybus.h [new file with mode: 0644]
drivers/staging/greybus/greybus_authentication.h [new file with mode: 0644]
drivers/staging/greybus/greybus_firmware.h [new file with mode: 0644]
drivers/staging/greybus/greybus_id.h [new file with mode: 0644]
drivers/staging/greybus/greybus_manifest.h [new file with mode: 0644]
drivers/staging/greybus/greybus_protocols.h [new file with mode: 0644]
drivers/staging/greybus/greybus_trace.h [new file with mode: 0644]
drivers/staging/greybus/hd.c [new file with mode: 0644]
drivers/staging/greybus/hd.h [new file with mode: 0644]
drivers/staging/greybus/hid.c [new file with mode: 0644]
drivers/staging/greybus/i2c.c [new file with mode: 0644]
drivers/staging/greybus/interface.c [new file with mode: 0644]
drivers/staging/greybus/interface.h [new file with mode: 0644]
drivers/staging/greybus/kernel_ver.h [new file with mode: 0644]
drivers/staging/greybus/light.c [new file with mode: 0644]
drivers/staging/greybus/log.c [new file with mode: 0644]
drivers/staging/greybus/loopback.c [new file with mode: 0644]
drivers/staging/greybus/manifest.c [new file with mode: 0644]
drivers/staging/greybus/manifest.h [new file with mode: 0644]
drivers/staging/greybus/module.c [new file with mode: 0644]
drivers/staging/greybus/module.h [new file with mode: 0644]
drivers/staging/greybus/operation.c [new file with mode: 0644]
drivers/staging/greybus/operation.h [new file with mode: 0644]
drivers/staging/greybus/power_supply.c [new file with mode: 0644]
drivers/staging/greybus/pwm.c [new file with mode: 0644]
drivers/staging/greybus/raw.c [new file with mode: 0644]
drivers/staging/greybus/scripts/checkpatch.pl [new file with mode: 0755]
drivers/staging/greybus/scripts/spelling.txt [new file with mode: 0644]
drivers/staging/greybus/sdio.c [new file with mode: 0644]
drivers/staging/greybus/spi.c [new file with mode: 0644]
drivers/staging/greybus/spilib.c [new file with mode: 0644]
drivers/staging/greybus/spilib.h [new file with mode: 0644]
drivers/staging/greybus/svc.c [new file with mode: 0644]
drivers/staging/greybus/svc.h [new file with mode: 0644]
drivers/staging/greybus/svc_watchdog.c [new file with mode: 0644]
drivers/staging/greybus/timesync.c [new file with mode: 0644]
drivers/staging/greybus/timesync.h [new file with mode: 0644]
drivers/staging/greybus/timesync_platform.c [new file with mode: 0644]
drivers/staging/greybus/tools/Android.mk [new file with mode: 0644]
drivers/staging/greybus/tools/Makefile [new file with mode: 0644]
drivers/staging/greybus/tools/README.loopback [new file with mode: 0644]
drivers/staging/greybus/tools/lbtest [new file with mode: 0755]
drivers/staging/greybus/tools/loopback_test.c [new file with mode: 0644]
drivers/staging/greybus/uart.c [new file with mode: 0644]
drivers/staging/greybus/usb.c [new file with mode: 0644]
drivers/staging/greybus/vibrator.c [new file with mode: 0644]

diff --git a/drivers/staging/greybus/.gitignore b/drivers/staging/greybus/.gitignore
new file mode 100644 (file)
index 0000000..faf45ee
--- /dev/null
@@ -0,0 +1,15 @@
+.*
+*.cmd
+*.ko
+*.mod.c
+modules.order
+Module.symvers
+*.o
+*.o.*
+*.swp
+.tmp_versions
+tags
+cscope.*
+ncscope.*
+*.patch
+tools/loopback_test
diff --git a/drivers/staging/greybus/Documentation/es1_ap_desc.c b/drivers/staging/greybus/Documentation/es1_ap_desc.c
new file mode 100644 (file)
index 0000000..1502089
--- /dev/null
@@ -0,0 +1,70 @@
+/* ES1 AP Bridge Chip USB descriptor definitions */
+
+static const u8 es1_dev_descriptor[] = {
+       0x12,           /* __u8   bLength */
+       0x01,           /* __u8   bDescriptorType; Device */
+       0x00, 0x02      /* __le16 bcdUSB v2.0 */
+       0x00,           /* __u8   bDeviceClass */
+       0x00,           /* __u8   bDeviceClass */
+       0x00,           /* __u8   bDeviceSubClass; */
+       0x00,           /* __u8   bDeviceProtocol; */
+       0x40,           /* __u8   bMaxPacketSize0; 2^64 = 512 Bytes */
+
+       0xff, 0xff,     /* __le16 idVendor; 0xffff  made up for now */
+       0x01, 0x00,     /* __le16 idProduct; 0x0001  made up for now */
+       0x01, 0x00,     /* __le16 bcdDevice; ES1 */
+
+       0x03,           /* __u8  iManufacturer; */
+       0x02,           /* __u8  iProduct; */
+       0x01,           /* __u8  iSerialNumber; */
+       0x01            /* __u8  bNumConfigurations; */
+};
+
+static const u8 es1_config_descriptor[] = {
+       /* one configuration */
+       0x09,           /*  __u8   bLength; */
+       0x02,           /*  __u8   bDescriptorType; Configuration */
+       0x19, 0x00,     /*  __le16 wTotalLength; */
+       0x01,           /*  __u8   bNumInterfaces; (1) */
+       0x01,           /*  __u8   bConfigurationValue; */
+       0x00,           /*  __u8   iConfiguration; */
+       0xc0,           /*  __u8   bmAttributes;
+                                Bit 7: must be set,
+                                    6: Self-powered,
+                                    5: Remote wakeup,
+                                    4..0: resvd */
+       0x00,           /*  __u8  MaxPower; */
+
+       /* one interface */
+       0x09,           /*  __u8  if_bLength; */
+       0x04,           /*  __u8  if_bDescriptorType; Interface */
+       0x00,           /*  __u8  if_bInterfaceNumber; */
+       0x00,           /*  __u8  if_bAlternateSetting; */
+       0x03,           /*  __u8  if_bNumEndpoints; */
+       0xff,           /*  __u8  if_bInterfaceClass; Vendor-specific */
+       0xff,           /*  __u8  if_bInterfaceSubClass; Vendor-specific */
+       0xff,           /*  __u8  if_bInterfaceProtocol; Vendor-specific */
+       0x00,           /*  __u8  if_iInterface; */
+
+       /* three endpoints */
+       0x07,           /*  __u8   ep_bLength; */
+       0x05,           /*  __u8   ep_bDescriptorType; Endpoint */
+       0x81,           /*  __u8   ep_bEndpointAddress; IN Endpoint 1 */
+       0x03,           /*  __u8   ep_bmAttributes; Interrupt */
+       0x00, 0x04,     /*  __le16 ep_wMaxPacketSize; 1024 */
+       0x40,           /*  __u8   ep_bInterval; 64ms */
+
+       0x07,           /*  __u8   ep_bLength; */
+       0x05,           /*  __u8   ep_bDescriptorType; Endpoint */
+       0x82,           /*  __u8   ep_bEndpointAddress; IN Endpoint 2 */
+       0x02,           /*  __u8   ep_bmAttributes; Bulk */
+       0x00, 0x04,     /*  __le16 ep_wMaxPacketSize; 1024 */
+       0x40            /*  __u8   ep_bInterval; */
+
+       0x07,           /*  __u8   ep_bLength; */
+       0x05,           /*  __u8   ep_bDescriptorType; Endpoint */
+       0x02,           /*  __u8   ep_bEndpointAddress; Out Endpoint 2 */
+       0x02,           /*  __u8   ep_bmAttributes; Bulk */
+       0x00, 0x04,     /*  __le16 ep_wMaxPacketSize; 1024 */
+       0x40            /*  __u8   ep_bInterval; */
+};
diff --git a/drivers/staging/greybus/Documentation/firmware/authenticate.c b/drivers/staging/greybus/Documentation/firmware/authenticate.c
new file mode 100644 (file)
index 0000000..ab0688a
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Sample code to test CAP protocol
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2016 Google Inc. All rights reserved.
+ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Google Inc. All rights reserved.
+ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google Inc. or Linaro Ltd. nor the names of
+ *    its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
+ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "../../greybus_authentication.h"
+
+struct cap_ioc_get_endpoint_uid uid;
+struct cap_ioc_get_ims_certificate cert = {
+       .certificate_class = 0,
+       .certificate_id = 0,
+};
+
+struct cap_ioc_authenticate authenticate = {
+       .auth_type = 0,
+       .challenge = {0},
+};
+
+int main(int argc, char *argv[])
+{
+       unsigned int timeout = 10000;
+       char *capdev;
+       int fd, ret;
+
+       /* Make sure arguments are correct */
+       if (argc != 2) {
+               printf("\nUsage: ./firmware <Path of the gb-cap-X dev>\n");
+               return 0;
+       }
+
+       capdev = argv[1];
+
+       printf("Opening %s authentication device\n", capdev);
+
+       fd = open(capdev, O_RDWR);
+       if (fd < 0) {
+               printf("Failed to open: %s\n", capdev);
+               return -1;
+       }
+
+       /* Get UID */
+       printf("Get UID\n");
+
+       ret = ioctl(fd, CAP_IOC_GET_ENDPOINT_UID, &uid);
+       if (ret < 0) {
+               printf("Failed to get UID: %s (%d)\n", capdev, ret);
+               ret = -1;
+               goto close_fd;
+       }
+
+       printf("UID received: 0x%llx\n", *(long long unsigned int *)(uid.uid));
+
+       /* Get certificate */
+       printf("Get IMS certificate\n");
+
+       ret = ioctl(fd, CAP_IOC_GET_IMS_CERTIFICATE, &cert);
+       if (ret < 0) {
+               printf("Failed to get IMS certificate: %s (%d)\n", capdev, ret);
+               ret = -1;
+               goto close_fd;
+       }
+
+       printf("IMS Certificate size: %d\n", cert.cert_size);
+
+       /* Authenticate */
+       printf("Authenticate module\n");
+
+       memcpy(authenticate.uid, uid.uid, 8);
+
+       ret = ioctl(fd, CAP_IOC_AUTHENTICATE, &authenticate);
+       if (ret < 0) {
+               printf("Failed to authenticate module: %s (%d)\n", capdev, ret);
+               ret = -1;
+               goto close_fd;
+       }
+
+       printf("Authenticated, result (%02x), sig-size (%02x)\n",
+               authenticate.result_code, authenticate.signature_size);
+
+close_fd:
+       close(fd);
+
+       return ret;
+}
diff --git a/drivers/staging/greybus/Documentation/firmware/firmware-management b/drivers/staging/greybus/Documentation/firmware/firmware-management
new file mode 100644 (file)
index 0000000..7918257
--- /dev/null
@@ -0,0 +1,333 @@
+
+Firmware Management
+-------------------
+ Copyright 2016 Google Inc.
+ Copyright 2016 Linaro Ltd.
+
+Interface-Manifest
+------------------
+
+All firmware packages on the Modules or Interfaces are managed by a special
+Firmware Management Protocol. To support Firmware Management by the AP, the
+Interface Manifest shall at least contain the Firmware Management Bundle and a
+Firmware Management Protocol CPort within it.
+
+The bundle may contain additional CPorts based on the extra functionality
+required to manage firmware packages.
+
+For example, this is how the Firmware Management part of the Interface Manifest
+may look like:
+
+       ; Firmware Management Bundle (Bundle 1):
+       [bundle-descriptor 1]
+       class = 0x16
+
+       ; (Mandatory) Firmware Management Protocol on CPort 1
+       [cport-descriptor 2]
+       bundle = 1
+       protocol = 0x18
+
+       ; (Optional) Firmware Download Protocol on CPort 2
+       [cport-descriptor 1]
+       bundle = 1
+       protocol = 0x17
+
+       ; (Optional) SPI protocol on CPort 3
+       [cport-descriptor 3]
+       bundle = 1
+       protocol = 0x0b
+
+       ; (Optional) Component Authentication Protocol (CAP) on CPort 4
+       [cport-descriptor 4]
+       bundle = 1
+       protocol = 0x19
+
+
+Sysfs Interfaces - Firmware Management
+--------------------------------------
+
+The Firmware Management Protocol interacts with Userspace using the character
+device interface. The character device will be present in /dev/ directory
+and will be named gb-fw-mgmt-<N>. The number <N> is assigned at runtime.
+
+Identifying the Character Device
+================================
+
+There can be multiple devices present in /dev/ directory with name gb-fw-mgmt-N
+and user first needs to identify the character device used for
+firmware-management for a particular interface.
+
+The Firmware Management core creates a device of class 'gb_fw_mgmt', which shall
+be used by the user to identify the right character device for it. The class
+device is created within the Bundle directory for a particular Interface.
+
+For example this is how the class-device can be present:
+
+/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_fw_mgmt/gb-fw-mgmt-0
+
+The last name in this path: gb-fw-mgmt-0 is precisely the name of the char
+device and so the device in this case will be:
+
+/dev/gb-fw-mgmt-0.
+
+Operations on the Char device
+=============================
+
+The Character device (gb-fw-mgmt-0 in example) can be opened by the userspace
+application and it can perform various 'ioctl' operations on the device. The
+device doesn't support any read/write operations.
+
+Following are the IOCTLs and their data structures available to the user:
+
+/* IOCTL support */
+#define GB_FW_LOAD_METHOD_UNIPRO               0x01
+#define GB_FW_LOAD_METHOD_INTERNAL             0x02
+
+#define GB_FW_LOAD_STATUS_FAILED               0x00
+#define GB_FW_LOAD_STATUS_UNVALIDATED          0x01
+#define GB_FW_LOAD_STATUS_VALIDATED            0x02
+#define GB_FW_LOAD_STATUS_VALIDATION_FAILED    0x03
+
+#define GB_FW_BACKEND_FW_STATUS_SUCCESS                0x01
+#define GB_FW_BACKEND_FW_STATUS_FAIL_FIND      0x02
+#define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH     0x03
+#define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE     0x04
+#define GB_FW_BACKEND_FW_STATUS_INT            0x05
+#define GB_FW_BACKEND_FW_STATUS_RETRY          0x06
+#define GB_FW_BACKEND_FW_STATUS_NOT_SUPPORTED  0x07
+
+#define GB_FW_BACKEND_VERSION_STATUS_SUCCESS           0x01
+#define GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE     0x02
+#define GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED     0x03
+#define GB_FW_BACKEND_VERSION_STATUS_RETRY             0x04
+#define GB_FW_BACKEND_VERSION_STATUS_FAIL_INT          0x05
+
+
+struct fw_mgmt_ioc_get_intf_version {
+       __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
+       __u16 major;
+       __u16 minor;
+} __attribute__ ((__packed__));
+
+struct fw_mgmt_ioc_get_backend_version {
+       __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE];
+       __u16 major;
+       __u16 minor;
+       __u8 status;
+} __attribute__ ((__packed__));
+
+struct fw_mgmt_ioc_intf_load_and_validate {
+       __u8                    firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE];
+       __u8                    load_method;
+       __u8                    status;
+       __u16                   major;
+       __u16                   minor;
+} __packed;
+
+struct fw_mgmt_ioc_backend_fw_update {
+       __u8                    firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE];
+       __u8                    status;
+} __packed;
+
+#define FW_MGMT_IOCTL_BASE                     'S'
+#define FW_MGMT_IOC_GET_INTF_FW                        _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_intf_version)
+#define FW_MGMT_IOC_GET_BACKEND_FW             _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_backend_version)
+#define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE     _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate)
+#define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE     _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update)
+#define FW_MGMT_IOC_SET_TIMEOUT_MS             _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int)
+#define FW_MGMT_IOC_MODE_SWITCH                        _IO(FW_MGMT_IOCTL_BASE, 5)
+
+1. FW_MGMT_IOC_GET_INTF_FW:
+
+   This ioctl shall be used by the user to get the version and firmware-tag of
+   the currently running Interface Firmware. All the fields of the 'struct
+   fw_mgmt_ioc_get_fw' are filled by the kernel.
+
+2. FW_MGMT_IOC_GET_BACKEND_FW:
+
+   This ioctl shall be used by the user to get the version of a currently
+   running Backend Interface Firmware identified by a firmware-tag. The user is
+   required to fill the 'firmware_tag' field of the 'struct fw_mgmt_ioc_get_fw'
+   in this case. The 'major' and 'minor' fields are set by the kernel in
+   response.
+
+3. FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE:
+
+   This ioctl shall be used by the user to load an Interface Firmware package on
+   an Interface. The user needs to fill the 'firmware_tag' and 'load_method'
+   fields of the 'struct fw_mgmt_ioc_intf_load_and_validate'. The 'status',
+   'major' and 'minor' fields are set by the kernel in response.
+
+4. FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE:
+
+   This ioctl shall be used by the user to request an Interface to update a
+   Backend Interface Firmware.  The user is required to fill the 'firmware_tag'
+   field of the 'struct fw_mgmt_ioc_get_fw' in this case. The 'status' field is
+   set by the kernel in response.
+
+5. FW_MGMT_IOC_SET_TIMEOUT_MS:
+
+   This ioctl shall be used by the user to increase the timeout interval within
+   which the firmware must get loaded by the Module. The default timeout is 1
+   second. The user needs to pass the timeout in milliseconds.
+
+6. FW_MGMT_IOC_MODE_SWITCH:
+
+   This ioctl shall be used by the user to mode-switch the module to the
+   previously loaded interface firmware. If the interface firmware isn't loaded
+   previously, or if another unsuccessful FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE
+   operation is started after loading interface firmware, then the firmware core
+   wouldn't allow mode-switch.
+
+
+Sysfs Interfaces - Authentication
+---------------------------------
+
+The Component Authentication Protocol interacts with Userspace using the
+character device interface. The character device will be present in /dev/
+directory and will be named gb-authenticate-<N>. The number <N> is assigned at
+runtime.
+
+Identifying the Character Device
+================================
+
+There can be multiple devices present in /dev/ directory with name
+gb-authenticate-N and user first needs to identify the character device used for
+authentication a of particular interface.
+
+The Authentication core creates a device of class 'gb_authenticate', which shall
+be used by the user to identify the right character device for it. The class
+device is created within the Bundle directory for a particular Interface.
+
+For example this is how the class-device can be present:
+
+/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_authenticate/gb-authenticate-0
+
+The last name in this path: gb-authenticate-0 is precisely the name of the char
+device and so the device in this case will be:
+
+/dev/gb-authenticate-0.
+
+Operations on the Char device
+=============================
+
+The Character device (/dev/gb-authenticate-0 in above example) can be opened by
+the userspace application and it can perform various 'ioctl' operations on the
+device. The device doesn't support any read/write operations.
+
+Following are the IOCTLs and their data structures available to the user:
+
+#define CAP_CERTIFICATE_MAX_SIZE       1600
+#define CAP_SIGNATURE_MAX_SIZE         320
+
+/* Certificate class types */
+#define CAP_CERT_IMS_EAPC              0x00000001
+#define CAP_CERT_IMS_EASC              0x00000002
+#define CAP_CERT_IMS_EARC              0x00000003
+#define CAP_CERT_IMS_IAPC              0x00000004
+#define CAP_CERT_IMS_IASC              0x00000005
+#define CAP_CERT_IMS_IARC              0x00000006
+
+/* IMS Certificate response result codes */
+#define CAP_IMS_RESULT_CERT_FOUND      0x00
+#define CAP_IMS_RESULT_CERT_CLASS_INVAL        0x01
+#define CAP_IMS_RESULT_CERT_CORRUPT    0x02
+#define CAP_IMS_RESULT_CERT_NOT_FOUND  0x03
+
+/* Authentication types */
+#define CAP_AUTH_IMS_PRI               0x00000001
+#define CAP_AUTH_IMS_SEC               0x00000002
+#define CAP_AUTH_IMS_RSA               0x00000003
+
+/* Authenticate response result codes */
+#define CAP_AUTH_RESULT_CR_SUCCESS     0x00
+#define CAP_AUTH_RESULT_CR_BAD_TYPE    0x01
+#define CAP_AUTH_RESULT_CR_WRONG_EP    0x02
+#define CAP_AUTH_RESULT_CR_NO_KEY      0x03
+#define CAP_AUTH_RESULT_CR_SIG_FAIL    0x04
+
+
+/* IOCTL support */
+struct cap_ioc_get_endpoint_uid {
+       __u8                    uid[8];
+} __attribute__ ((__packed__));
+
+struct cap_ioc_get_ims_certificate {
+       __u32                   certificate_class;
+       __u32                   certificate_id;
+
+       __u8                    result_code;
+       __u32                   cert_size;
+       __u8                    certificate[CAP_CERTIFICATE_MAX_SIZE];
+} __attribute__ ((__packed__));
+
+struct cap_ioc_authenticate {
+       __u32                   auth_type;
+       __u8                    uid[8];
+       __u8                    challenge[32];
+
+       __u8                    result_code;
+       __u8                    response[64];
+       __u32                   signature_size;
+       __u8                    signature[CAP_SIGNATURE_MAX_SIZE];
+} __attribute__ ((__packed__));
+
+#define CAP_IOCTL_BASE                 'C'
+#define CAP_IOC_GET_ENDPOINT_UID       _IOR(CAP_IOCTL_BASE, 0, struct cap_ioc_get_endpoint_uid)
+#define CAP_IOC_GET_IMS_CERTIFICATE    _IOWR(CAP_IOCTL_BASE, 1, struct cap_ioc_get_ims_certificate)
+#define CAP_IOC_AUTHENTICATE           _IOWR(CAP_IOCTL_BASE, 2, struct cap_ioc_authenticate)
+
+
+1. CAP_IOC_GET_ENDPOINT_UID:
+
+   This ioctl shall be used by the user to get the endpoint UID associated with
+   the Interface.  All the fields of the 'struct cap_ioc_get_endpoint_uid' are
+   filled by the kernel.
+
+2. CAP_IOC_GET_IMS_CERTIFICATE:
+
+   This ioctl shall be used by the user to retrieve one of the available
+   cryptographic certificates held by the Interface for use in Component
+   Authentication. The user is required to fill the 'certificate_class' and
+   'certificate_id' field of the 'struct cap_ioc_get_ims_certificate' in this
+   case. The other fields will be set by the kernel in response. The first
+   'cert_size' bytes of the 'certificate' shall be read by the user and others
+   must be discarded.
+
+3. CAP_IOC_AUTHENTICATE:
+
+   This ioctl shall be used by the user to authenticate the Module attached to
+   an Interface.  The user needs to fill the 'auth_type', 'uid', and 'challenge'
+   fields of the 'struct cap_ioc_authenticate'. The other fields will be set by
+   the kernel in response.  The first 'signature_size' bytes of the 'signature'
+   shall be read by the user and others must be discarded.
+
+
+Sysfs Interfaces - Firmware Download
+------------------------------------
+
+The Firmware Download Protocol uses the existing Linux Kernel's Firmware class
+and the interface provided to userspace are described in:
+Documentation/firmware_class/.
+
+
+Sysfs Interfaces - SPI Flash
+----------------------------
+
+The SPI flash is exposed in userspace as a MTD device and is created
+within the Bundle directory. For example, this is how the path may look like:
+
+$ ls /sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/spi_master/spi32766/spi32766.0/mtd
+mtd0    mtd0ro
+
+
+Sample Applications
+-------------------
+
+The current directory also provides a firmware.c test application, which can be
+referenced while developing userspace application to talk to firmware-management
+protocol.
+
+The current directory also provides a authenticate.c test application, which can
+be referenced while developing userspace application to talk to
+component authentication protocol.
diff --git a/drivers/staging/greybus/Documentation/firmware/firmware.c b/drivers/staging/greybus/Documentation/firmware/firmware.c
new file mode 100644 (file)
index 0000000..ff93824
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Sample code to test firmware-management protocol
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2016 Google Inc. All rights reserved.
+ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Google Inc. All rights reserved.
+ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google Inc. or Linaro Ltd. nor the names of
+ *    its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
+ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "../../greybus_firmware.h"
+
+#define FW_DEV_DEFAULT         "/dev/gb-fw-mgmt-0"
+#define FW_TAG_INT_DEFAULT     "s3f"
+#define FW_TAG_BCND_DEFAULT    "bf_01"
+#define FW_UPDATE_TYPE_DEFAULT 0
+#define FW_TIMEOUT_DEFAULT     10000;
+
+static const char *firmware_tag;
+static const char *fwdev = FW_DEV_DEFAULT;
+static int fw_update_type = FW_UPDATE_TYPE_DEFAULT;
+static int fw_timeout = FW_TIMEOUT_DEFAULT;
+
+static struct fw_mgmt_ioc_get_intf_version intf_fw_info;
+static struct fw_mgmt_ioc_get_backend_version backend_fw_info;
+static struct fw_mgmt_ioc_intf_load_and_validate intf_load;
+static struct fw_mgmt_ioc_backend_fw_update backend_update;
+
+static void usage(void)
+{
+       printf("\nUsage: ./firmware <gb-fw-mgmt-X (default: gb-fw-mgmt-0)> <interface: 0, backend: 1 (default: 0)> <firmware-tag> (default: \"s3f\"/\"bf_01\") <timeout (default: 10000 ms)>\n");
+}
+
+static int update_intf_firmware(int fd)
+{
+       int ret;
+
+       /* Get Interface Firmware Version */
+       printf("Get Interface Firmware Version\n");
+
+       ret = ioctl(fd, FW_MGMT_IOC_GET_INTF_FW, &intf_fw_info);
+       if (ret < 0) {
+               printf("Failed to get interface firmware version: %s (%d)\n",
+                       fwdev, ret);
+               return -1;
+       }
+
+       printf("Interface Firmware tag (%s), major (%d), minor (%d)\n",
+               intf_fw_info.firmware_tag, intf_fw_info.major,
+               intf_fw_info.minor);
+
+       /* Try Interface Firmware load over Unipro */
+       printf("Loading Interface Firmware\n");
+
+       intf_load.load_method = GB_FW_U_LOAD_METHOD_UNIPRO;
+       intf_load.status = 0;
+       intf_load.major = 0;
+       intf_load.minor = 0;
+
+       strncpy((char *)&intf_load.firmware_tag, firmware_tag,
+               GB_FIRMWARE_U_TAG_MAX_SIZE);
+
+       ret = ioctl(fd, FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE, &intf_load);
+       if (ret < 0) {
+               printf("Failed to load interface firmware: %s (%d)\n", fwdev,
+                       ret);
+               return -1;
+       }
+
+       if (intf_load.status != GB_FW_U_LOAD_STATUS_VALIDATED &&
+           intf_load.status != GB_FW_U_LOAD_STATUS_UNVALIDATED) {
+               printf("Load status says loading failed: %d\n",
+                       intf_load.status);
+               return -1;
+       }
+
+       printf("Interface Firmware (%s) Load done: major: %d, minor: %d, status: %d\n",
+               firmware_tag, intf_load.major, intf_load.minor,
+               intf_load.status);
+
+       /* Initiate Mode-switch to the newly loaded firmware */
+       printf("Initiate Mode switch\n");
+
+       ret = ioctl(fd, FW_MGMT_IOC_MODE_SWITCH);
+       if (ret < 0)
+               printf("Failed to initiate mode-switch (%d)\n", ret);
+
+       return ret;
+}
+
+static int update_backend_firmware(int fd)
+{
+       int ret;
+
+       /* Get Backend Firmware Version */
+       printf("Getting Backend Firmware Version\n");
+
+       strncpy((char *)&backend_fw_info.firmware_tag, firmware_tag,
+               GB_FIRMWARE_U_TAG_MAX_SIZE);
+
+retry_fw_version:
+       ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &backend_fw_info);
+       if (ret < 0) {
+               printf("Failed to get backend firmware version: %s (%d)\n",
+                       fwdev, ret);
+               return -1;
+       }
+
+       printf("Backend Firmware tag (%s), major (%d), minor (%d), status (%d)\n",
+               backend_fw_info.firmware_tag, backend_fw_info.major,
+               backend_fw_info.minor, backend_fw_info.status);
+
+       if (backend_fw_info.status == GB_FW_U_BACKEND_VERSION_STATUS_RETRY)
+               goto retry_fw_version;
+
+       if ((backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_SUCCESS)
+           && (backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_NOT_AVAILABLE)) {
+               printf("Failed to get backend firmware version: %s (%d)\n",
+                       fwdev, backend_fw_info.status);
+               return -1;
+       }
+
+       /* Try Backend Firmware Update over Unipro */
+       printf("Updating Backend Firmware\n");
+
+       strncpy((char *)&backend_update.firmware_tag, firmware_tag,
+               GB_FIRMWARE_U_TAG_MAX_SIZE);
+
+retry_fw_update:
+       backend_update.status = 0;
+
+       ret = ioctl(fd, FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE, &backend_update);
+       if (ret < 0) {
+               printf("Failed to load backend firmware: %s (%d)\n", fwdev, ret);
+               return -1;
+       }
+
+       if (backend_update.status == GB_FW_U_BACKEND_FW_STATUS_RETRY) {
+               printf("Retrying firmware update: %d\n", backend_update.status);
+               goto retry_fw_update;
+       }
+
+       if (backend_update.status != GB_FW_U_BACKEND_FW_STATUS_SUCCESS) {
+               printf("Load status says loading failed: %d\n",
+                       backend_update.status);
+       } else {
+               printf("Backend Firmware (%s) Load done: status: %d\n",
+                               firmware_tag, backend_update.status);
+       }
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int fd, ret;
+
+       if (argc > 1 &&
+           (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
+               usage();
+               return -1;
+       }
+
+       if (argc > 1)
+               fwdev = argv[1];
+
+       if (argc > 2)
+               sscanf(argv[2], "%u", &fw_update_type);
+
+       if (argc > 3) {
+               firmware_tag = argv[3];
+       } else if (!fw_update_type) {
+               firmware_tag = FW_TAG_INT_DEFAULT;
+       } else {
+               firmware_tag = FW_TAG_BCND_DEFAULT;
+       }
+
+       if (argc > 4)
+               sscanf(argv[4], "%u", &fw_timeout);
+
+       printf("Trying Firmware update: fwdev: %s, type: %s, tag: %s, timeout: %d\n",
+               fwdev, fw_update_type == 0 ? "interface" : "backend",
+               firmware_tag, fw_timeout);
+
+       printf("Opening %s firmware management device\n", fwdev);
+
+       fd = open(fwdev, O_RDWR);
+       if (fd < 0) {
+               printf("Failed to open: %s\n", fwdev);
+               return -1;
+       }
+
+       /* Set Timeout */
+       printf("Setting timeout to %u ms\n", fw_timeout);
+
+       ret = ioctl(fd, FW_MGMT_IOC_SET_TIMEOUT_MS, &fw_timeout);
+       if (ret < 0) {
+               printf("Failed to set timeout: %s (%d)\n", fwdev, ret);
+               ret = -1;
+               goto close_fd;
+       }
+
+       if (!fw_update_type)
+               ret = update_intf_firmware(fd);
+       else
+               ret = update_backend_firmware(fd);
+
+close_fd:
+       close(fd);
+
+       return ret;
+}
diff --git a/drivers/staging/greybus/Documentation/sysfs-bus-greybus b/drivers/staging/greybus/Documentation/sysfs-bus-greybus
new file mode 100644 (file)
index 0000000..2e99896
--- /dev/null
@@ -0,0 +1,275 @@
+What:          /sys/bus/greybus/devices/greybusN
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The "root" greybus device for the Greybus device tree, or bus,
+               where N is a dynamically assigned 1-based id.
+
+What:          /sys/bus/greybus/devices/greybusN/bus_id
+Date:          April 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The ID of the "root" greybus device, or bus.
+
+What:          /sys/bus/greybus/devices/N-M
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               A Module M on the bus N, where M is the 1-byte interface
+               ID of the module's primary interface.
+
+What:          /sys/bus/greybus/devices/N-M/eject
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Writing a non-zero argument to this attibute disables the
+               module's interfaces before physically ejecting it.
+
+What:          /sys/bus/greybus/devices/N-M/module_id
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The ID of a Greybus module, corresponding to the ID of its
+               primary interface.
+
+What:          /sys/bus/greybus/devices/N-M/num_interfaces
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The number of interfaces of a module.
+
+What:          /sys/bus/greybus/devices/N-M.I
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               An Interface I on the bus N and module N-M, where I is the
+               1-byte interface ID.
+
+What:          /sys/bus/greybus/devices/N-M.I/current_now
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Current measurement of the interface in microamps (uA)
+
+What:          /sys/bus/greybus/devices/N-M.I/ddbl1_manufacturer_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Unipro Device Descriptor Block Level 1 manufacturer ID for the
+               greybus Interface.
+
+What:          /sys/bus/greybus/devices/N-M.I/ddbl1_product_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Unipro Device Descriptor Block Level 1 product ID for the
+               greybus Interface.
+
+What:          /sys/bus/greybus/devices/N-M.I/interface_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The ID of a Greybus interface.
+
+What:          /sys/bus/greybus/devices/N-M.I/interface_type
+Date:          June 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The type of a Greybus interface; "dummy", "unipro", "greybus",
+               or "unknown".
+
+What:          /sys/bus/greybus/devices/N-M.I/power_now
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Power measurement of the interface in microwatts (uW)
+
+What:          /sys/bus/greybus/devices/N-M.I/power_state
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               This file reflects the power state of a Greybus interface. If
+               the value read from it is "on", then power is currently
+               supplied to the interface. Otherwise it will read "off" and
+               power is currently not supplied to the interface.
+
+               If the value read is "off", then writing "on" (or '1', 'y',
+               'Y') to this file will enable power to the interface and an
+               attempt to boot and possibly enumerate it will be made. Note
+               that on errors, the interface will again be powered down.
+
+               If the value read is "on", then writing "off" (or '0', 'n',
+               'N') to this file will power down the interface.
+
+What:          /sys/bus/greybus/devices/N-M.I/product_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Product ID of a Greybus interface.
+
+What:          /sys/bus/greybus/devices/N-M.I/serial_number
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Serial Number of the Greybus interface, represented by a 64 bit
+               hexadecimal number.
+
+What:          /sys/bus/greybus/devices/N-M.I/vendor_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Vendor ID of a Greybus interface.
+
+What:          /sys/bus/greybus/devices/N-M.I/voltage_now
+Date:          March 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Voltage measurement of the interface in microvolts (uV)
+
+What:          /sys/bus/greybus/devices/N-M.I.ctrl
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Abstract control device for interface I that represents the
+               current mode of an enumerated Greybus interface.
+
+What:          /sys/bus/greybus/devices/N-M.I.ctrl/product_string
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Product ID string of a Greybus interface.
+
+What:          /sys/bus/greybus/devices/N-M.I.ctrl/vendor_string
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Vendor ID string of a Greybus interface.
+
+What:          /sys/bus/greybus/devices/N-M.I.B
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               A bundle B on the Interface I, B is replaced by a 1-byte
+               number representing the bundle.
+
+What:          /sys/bus/greybus/devices/N-M.I.B/bundle_class
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The greybus class of the bundle B.
+
+What:          /sys/bus/greybus/devices/N-M.I.B/bundle_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The interface-unique id of the bundle B.
+
+What:          /sys/bus/greybus/devices/N-M.I.B/gpbX
+Date:          April 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The General Purpose Bridged PHY device of the bundle B,
+               where X is a dynamically assigned 0-based id.
+
+What:          /sys/bus/greybus/devices/N-M.I.B/state
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               A bundle has a state that is managed by the userspace
+               Endo process.  This file allows that Endo to signal
+               other Android HALs that the state of the bundle has
+               changed to a specific value.  When written to, any
+               process watching the file will be woken up, and the new
+               value can be read. It's a "poor-man's IPC", yes, but
+               simplifies the Android userspace code immensely.
+
+What:          /sys/bus/greybus/devices/N-svc
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The singleton SVC device of bus N.
+
+What:          /sys/bus/greybus/devices/N-svc/ap_intf_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The AP interface ID, a 1-byte non-zero integer which
+               defines the position of the AP module on the frame.
+               The interface positions are defined in the GMP
+               Module Developer Kit.
+
+What:          /sys/bus/greybus/devices/N-svc/endo_id
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The Endo ID, which is a 2-byte hexadecimal value
+               defined by the Endo layout scheme, documented in
+               the GMP Module Developer Kit.
+
+What:          /sys/bus/greybus/devices/N-svc/intf_eject
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               Write the number of the interface that you wish to
+               forcibly eject from the system.
+
+What:          /sys/bus/greybus/devices/N-svc/version
+Date:          October 2015
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               The version number of the firmware in the SVC device.
+
+What:          /sys/bus/greybus/devices/N-svc/watchdog
+Date:          October 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               If the SVC watchdog is enabled or not.  Writing 0 to this
+               file will disable the watchdog, writing 1 will enable it.
+
+What:          /sys/bus/greybus/devices/N-svc/watchdog_action
+Date:          July 2016
+KernelVersion: 4.XX
+Contact:       Greg Kroah-Hartman <greg@kroah.com>
+Description:
+               This attribute indicates the action to be performed upon SVC
+               watchdog bite.
+
+               The action can be one of the "reset" or "panic". Writing either
+               one of the "reset" or "panic" will change the behavior of SVC
+               watchdog bite. Default value is "reset".
+
+               "reset" means the UniPro subsystem is to be reset.
+
+               "panic" means SVC watchdog bite will cause kernel to panic.
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_class
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/bundle_id
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.1/state
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_class
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/bundle_id
new file mode 100644 (file)
index 0000000..0cfbf08
--- /dev/null
@@ -0,0 +1 @@
+2
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.2/state
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/product_string
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/1-2.2.ctrl/vendor_string
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_manufacturer_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/ddbl1_product_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/interface_id
new file mode 100644 (file)
index 0000000..0cfbf08
--- /dev/null
@@ -0,0 +1 @@
+2
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/product_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/serial_number
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/1-2.2/vendor_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/eject
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/module_id
new file mode 100644 (file)
index 0000000..0cfbf08
--- /dev/null
@@ -0,0 +1 @@
+2
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-2/num_interfaces
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_class
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/bundle_id
new file mode 100644 (file)
index 0000000..0cfbf08
--- /dev/null
@@ -0,0 +1 @@
+2
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/gpio/gpiochip490/.gitignore
new file mode 100644 (file)
index 0000000..f935021
--- /dev/null
@@ -0,0 +1 @@
+!.gitignore
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/gbphy1/i2c-4/.gitignore
new file mode 100644 (file)
index 0000000..f935021
--- /dev/null
@@ -0,0 +1 @@
+!.gitignore
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.2/state
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/product_string
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/1-5.5.ctrl/vendor_string
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_manufacturer_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/ddbl1_product_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/interface_id
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/product_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/serial_number
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.5/vendor_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/1-5.6/interface_id
new file mode 100644 (file)
index 0000000..1e8b314
--- /dev/null
@@ -0,0 +1 @@
+6
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/eject
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/module_id
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-5/num_interfaces
new file mode 100644 (file)
index 0000000..0cfbf08
--- /dev/null
@@ -0,0 +1 @@
+2
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/ap_intf_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/endo_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject b/drivers/staging/greybus/Documentation/sysfs/greybus1/1-svc/intf_eject
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id b/drivers/staging/greybus/Documentation/sysfs/greybus1/bus_id
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_class
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/bundle_id
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.1/state
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/product_string
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/2-3.3.ctrl/vendor_string
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_manufacturer_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/ddbl1_product_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/interface_id
new file mode 100644 (file)
index 0000000..00750ed
--- /dev/null
@@ -0,0 +1 @@
+3
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/product_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/serial_number
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/2-3.3/vendor_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/eject
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/module_id
new file mode 100644 (file)
index 0000000..00750ed
--- /dev/null
@@ -0,0 +1 @@
+3
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-3/num_interfaces
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/ap_intf_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/endo_id
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject b/drivers/staging/greybus/Documentation/sysfs/greybus2/2-svc/intf_eject
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id b/drivers/staging/greybus/Documentation/sysfs/greybus2/bus_id
new file mode 100644 (file)
index 0000000..0cfbf08
--- /dev/null
@@ -0,0 +1 @@
+2
diff --git a/drivers/staging/greybus/LICENSE b/drivers/staging/greybus/LICENSE
new file mode 100644 (file)
index 0000000..d7f1051
--- /dev/null
@@ -0,0 +1,339 @@
+GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {description}
+    Copyright (C) {year}  {fullname}
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  {signature of Ty Coon}, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile
new file mode 100644 (file)
index 0000000..fa5aaf3
--- /dev/null
@@ -0,0 +1,147 @@
+greybus-y :=   core.o          \
+               debugfs.o       \
+               hd.o            \
+               manifest.o      \
+               module.o        \
+               interface.o     \
+               bundle.o        \
+               connection.o    \
+               control.o       \
+               svc.o           \
+               svc_watchdog.o  \
+               operation.o     \
+               timesync.o      \
+               timesync_platform.o
+
+gb-gbphy-y := gbphy.o
+
+# Prefix all modules with gb-
+gb-vibrator-y := vibrator.o
+gb-power-supply-y := power_supply.o
+gb-log-y := log.o
+gb-loopback-y := loopback.o
+gb-light-y := light.o
+gb-raw-y := raw.o
+gb-hid-y := hid.o
+gb-es2-y := es2.o
+gb-arche-y := arche-platform.o arche-apb-ctrl.o
+gb-audio-module-y := audio_module.o audio_topology.o
+gb-audio-codec-y := audio_codec.o
+gb-audio-gb-y := audio_gb.o
+gb-audio-apbridgea-y := audio_apbridgea.o
+gb-audio-manager-y += audio_manager.o
+gb-audio-manager-y += audio_manager_module.o
+gb-bootrom-y := bootrom.o
+gb-camera-y := camera.o
+gb-firmware-y := fw-core.o fw-download.o fw-management.o authentication.o
+gb-spilib-y := spilib.o
+gb-sdio-y := sdio.o
+gb-uart-y := uart.o
+gb-pwm-y := pwm.o
+gb-gpio-y := gpio.o
+gb-i2c-y := i2c.o
+gb-usb-y := usb.o
+gb-spi-y := spi.o
+
+obj-m += greybus.o
+obj-m += gb-gbphy.o
+obj-m += gb-vibrator.o
+obj-m += gb-power-supply.o
+obj-m += gb-log.o
+obj-m += gb-loopback.o
+obj-m += gb-light.o
+obj-m += gb-hid.o
+obj-m += gb-raw.o
+obj-m += gb-es2.o
+ifeq ($(CONFIG_USB_HSIC_USB3613),y)
+ obj-m += gb-arche.o
+endif
+ifeq ($(CONFIG_ARCH_MSM8994),y)
+ obj-m += gb-audio-codec.o
+ obj-m += gb-audio-module.o
+ obj-m += gb-camera.o
+endif
+obj-m += gb-audio-gb.o
+obj-m += gb-audio-apbridgea.o
+obj-m += gb-audio-manager.o
+obj-m += gb-bootrom.o
+obj-m += gb-firmware.o
+obj-m += gb-spilib.o
+obj-m += gb-sdio.o
+obj-m += gb-uart.o
+obj-m += gb-pwm.o
+obj-m += gb-gpio.o
+obj-m += gb-i2c.o
+obj-m += gb-usb.o
+obj-m += gb-spi.o
+
+KERNELVER              ?= $(shell uname -r)
+KERNELDIR              ?= /lib/modules/$(KERNELVER)/build
+INSTALL_MOD_PATH       ?= /..
+PWD                    := $(shell pwd)
+
+# kernel config option that shall be enable
+CONFIG_OPTIONS_ENABLE := POWER_SUPPLY PWM SYSFS SPI USB SND_SOC MMC LEDS_CLASS INPUT
+
+# kernel config option that shall be disable
+CONFIG_OPTIONS_DISABLE :=
+
+# this only run in kbuild part of the makefile
+ifneq ($(KERNELRELEASE),)
+# This function returns the argument version if current kernel version is minor
+# than the passed version, return 1 if equal or the current kernel version if it
+# is greater than argument version.
+kvers_cmp=$(shell [ "$(KERNELVERSION)" = "$(1)" ] && echo 1 || printf "$(1)\n$(KERNELVERSION)" | sort -V | tail -1)
+
+ifneq ($(call kvers_cmp,"3.19.0"),3.19.0)
+    CONFIG_OPTIONS_ENABLE += LEDS_CLASS_FLASH
+endif
+
+ifneq ($(call kvers_cmp,"4.2.0"),4.2.0)
+    CONFIG_OPTIONS_ENABLE += V4L2_FLASH_LED_CLASS
+endif
+
+$(foreach opt,$(CONFIG_OPTIONS_ENABLE),$(if $(CONFIG_$(opt)),, \
+     $(error CONFIG_$(opt) is disabled in the kernel configuration and must be enable \
+     to continue compilation)))
+$(foreach opt,$(CONFIG_OPTIONS_DISABLE),$(if $(filter m y, $(CONFIG_$(opt))), \
+     $(error CONFIG_$(opt) is enabled in the kernel configuration and must be disable \
+     to continue compilation),))
+endif
+
+# add -Wall to try to catch everything we can.
+ccflags-y := -Wall
+
+# needed for trace events
+ccflags-y += -I$(src)
+
+GB_AUDIO_MANAGER_SYSFS ?= true
+ifeq ($(GB_AUDIO_MANAGER_SYSFS),true)
+gb-audio-manager-y += audio_manager_sysfs.o
+ccflags-y += -DGB_AUDIO_MANAGER_SYSFS
+endif
+
+all: module
+
+tools::
+       $(MAKE) -C tools KERNELDIR=$(realpath $(KERNELDIR))
+
+module:
+       $(MAKE) -C $(KERNELDIR) M=$(PWD)
+
+check:
+       $(MAKE) -C $(KERNELDIR) M=$(PWD) C=2 CF="-D__CHECK_ENDIAN__"
+
+clean:
+       rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
+       rm -f Module.markers Module.symvers modules.order
+       rm -rf .tmp_versions Modules.symvers
+       $(MAKE) -C tools clean
+
+coccicheck:
+       $(MAKE) -C $(KERNELDIR) M=$(PWD) coccicheck
+
+install: module
+       mkdir -p $(INSTALL_MOD_PATH)/lib/modules/$(KERNELVER)/kernel/drivers/greybus/
+       cp -f *.ko $(INSTALL_MOD_PATH)/lib/modules/$(KERNELVER)/kernel/drivers/greybus/
+       depmod -b $(INSTALL_MOD_PATH) -a $(KERNELVER)
diff --git a/drivers/staging/greybus/README b/drivers/staging/greybus/README
new file mode 100644 (file)
index 0000000..b0745d3
--- /dev/null
@@ -0,0 +1,10 @@
+Greybus kernel code
+
+To build against the running kernel (odds are you don't want this):
+       make
+
+To build against a specific kernel source tree (odds are you want this):
+       KERNELDIR=/home/some/random/place make
+
+Any questions / concerns about this code base, please email:
+       Greg Kroah-Hartman <greg@kroah.com>
diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c
new file mode 100644 (file)
index 0000000..59d9d42
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+ * Arche Platform driver to control APB.
+ *
+ * Copyright 2014-2015 Google Inc.
+ * Copyright 2014-2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include "arche_platform.h"
+
+
+struct arche_apb_ctrl_drvdata {
+       /* Control GPIO signals to and from AP <=> AP Bridges */
+       int resetn_gpio;
+       int boot_ret_gpio;
+       int pwroff_gpio;
+       int wake_in_gpio;
+       int wake_out_gpio;
+       int pwrdn_gpio;
+
+       enum arche_platform_state state;
+       bool init_disabled;
+
+       struct regulator *vcore;
+       struct regulator *vio;
+
+       int clk_en_gpio;
+       struct clk *clk;
+
+       struct pinctrl *pinctrl;
+       struct pinctrl_state *pin_default;
+
+       /* V2: SPI Bus control  */
+       int spi_en_gpio;
+       bool spi_en_polarity_high;
+};
+
+/*
+ * Note that these low level api's are active high
+ */
+static inline void deassert_reset(unsigned int gpio)
+{
+       gpio_set_value(gpio, 1);
+}
+
+static inline void assert_reset(unsigned int gpio)
+{
+       gpio_set_value(gpio, 0);
+}
+
+/*
+ * Note: Please do not modify the below sequence, as it is as per the spec
+ */
+static int coldboot_seq(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
+       int ret;
+
+       if (apb->init_disabled ||
+                       apb->state == ARCHE_PLATFORM_STATE_ACTIVE)
+               return 0;
+
+       /* Hold APB in reset state */
+       assert_reset(apb->resetn_gpio);
+
+       if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
+                       gpio_is_valid(apb->spi_en_gpio))
+               devm_gpio_free(dev, apb->spi_en_gpio);
+
+       /* Enable power to APB */
+       if (!IS_ERR(apb->vcore)) {
+               ret = regulator_enable(apb->vcore);
+               if (ret) {
+                       dev_err(dev, "failed to enable core regulator\n");
+                       return ret;
+               }
+       }
+
+       if (!IS_ERR(apb->vio)) {
+               ret = regulator_enable(apb->vio);
+               if (ret) {
+                       dev_err(dev, "failed to enable IO regulator\n");
+                       return ret;
+               }
+       }
+
+       apb_bootret_deassert(dev);
+
+       /* On DB3 clock was not mandatory */
+       if (gpio_is_valid(apb->clk_en_gpio))
+               gpio_set_value(apb->clk_en_gpio, 1);
+
+       usleep_range(100, 200);
+
+       /* deassert reset to APB : Active-low signal */
+       deassert_reset(apb->resetn_gpio);
+
+       apb->state = ARCHE_PLATFORM_STATE_ACTIVE;
+
+       return 0;
+}
+
+static int fw_flashing_seq(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
+       int ret;
+
+       if (apb->init_disabled ||
+                       apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
+               return 0;
+
+       ret = regulator_enable(apb->vcore);
+       if (ret) {
+               dev_err(dev, "failed to enable core regulator\n");
+               return ret;
+       }
+
+       ret = regulator_enable(apb->vio);
+       if (ret) {
+               dev_err(dev, "failed to enable IO regulator\n");
+               return ret;
+       }
+
+       if (gpio_is_valid(apb->spi_en_gpio)) {
+               unsigned long flags;
+
+               if (apb->spi_en_polarity_high)
+                       flags = GPIOF_OUT_INIT_HIGH;
+               else
+                       flags = GPIOF_OUT_INIT_LOW;
+
+               ret = devm_gpio_request_one(dev, apb->spi_en_gpio,
+                               flags, "apb_spi_en");
+               if (ret) {
+                       dev_err(dev, "Failed requesting SPI bus en gpio %d\n",
+                               apb->spi_en_gpio);
+                       return ret;
+               }
+       }
+
+       /* for flashing device should be in reset state */
+       assert_reset(apb->resetn_gpio);
+       apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
+
+       return 0;
+}
+
+static int standby_boot_seq(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
+
+       if (apb->init_disabled)
+               return 0;
+
+       /* Even if it is in OFF state, then we do not want to change the state */
+       if (apb->state == ARCHE_PLATFORM_STATE_STANDBY ||
+                       apb->state == ARCHE_PLATFORM_STATE_OFF)
+               return 0;
+
+       if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
+                       gpio_is_valid(apb->spi_en_gpio))
+               devm_gpio_free(dev, apb->spi_en_gpio);
+
+       /*
+        * As per WDM spec, do nothing
+        *
+        * Pasted from WDM spec,
+        *  - A falling edge on POWEROFF_L is detected (a)
+        *  - WDM enters standby mode, but no output signals are changed
+        * */
+
+       /* TODO: POWEROFF_L is input to WDM module  */
+       apb->state = ARCHE_PLATFORM_STATE_STANDBY;
+       return 0;
+}
+
+static void poweroff_seq(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
+
+       if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF)
+               return;
+
+       if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
+                       gpio_is_valid(apb->spi_en_gpio))
+               devm_gpio_free(dev, apb->spi_en_gpio);
+
+       /* disable the clock */
+       if (gpio_is_valid(apb->clk_en_gpio))
+               gpio_set_value(apb->clk_en_gpio, 0);
+
+       if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0)
+               regulator_disable(apb->vcore);
+
+       if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0)
+               regulator_disable(apb->vio);
+
+       /* As part of exit, put APB back in reset state */
+       assert_reset(apb->resetn_gpio);
+       apb->state = ARCHE_PLATFORM_STATE_OFF;
+
+       /* TODO: May have to send an event to SVC about this exit */
+}
+
+void apb_bootret_assert(struct device *dev)
+{
+       struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
+
+       gpio_set_value(apb->boot_ret_gpio, 1);
+}
+
+void apb_bootret_deassert(struct device *dev)
+{
+       struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
+
+       gpio_set_value(apb->boot_ret_gpio, 0);
+}
+
+int apb_ctrl_coldboot(struct device *dev)
+{
+       return coldboot_seq(to_platform_device(dev));
+}
+
+int apb_ctrl_fw_flashing(struct device *dev)
+{
+       return fw_flashing_seq(to_platform_device(dev));
+}
+
+int apb_ctrl_standby_boot(struct device *dev)
+{
+       return standby_boot_seq(to_platform_device(dev));
+}
+
+void apb_ctrl_poweroff(struct device *dev)
+{
+       poweroff_seq(to_platform_device(dev));
+}
+
+static ssize_t state_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
+       int ret = 0;
+       bool is_disabled;
+
+       if (sysfs_streq(buf, "off")) {
+               if (apb->state == ARCHE_PLATFORM_STATE_OFF)
+                       return count;
+
+               poweroff_seq(pdev);
+       } else if (sysfs_streq(buf, "active")) {
+               if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE)
+                       return count;
+
+               poweroff_seq(pdev);
+               is_disabled = apb->init_disabled;
+               apb->init_disabled = false;
+               ret = coldboot_seq(pdev);
+               if (ret)
+                       apb->init_disabled = is_disabled;
+       } else if (sysfs_streq(buf, "standby")) {
+               if (apb->state == ARCHE_PLATFORM_STATE_STANDBY)
+                       return count;
+
+               ret = standby_boot_seq(pdev);
+       } else if (sysfs_streq(buf, "fw_flashing")) {
+               if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
+                       return count;
+
+               /* First we want to make sure we power off everything
+                * and then enter FW flashing state */
+               poweroff_seq(pdev);
+               ret = fw_flashing_seq(pdev);
+       } else {
+               dev_err(dev, "unknown state\n");
+               ret = -EINVAL;
+       }
+
+       return ret ? ret : count;
+}
+
+static ssize_t state_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
+
+       switch (apb->state) {
+       case ARCHE_PLATFORM_STATE_OFF:
+               return sprintf(buf, "off%s\n",
+                               apb->init_disabled ? ",disabled" : "");
+       case ARCHE_PLATFORM_STATE_ACTIVE:
+               return sprintf(buf, "active\n");
+       case ARCHE_PLATFORM_STATE_STANDBY:
+               return sprintf(buf, "standby\n");
+       case ARCHE_PLATFORM_STATE_FW_FLASHING:
+               return sprintf(buf, "fw_flashing\n");
+       default:
+               return sprintf(buf, "unknown state\n");
+       }
+}
+
+static DEVICE_ATTR_RW(state);
+
+static int apb_ctrl_get_devtree_data(struct platform_device *pdev,
+               struct arche_apb_ctrl_drvdata *apb)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       int ret;
+
+       apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+       if (apb->resetn_gpio < 0) {
+               dev_err(dev, "failed to get reset gpio\n");
+               return apb->resetn_gpio;
+       }
+       ret = devm_gpio_request_one(dev, apb->resetn_gpio,
+                       GPIOF_OUT_INIT_LOW, "apb-reset");
+       if (ret) {
+               dev_err(dev, "Failed requesting reset gpio %d\n",
+                               apb->resetn_gpio);
+               return ret;
+       }
+
+       apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0);
+       if (apb->boot_ret_gpio < 0) {
+               dev_err(dev, "failed to get boot retention gpio\n");
+               return apb->boot_ret_gpio;
+       }
+       ret = devm_gpio_request_one(dev, apb->boot_ret_gpio,
+                       GPIOF_OUT_INIT_LOW, "boot retention");
+       if (ret) {
+               dev_err(dev, "Failed requesting bootret gpio %d\n",
+                               apb->boot_ret_gpio);
+               return ret;
+       }
+
+       /* It's not mandatory to support power management interface */
+       apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0);
+       if (apb->pwroff_gpio < 0) {
+               dev_err(dev, "failed to get power off gpio\n");
+               return apb->pwroff_gpio;
+       }
+       ret = devm_gpio_request_one(dev, apb->pwroff_gpio,
+                       GPIOF_IN, "pwroff_n");
+       if (ret) {
+               dev_err(dev, "Failed requesting pwroff_n gpio %d\n",
+                               apb->pwroff_gpio);
+               return ret;
+       }
+
+       /* Do not make clock mandatory as of now (for DB3) */
+       apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0);
+       if (apb->clk_en_gpio < 0) {
+               dev_warn(dev, "failed to get clock en gpio\n");
+       } else if (gpio_is_valid(apb->clk_en_gpio)) {
+               ret = devm_gpio_request_one(dev, apb->clk_en_gpio,
+                               GPIOF_OUT_INIT_LOW, "apb_clk_en");
+               if (ret) {
+                       dev_warn(dev, "Failed requesting APB clock en gpio %d\n",
+                                       apb->clk_en_gpio);
+                       return ret;
+               }
+       }
+
+       apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0);
+       if (apb->pwrdn_gpio < 0)
+               dev_warn(dev, "failed to get power down gpio\n");
+
+       /* Regulators are optional, as we may have fixed supply coming in */
+       apb->vcore = devm_regulator_get(dev, "vcore");
+       if (IS_ERR(apb->vcore))
+               dev_warn(dev, "no core regulator found\n");
+
+       apb->vio = devm_regulator_get(dev, "vio");
+       if (IS_ERR(apb->vio))
+               dev_warn(dev, "no IO regulator found\n");
+
+       apb->pinctrl = devm_pinctrl_get(&pdev->dev);
+       if (IS_ERR(apb->pinctrl)) {
+               dev_err(&pdev->dev, "could not get pinctrl handle\n");
+               return PTR_ERR(apb->pinctrl);
+       }
+       apb->pin_default = pinctrl_lookup_state(apb->pinctrl, "default");
+       if (IS_ERR(apb->pin_default)) {
+               dev_err(&pdev->dev, "could not get default pin state\n");
+               return PTR_ERR(apb->pin_default);
+       }
+
+       /* Only applicable for platform >= V2 */
+       apb->spi_en_gpio = of_get_named_gpio(np, "spi-en-gpio", 0);
+       if (apb->spi_en_gpio >= 0) {
+               if (of_property_read_bool(pdev->dev.of_node,
+                                       "spi-en-active-high"))
+                       apb->spi_en_polarity_high = true;
+       }
+
+       return 0;
+}
+
+static int arche_apb_ctrl_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct arche_apb_ctrl_drvdata *apb;
+       struct device *dev = &pdev->dev;
+
+       apb = devm_kzalloc(&pdev->dev, sizeof(*apb), GFP_KERNEL);
+       if (!apb)
+               return -ENOMEM;
+
+       ret = apb_ctrl_get_devtree_data(pdev, apb);
+       if (ret) {
+               dev_err(dev, "failed to get apb devicetree data %d\n", ret);
+               return ret;
+       }
+
+       /* Initially set APB to OFF state */
+       apb->state = ARCHE_PLATFORM_STATE_OFF;
+       /* Check whether device needs to be enabled on boot */
+       if (of_property_read_bool(pdev->dev.of_node, "arche,init-disable"))
+               apb->init_disabled = true;
+
+       platform_set_drvdata(pdev, apb);
+
+       /* Create sysfs interface to allow user to change state dynamically */
+       ret = device_create_file(dev, &dev_attr_state);
+       if (ret) {
+               dev_err(dev, "failed to create state file in sysfs\n");
+               return ret;
+       }
+
+       dev_info(&pdev->dev, "Device registered successfully\n");
+       return 0;
+}
+
+static int arche_apb_ctrl_remove(struct platform_device *pdev)
+{
+       device_remove_file(&pdev->dev, &dev_attr_state);
+       poweroff_seq(pdev);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static int arche_apb_ctrl_suspend(struct device *dev)
+{
+       /*
+        * If timing profile permits, we may shutdown bridge
+        * completely
+        *
+        * TODO: sequence ??
+        *
+        * Also, need to make sure we meet precondition for unipro suspend
+        * Precondition: Definition ???
+        */
+       return 0;
+}
+
+static int arche_apb_ctrl_resume(struct device *dev)
+{
+       /*
+        * Atleast for ES2 we have to meet the delay requirement between
+        * unipro switch and AP bridge init, depending on whether bridge is in
+        * OFF state or standby state.
+        *
+        * Based on whether bridge is in standby or OFF state we may have to
+        * assert multiple signals. Please refer to WDM spec, for more info.
+        *
+        */
+       return 0;
+}
+
+static void arche_apb_ctrl_shutdown(struct platform_device *pdev)
+{
+       apb_ctrl_poweroff(&pdev->dev);
+}
+
+static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend,
+                        arche_apb_ctrl_resume);
+
+static struct of_device_id arche_apb_ctrl_of_match[] = {
+       { .compatible = "usbffff,2", },
+       { },
+};
+
+static struct platform_driver arche_apb_ctrl_device_driver = {
+       .probe          = arche_apb_ctrl_probe,
+       .remove         = arche_apb_ctrl_remove,
+       .shutdown       = arche_apb_ctrl_shutdown,
+       .driver         = {
+               .name   = "arche-apb-ctrl",
+               .pm     = &arche_apb_ctrl_pm_ops,
+               .of_match_table = arche_apb_ctrl_of_match,
+       }
+};
+
+int __init arche_apb_init(void)
+{
+       return platform_driver_register(&arche_apb_ctrl_device_driver);
+}
+
+void __exit arche_apb_exit(void)
+{
+       platform_driver_unregister(&arche_apb_ctrl_device_driver);
+}
diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c
new file mode 100644 (file)
index 0000000..9d9048e
--- /dev/null
@@ -0,0 +1,828 @@
+/*
+ * Arche Platform driver to enable Unipro link.
+ *
+ * Copyright 2014-2015 Google Inc.
+ * Copyright 2014-2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/suspend.h>
+#include <linux/time.h>
+#include "arche_platform.h"
+#include "greybus.h"
+
+#include <linux/usb/usb3613.h>
+
+#define WD_COLDBOOT_PULSE_WIDTH_MS     30
+
+enum svc_wakedetect_state {
+       WD_STATE_IDLE,                  /* Default state = pulled high/low */
+       WD_STATE_BOOT_INIT,             /* WD = falling edge (low) */
+       WD_STATE_COLDBOOT_TRIG,         /* WD = rising edge (high), > 30msec */
+       WD_STATE_STANDBYBOOT_TRIG,      /* As of now not used ?? */
+       WD_STATE_COLDBOOT_START,        /* Cold boot process started */
+       WD_STATE_STANDBYBOOT_START,     /* Not used */
+       WD_STATE_TIMESYNC,
+};
+
+struct arche_platform_drvdata {
+       /* Control GPIO signals to and from AP <=> SVC */
+       int svc_reset_gpio;
+       bool is_reset_act_hi;
+       int svc_sysboot_gpio;
+       int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
+
+       enum arche_platform_state state;
+
+       int svc_refclk_req;
+       struct clk *svc_ref_clk;
+
+       struct pinctrl *pinctrl;
+       struct pinctrl_state *pin_default;
+
+       int num_apbs;
+
+       enum svc_wakedetect_state wake_detect_state;
+       int wake_detect_irq;
+       spinlock_t wake_lock;                   /* Protect wake_detect_state */
+       struct mutex platform_state_mutex;      /* Protect state */
+       wait_queue_head_t wq;                   /* WQ for arche_pdata->state */
+       unsigned long wake_detect_start;
+       struct notifier_block pm_notifier;
+
+       struct device *dev;
+       struct gb_timesync_svc *timesync_svc_pdata;
+};
+
+static int arche_apb_bootret_assert(struct device *dev, void *data)
+{
+       apb_bootret_assert(dev);
+       return 0;
+}
+
+static int arche_apb_bootret_deassert(struct device *dev, void *data)
+{
+       apb_bootret_deassert(dev);
+       return 0;
+}
+
+/* Requires calling context to hold arche_pdata->platform_state_mutex */
+static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
+                                    enum arche_platform_state state)
+{
+       arche_pdata->state = state;
+}
+
+/*
+ * arche_platform_change_state: Change the operational state
+ *
+ * This exported function allows external drivers to change the state
+ * of the arche-platform driver.
+ * Note that this function only supports transitions between two states
+ * with limited functionality.
+ *
+ *  - ARCHE_PLATFORM_STATE_TIME_SYNC:
+ *    Once set, allows timesync operations between SVC <=> AP and makes
+ *    sure that arche-platform driver ignores any subsequent events/pulses
+ *    from SVC over wake/detect.
+ *
+ *  - ARCHE_PLATFORM_STATE_ACTIVE:
+ *    Puts back driver to active state, where any pulse from SVC on wake/detect
+ *    line would trigger either cold/standby boot.
+ *    Note: Transition request from this function does not trigger cold/standby
+ *          boot. It just puts back driver book keeping variable back to ACTIVE
+ *          state and restores the interrupt.
+ *
+ * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently
+ * satisfy the requested state-transition or -EINVAL for all other
+ * state-transition requests.
+ */
+int arche_platform_change_state(enum arche_platform_state state,
+                               struct gb_timesync_svc *timesync_svc_pdata)
+{
+       struct arche_platform_drvdata *arche_pdata;
+       struct platform_device *pdev;
+       struct device_node *np;
+       int ret = -EAGAIN;
+       unsigned long flags;
+
+       np = of_find_compatible_node(NULL, NULL, "google,arche-platform");
+       if (!np) {
+               pr_err("google,arche-platform device node not found\n");
+               return -ENODEV;
+       }
+
+       pdev = of_find_device_by_node(np);
+       if (!pdev) {
+               pr_err("arche-platform device not found\n");
+               return -ENODEV;
+       }
+
+       arche_pdata = platform_get_drvdata(pdev);
+
+       mutex_lock(&arche_pdata->platform_state_mutex);
+       spin_lock_irqsave(&arche_pdata->wake_lock, flags);
+
+       if (arche_pdata->state == state) {
+               ret = 0;
+               goto exit;
+       }
+
+       switch (state) {
+       case ARCHE_PLATFORM_STATE_TIME_SYNC:
+               if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
+                       dev_err(arche_pdata->dev,
+                               "driver busy with wake/detect line ops\n");
+                       goto  exit;
+               }
+               device_for_each_child(arche_pdata->dev, NULL,
+                                     arche_apb_bootret_assert);
+               arche_pdata->wake_detect_state = WD_STATE_TIMESYNC;
+               break;
+       case ARCHE_PLATFORM_STATE_ACTIVE:
+               if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) {
+                       ret = -EINVAL;
+                       goto exit;
+               }
+               device_for_each_child(arche_pdata->dev, NULL,
+                                     arche_apb_bootret_deassert);
+               arche_pdata->wake_detect_state = WD_STATE_IDLE;
+               break;
+       case ARCHE_PLATFORM_STATE_OFF:
+       case ARCHE_PLATFORM_STATE_STANDBY:
+       case ARCHE_PLATFORM_STATE_FW_FLASHING:
+               dev_err(arche_pdata->dev, "busy, request to retry later\n");
+               goto exit;
+       default:
+               ret = -EINVAL;
+               dev_err(arche_pdata->dev,
+                       "invalid state transition request\n");
+               goto exit;
+       }
+       arche_pdata->timesync_svc_pdata = timesync_svc_pdata;
+       arche_platform_set_state(arche_pdata, state);
+       if (state == ARCHE_PLATFORM_STATE_ACTIVE)
+               wake_up(&arche_pdata->wq);
+
+       ret = 0;
+exit:
+       spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
+       mutex_unlock(&arche_pdata->platform_state_mutex);
+       of_node_put(np);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(arche_platform_change_state);
+
+/* Requires arche_pdata->wake_lock is held by calling context */
+static void arche_platform_set_wake_detect_state(
+                               struct arche_platform_drvdata *arche_pdata,
+                               enum svc_wakedetect_state state)
+{
+       arche_pdata->wake_detect_state = state;
+}
+
+static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
+{
+       gpio_set_value(gpio, onoff);
+}
+
+static int apb_cold_boot(struct device *dev, void *data)
+{
+       int ret;
+
+       ret = apb_ctrl_coldboot(dev);
+       if (ret)
+               dev_warn(dev, "failed to coldboot\n");
+
+       /*Child nodes are independent, so do not exit coldboot operation */
+       return 0;
+}
+
+static int apb_poweroff(struct device *dev, void *data)
+{
+       apb_ctrl_poweroff(dev);
+
+       /* Enable HUB3613 into HUB mode. */
+       if (usb3613_hub_mode_ctrl(false))
+               dev_warn(dev, "failed to control hub device\n");
+
+       return 0;
+}
+
+static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata)
+{
+       /* Enable interrupt here, to read event back from SVC */
+       gpio_direction_input(arche_pdata->wake_detect_gpio);
+       enable_irq(arche_pdata->wake_detect_irq);
+}
+
+static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
+{
+       struct arche_platform_drvdata *arche_pdata = devid;
+       unsigned long flags;
+
+       spin_lock_irqsave(&arche_pdata->wake_lock, flags);
+       if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
+               /* Something is wrong */
+               spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
+               return IRQ_HANDLED;
+       }
+
+       arche_platform_set_wake_detect_state(arche_pdata,
+                                            WD_STATE_COLDBOOT_START);
+       spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
+
+       /* It should complete power cycle, so first make sure it is poweroff */
+       device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
+
+       /* Bring APB out of reset: cold boot sequence */
+       device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
+
+       /* Enable HUB3613 into HUB mode. */
+       if (usb3613_hub_mode_ctrl(true))
+               dev_warn(arche_pdata->dev, "failed to control hub device\n");
+
+       spin_lock_irqsave(&arche_pdata->wake_lock, flags);
+       arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
+       spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
+{
+       struct arche_platform_drvdata *arche_pdata = devid;
+       unsigned long flags;
+
+       spin_lock_irqsave(&arche_pdata->wake_lock, flags);
+
+       if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) {
+               gb_timesync_irq(arche_pdata->timesync_svc_pdata);
+               goto exit;
+       }
+
+       if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
+               /* wake/detect rising */
+
+               /*
+                * If wake/detect line goes high after low, within less than
+                * 30msec, then standby boot sequence is initiated, which is not
+                * supported/implemented as of now. So ignore it.
+                */
+               if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
+                       if (time_before(jiffies,
+                                       arche_pdata->wake_detect_start +
+                                       msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
+                               arche_platform_set_wake_detect_state(arche_pdata,
+                                                                    WD_STATE_IDLE);
+                       } else {
+                               /* Check we are not in middle of irq thread already */
+                               if (arche_pdata->wake_detect_state !=
+                                               WD_STATE_COLDBOOT_START) {
+                                       arche_platform_set_wake_detect_state(arche_pdata,
+                                                                            WD_STATE_COLDBOOT_TRIG);
+                                       spin_unlock_irqrestore(
+                                               &arche_pdata->wake_lock,
+                                               flags);
+                                       return IRQ_WAKE_THREAD;
+                               }
+                       }
+               }
+       } else {
+               /* wake/detect falling */
+               if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
+                       arche_pdata->wake_detect_start = jiffies;
+                       /*
+                        * In the begining, when wake/detect goes low (first time), we assume
+                        * it is meant for coldboot and set the flag. If wake/detect line stays low
+                        * beyond 30msec, then it is coldboot else fallback to standby boot.
+                        */
+                       arche_platform_set_wake_detect_state(arche_pdata,
+                                                            WD_STATE_BOOT_INIT);
+               }
+       }
+
+exit:
+       spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Requires arche_pdata->platform_state_mutex to be held
+ */
+static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
+{
+       int ret;
+
+       if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
+               return 0;
+
+       dev_info(arche_pdata->dev, "Booting from cold boot state\n");
+
+       svc_reset_onoff(arche_pdata->svc_reset_gpio,
+                       arche_pdata->is_reset_act_hi);
+
+       gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
+       usleep_range(100, 200);
+
+       ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
+       if (ret) {
+               dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
+                               ret);
+               return ret;
+       }
+
+       /* bring SVC out of reset */
+       svc_reset_onoff(arche_pdata->svc_reset_gpio,
+                       !arche_pdata->is_reset_act_hi);
+
+       arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
+
+       return 0;
+}
+
+/*
+ * Requires arche_pdata->platform_state_mutex to be held
+ */
+static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
+{
+       int ret;
+
+       if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
+               return 0;
+
+       dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
+
+       svc_reset_onoff(arche_pdata->svc_reset_gpio,
+                       arche_pdata->is_reset_act_hi);
+
+       gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
+
+       usleep_range(100, 200);
+
+       ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
+       if (ret) {
+               dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
+                               ret);
+               return ret;
+       }
+
+       svc_reset_onoff(arche_pdata->svc_reset_gpio,
+                       !arche_pdata->is_reset_act_hi);
+
+       arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
+
+       return 0;
+}
+
+/*
+ * Requires arche_pdata->platform_state_mutex to be held
+ */
+static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
+{
+       unsigned long flags;
+
+       if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
+               return;
+
+       /* If in fw_flashing mode, then no need to repeate things again */
+       if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
+               disable_irq(arche_pdata->wake_detect_irq);
+
+               spin_lock_irqsave(&arche_pdata->wake_lock, flags);
+               arche_platform_set_wake_detect_state(arche_pdata,
+                                                    WD_STATE_IDLE);
+               spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
+       }
+
+       clk_disable_unprepare(arche_pdata->svc_ref_clk);
+
+       /* As part of exit, put APB back in reset state */
+       svc_reset_onoff(arche_pdata->svc_reset_gpio,
+                       arche_pdata->is_reset_act_hi);
+
+       arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
+}
+
+static ssize_t state_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
+       int ret = 0;
+
+retry:
+       mutex_lock(&arche_pdata->platform_state_mutex);
+       if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) {
+               mutex_unlock(&arche_pdata->platform_state_mutex);
+               ret = wait_event_interruptible(
+                       arche_pdata->wq,
+                       arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC);
+               if (ret)
+                       return ret;
+               goto retry;
+       }
+
+       if (sysfs_streq(buf, "off")) {
+               if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
+                       goto exit;
+
+               /*  If SVC goes down, bring down APB's as well */
+               device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
+
+               arche_platform_poweroff_seq(arche_pdata);
+
+       } else if (sysfs_streq(buf, "active")) {
+               if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
+                       goto exit;
+
+               /* First we want to make sure we power off everything
+                * and then activate back again */
+               device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
+               arche_platform_poweroff_seq(arche_pdata);
+
+               arche_platform_wd_irq_en(arche_pdata);
+               ret = arche_platform_coldboot_seq(arche_pdata);
+               if (ret)
+                       goto exit;
+
+       } else if (sysfs_streq(buf, "standby")) {
+               if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
+                       goto exit;
+
+               dev_warn(arche_pdata->dev, "standby state not supported\n");
+       } else if (sysfs_streq(buf, "fw_flashing")) {
+               if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
+                       goto exit;
+
+               /*
+                * Here we only control SVC.
+                *
+                * In case of FW_FLASHING mode we do not want to control
+                * APBs, as in case of V2, SPI bus is shared between both
+                * the APBs. So let user chose which APB he wants to flash.
+                */
+               arche_platform_poweroff_seq(arche_pdata);
+
+               ret = arche_platform_fw_flashing_seq(arche_pdata);
+               if (ret)
+                       goto exit;
+       } else {
+               dev_err(arche_pdata->dev, "unknown state\n");
+               ret = -EINVAL;
+       }
+
+exit:
+       mutex_unlock(&arche_pdata->platform_state_mutex);
+       return ret ? ret : count;
+}
+
+static ssize_t state_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
+
+       switch (arche_pdata->state) {
+       case ARCHE_PLATFORM_STATE_OFF:
+               return sprintf(buf, "off\n");
+       case ARCHE_PLATFORM_STATE_ACTIVE:
+               return sprintf(buf, "active\n");
+       case ARCHE_PLATFORM_STATE_STANDBY:
+               return sprintf(buf, "standby\n");
+       case ARCHE_PLATFORM_STATE_FW_FLASHING:
+               return sprintf(buf, "fw_flashing\n");
+       case ARCHE_PLATFORM_STATE_TIME_SYNC:
+               return sprintf(buf, "time_sync\n");
+       default:
+               return sprintf(buf, "unknown state\n");
+       }
+}
+
+static DEVICE_ATTR_RW(state);
+
+static int arche_platform_pm_notifier(struct notifier_block *notifier,
+                                     unsigned long pm_event, void *unused)
+{
+       struct arche_platform_drvdata *arche_pdata =
+               container_of(notifier, struct arche_platform_drvdata,
+                            pm_notifier);
+       int ret = NOTIFY_DONE;
+
+       mutex_lock(&arche_pdata->platform_state_mutex);
+       switch (pm_event) {
+       case PM_SUSPEND_PREPARE:
+               if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
+                       ret = NOTIFY_STOP;
+                       break;
+               }
+               device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
+               arche_platform_poweroff_seq(arche_pdata);
+               break;
+       case PM_POST_SUSPEND:
+               if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF)
+                       break;
+
+               arche_platform_wd_irq_en(arche_pdata);
+               arche_platform_coldboot_seq(arche_pdata);
+               break;
+       default:
+               break;
+       }
+       mutex_unlock(&arche_pdata->platform_state_mutex);
+
+       return ret;
+}
+
+static int arche_platform_probe(struct platform_device *pdev)
+{
+       struct arche_platform_drvdata *arche_pdata;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       int ret;
+
+       arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
+       if (!arche_pdata)
+               return -ENOMEM;
+
+       /* setup svc reset gpio */
+       arche_pdata->is_reset_act_hi = of_property_read_bool(np,
+                                       "svc,reset-active-high");
+       arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
+       if (arche_pdata->svc_reset_gpio < 0) {
+               dev_err(dev, "failed to get reset-gpio\n");
+               return arche_pdata->svc_reset_gpio;
+       }
+       ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
+       if (ret) {
+               dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
+               return ret;
+       }
+       ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
+                                       arche_pdata->is_reset_act_hi);
+       if (ret) {
+               dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
+               return ret;
+       }
+       arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
+
+       arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
+                                       "svc,sysboot-gpio", 0);
+       if (arche_pdata->svc_sysboot_gpio < 0) {
+               dev_err(dev, "failed to get sysboot gpio\n");
+               return arche_pdata->svc_sysboot_gpio;
+       }
+       ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
+       if (ret) {
+               dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
+               return ret;
+       }
+       ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
+       if (ret) {
+               dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
+               return ret;
+       }
+
+       /* setup the clock request gpio first */
+       arche_pdata->svc_refclk_req = of_get_named_gpio(np,
+                                       "svc,refclk-req-gpio", 0);
+       if (arche_pdata->svc_refclk_req < 0) {
+               dev_err(dev, "failed to get svc clock-req gpio\n");
+               return arche_pdata->svc_refclk_req;
+       }
+       ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
+       if (ret) {
+               dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
+               return ret;
+       }
+       ret = gpio_direction_input(arche_pdata->svc_refclk_req);
+       if (ret) {
+               dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
+               return ret;
+       }
+
+       /* setup refclk2 to follow the pin */
+       arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
+       if (IS_ERR(arche_pdata->svc_ref_clk)) {
+               ret = PTR_ERR(arche_pdata->svc_ref_clk);
+               dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, arche_pdata);
+
+       arche_pdata->num_apbs = of_get_child_count(np);
+       dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
+
+       arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
+       if (arche_pdata->wake_detect_gpio < 0) {
+               dev_err(dev, "failed to get wake detect gpio\n");
+               ret = arche_pdata->wake_detect_gpio;
+               return ret;
+       }
+
+       ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
+       if (ret) {
+               dev_err(dev, "Failed requesting wake_detect gpio %d\n",
+                               arche_pdata->wake_detect_gpio);
+               return ret;
+       }
+
+       arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
+
+       arche_pdata->dev = &pdev->dev;
+
+       spin_lock_init(&arche_pdata->wake_lock);
+       mutex_init(&arche_pdata->platform_state_mutex);
+       init_waitqueue_head(&arche_pdata->wq);
+       arche_pdata->wake_detect_irq =
+               gpio_to_irq(arche_pdata->wake_detect_gpio);
+
+       ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
+                       arche_platform_wd_irq,
+                       arche_platform_wd_irq_thread,
+                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                       dev_name(dev), arche_pdata);
+       if (ret) {
+               dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
+               return ret;
+       }
+       disable_irq(arche_pdata->wake_detect_irq);
+
+       ret = device_create_file(dev, &dev_attr_state);
+       if (ret) {
+               dev_err(dev, "failed to create state file in sysfs\n");
+               return ret;
+       }
+
+       ret = of_platform_populate(np, NULL, NULL, dev);
+       if (ret) {
+               dev_err(dev, "failed to populate child nodes %d\n", ret);
+               goto err_device_remove;
+       }
+
+       arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
+       ret = register_pm_notifier(&arche_pdata->pm_notifier);
+
+       if (ret) {
+               dev_err(dev, "failed to register pm notifier %d\n", ret);
+               goto err_device_remove;
+       }
+
+       /* Register callback pointer */
+       arche_platform_change_state_cb = arche_platform_change_state;
+
+       /* Explicitly power off if requested */
+       if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) {
+               mutex_lock(&arche_pdata->platform_state_mutex);
+               ret = arche_platform_coldboot_seq(arche_pdata);
+               if (ret) {
+                       dev_err(dev, "Failed to cold boot svc %d\n", ret);
+                       goto err_coldboot;
+               }
+               arche_platform_wd_irq_en(arche_pdata);
+               mutex_unlock(&arche_pdata->platform_state_mutex);
+       }
+
+       dev_info(dev, "Device registered successfully\n");
+       return 0;
+
+err_coldboot:
+       mutex_unlock(&arche_pdata->platform_state_mutex);
+err_device_remove:
+       device_remove_file(&pdev->dev, &dev_attr_state);
+       return ret;
+}
+
+static int arche_remove_child(struct device *dev, void *unused)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       platform_device_unregister(pdev);
+
+       return 0;
+}
+
+static int arche_platform_remove(struct platform_device *pdev)
+{
+       struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
+
+       unregister_pm_notifier(&arche_pdata->pm_notifier);
+       device_remove_file(&pdev->dev, &dev_attr_state);
+       device_for_each_child(&pdev->dev, NULL, arche_remove_child);
+       arche_platform_poweroff_seq(arche_pdata);
+       platform_set_drvdata(pdev, NULL);
+
+       if (usb3613_hub_mode_ctrl(false))
+               dev_warn(arche_pdata->dev, "failed to control hub device\n");
+               /* TODO: Should we do anything more here ?? */
+       return 0;
+}
+
+static int arche_platform_suspend(struct device *dev)
+{
+       /*
+        * If timing profile premits, we may shutdown bridge
+        * completely
+        *
+        * TODO: sequence ??
+        *
+        * Also, need to make sure we meet precondition for unipro suspend
+        * Precondition: Definition ???
+        */
+       return 0;
+}
+
+static int arche_platform_resume(struct device *dev)
+{
+       /*
+        * Atleast for ES2 we have to meet the delay requirement between
+        * unipro switch and AP bridge init, depending on whether bridge is in
+        * OFF state or standby state.
+        *
+        * Based on whether bridge is in standby or OFF state we may have to
+        * assert multiple signals. Please refer to WDM spec, for more info.
+        *
+        */
+       return 0;
+}
+
+static void arche_platform_shutdown(struct platform_device *pdev)
+{
+       struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
+
+       arche_platform_poweroff_seq(arche_pdata);
+
+       usb3613_hub_mode_ctrl(false);
+}
+
+static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
+                       arche_platform_suspend,
+                       arche_platform_resume);
+
+static struct of_device_id arche_platform_of_match[] = {
+       { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
+       { },
+};
+
+static struct of_device_id arche_combined_id[] = {
+       { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
+       { .compatible = "usbffff,2", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, arche_combined_id);
+
+static struct platform_driver arche_platform_device_driver = {
+       .probe          = arche_platform_probe,
+       .remove         = arche_platform_remove,
+       .shutdown       = arche_platform_shutdown,
+       .driver         = {
+               .name   = "arche-platform-ctrl",
+               .pm     = &arche_platform_pm_ops,
+               .of_match_table = arche_platform_of_match,
+       }
+};
+
+static int __init arche_init(void)
+{
+       int retval;
+
+       retval = platform_driver_register(&arche_platform_device_driver);
+       if (retval)
+               return retval;
+
+       retval = arche_apb_init();
+       if (retval)
+               platform_driver_unregister(&arche_platform_device_driver);
+
+       return retval;
+}
+module_init(arche_init);
+
+static void __exit arche_exit(void)
+{
+       arche_apb_exit();
+       platform_driver_unregister(&arche_platform_device_driver);
+}
+module_exit(arche_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
+MODULE_DESCRIPTION("Arche Platform Driver");
diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h
new file mode 100644 (file)
index 0000000..bd12345
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Arche Platform driver to enable Unipro link.
+ *
+ * Copyright 2015-2016 Google Inc.
+ * Copyright 2015-2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef __ARCHE_PLATFORM_H
+#define __ARCHE_PLATFORM_H
+
+#include "timesync.h"
+
+enum arche_platform_state {
+       ARCHE_PLATFORM_STATE_OFF,
+       ARCHE_PLATFORM_STATE_ACTIVE,
+       ARCHE_PLATFORM_STATE_STANDBY,
+       ARCHE_PLATFORM_STATE_FW_FLASHING,
+       ARCHE_PLATFORM_STATE_TIME_SYNC,
+};
+
+int arche_platform_change_state(enum arche_platform_state state,
+                               struct gb_timesync_svc *pdata);
+
+extern int (*arche_platform_change_state_cb)(enum arche_platform_state state,
+                                            struct gb_timesync_svc *pdata);
+int __init arche_apb_init(void);
+void __exit arche_apb_exit(void);
+
+/* Operational states for the APB device */
+int apb_ctrl_coldboot(struct device *dev);
+int apb_ctrl_fw_flashing(struct device *dev);
+int apb_ctrl_standby_boot(struct device *dev);
+void apb_ctrl_poweroff(struct device *dev);
+void apb_bootret_assert(struct device *dev);
+void apb_bootret_deassert(struct device *dev);
+
+#endif /* __ARCHE_PLATFORM_H */
diff --git a/drivers/staging/greybus/arpc.h b/drivers/staging/greybus/arpc.h
new file mode 100644 (file)
index 0000000..7fbddfc
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2016 Google Inc. All rights reserved.
+ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License version 2 for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Google Inc. All rights reserved.
+ * Copyright(c) 2016 Linaro Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google Inc. or Linaro Ltd. nor the names of
+ *    its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
+ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ARPC_H
+#define __ARPC_H
+
+/* APBridgeA RPC (ARPC) */
+
+enum arpc_result {
+       ARPC_SUCCESS            = 0x00,
+       ARPC_NO_MEMORY          = 0x01,
+       ARPC_INVALID            = 0x02,
+       ARPC_TIMEOUT            = 0x03,
+       ARPC_UNKNOWN_ERROR      = 0xff,
+};
+
+struct arpc_request_message {
+       __le16  id;             /* RPC unique id */
+       __le16  size;           /* Size in bytes of header + payload */
+       __u8    type;           /* RPC type */
+       __u8    data[0];        /* ARPC data */
+} __packed;
+
+struct arpc_response_message {
+       __le16  id;             /* RPC unique id */
+       __u8    result;         /* Result of RPC */
+} __packed;
+
+
+/* ARPC requests */
+#define ARPC_TYPE_CPORT_CONNECTED              0x01
+#define ARPC_TYPE_CPORT_QUIESCE                        0x02
+#define ARPC_TYPE_CPORT_CLEAR                  0x03
+#define ARPC_TYPE_CPORT_FLUSH                  0x04
+#define ARPC_TYPE_CPORT_SHUTDOWN               0x05
+
+struct arpc_cport_connected_req {
+       __le16 cport_id;
+} __packed;
+
+struct arpc_cport_quiesce_req {
+       __le16 cport_id;
+       __le16 peer_space;
+       __le16 timeout;
+} __packed;
+
+struct arpc_cport_clear_req {
+       __le16 cport_id;
+} __packed;
+
+struct arpc_cport_flush_req {
+       __le16 cport_id;
+} __packed;
+
+struct arpc_cport_shutdown_req {
+       __le16 cport_id;
+       __le16 timeout;
+       __u8 phase;
+} __packed;
+
+#endif /* __ARPC_H */
diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c
new file mode 100644 (file)
index 0000000..1b4252d
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Greybus Audio Device Class Protocol helpers
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include "greybus.h"
+#include "greybus_protocols.h"
+#include "audio_apbridgea.h"
+#include "audio_codec.h"
+
+int gb_audio_apbridgea_set_config(struct gb_connection *connection,
+                                 __u16 i2s_port, __u32 format, __u32 rate,
+                                 __u32 mclk_freq)
+{
+       struct audio_apbridgea_set_config_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_CONFIG;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+       req.format = cpu_to_le32(format);
+       req.rate = cpu_to_le32(rate);
+       req.mclk_freq = cpu_to_le32(mclk_freq);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_config);
+
+int gb_audio_apbridgea_register_cport(struct gb_connection *connection,
+                                     __u16 i2s_port, __u16 cportid,
+                                     __u8 direction)
+{
+       struct audio_apbridgea_register_cport_request req;
+       int ret;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+       req.cport = cpu_to_le16(cportid);
+       req.direction = direction;
+
+       ret = gb_pm_runtime_get_sync(connection->bundle);
+       if (ret)
+               return ret;
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_register_cport);
+
+int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection,
+                                       __u16 i2s_port, __u16 cportid,
+                                       __u8 direction)
+{
+       struct audio_apbridgea_unregister_cport_request req;
+       int ret;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+       req.cport = cpu_to_le16(cportid);
+       req.direction = direction;
+
+       ret = gb_hd_output(connection->hd, &req, sizeof(req),
+                          GB_APB_REQUEST_AUDIO_CONTROL, true);
+
+       gb_pm_runtime_put_autosuspend(connection->bundle);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_unregister_cport);
+
+int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection,
+                                       __u16 i2s_port, __u16 size)
+{
+       struct audio_apbridgea_set_tx_data_size_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+       req.size = cpu_to_le16(size);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_tx_data_size);
+
+int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection,
+                                 __u16 i2s_port)
+{
+       struct audio_apbridgea_prepare_tx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_PREPARE_TX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_prepare_tx);
+
+int gb_audio_apbridgea_start_tx(struct gb_connection *connection,
+                               __u16 i2s_port, __u64 timestamp)
+{
+       struct audio_apbridgea_start_tx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_START_TX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+       req.timestamp = cpu_to_le64(timestamp);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_start_tx);
+
+int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, __u16 i2s_port)
+{
+       struct audio_apbridgea_stop_tx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_STOP_TX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_tx);
+
+int gb_audio_apbridgea_shutdown_tx(struct gb_connection *connection,
+                                  __u16 i2s_port)
+{
+       struct audio_apbridgea_shutdown_tx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_SHUTDOWN_TX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_shutdown_tx);
+
+int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection,
+                                       __u16 i2s_port, __u16 size)
+{
+       struct audio_apbridgea_set_rx_data_size_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+       req.size = cpu_to_le16(size);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_rx_data_size);
+
+int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection,
+                                 __u16 i2s_port)
+{
+       struct audio_apbridgea_prepare_rx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_PREPARE_RX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_prepare_rx);
+
+int gb_audio_apbridgea_start_rx(struct gb_connection *connection,
+                               __u16 i2s_port)
+{
+       struct audio_apbridgea_start_rx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_START_RX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_start_rx);
+
+int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, __u16 i2s_port)
+{
+       struct audio_apbridgea_stop_rx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_STOP_RX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_rx);
+
+int gb_audio_apbridgea_shutdown_rx(struct gb_connection *connection,
+                                  __u16 i2s_port)
+{
+       struct audio_apbridgea_shutdown_rx_request req;
+
+       req.hdr.type = AUDIO_APBRIDGEA_TYPE_SHUTDOWN_RX;
+       req.hdr.i2s_port = cpu_to_le16(i2s_port);
+
+       return gb_hd_output(connection->hd, &req, sizeof(req),
+                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+}
+EXPORT_SYMBOL_GPL(gb_audio_apbridgea_shutdown_rx);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("greybus:audio-apbridgea");
+MODULE_DESCRIPTION("Greybus Special APBridgeA Audio Protocol library");
+MODULE_AUTHOR("Mark Greer <mgreer@animalcreek.com>");
diff --git a/drivers/staging/greybus/audio_apbridgea.h b/drivers/staging/greybus/audio_apbridgea.h
new file mode 100644 (file)
index 0000000..b94cb05
--- /dev/null
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2015-2016 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * This is a special protocol for configuring communication over the
+ * I2S bus between the DSP on the MSM8994 and APBridgeA.  Therefore,
+ * we can predefine several low-level attributes of the communication
+ * because we know that they are supported.  In particular, the following
+ * assumptions are made:
+ *     - there are two channels (i.e., stereo)
+ *     - the low-level protocol is I2S as defined by Philips/NXP
+ *     - the DSP on the MSM8994 is the clock master for MCLK, BCLK, and WCLK
+ *     - WCLK changes on the falling edge of BCLK
+ *     - WCLK low for left channel; high for right channel
+ *     - TX data is sent on the falling edge of BCLK
+ *     - RX data is received/latched on the rising edge of BCLK
+ */
+
+#ifndef __AUDIO_APBRIDGEA_H
+#define __AUDIO_APBRIDGEA_H
+
+#define AUDIO_APBRIDGEA_TYPE_SET_CONFIG                        0x01
+#define AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT            0x02
+#define AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT          0x03
+#define AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE          0x04
+                                                       /* 0x05 unused */
+#define AUDIO_APBRIDGEA_TYPE_PREPARE_TX                        0x06
+#define AUDIO_APBRIDGEA_TYPE_START_TX                  0x07
+#define AUDIO_APBRIDGEA_TYPE_STOP_TX                   0x08
+#define AUDIO_APBRIDGEA_TYPE_SHUTDOWN_TX               0x09
+#define AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE          0x0a
+                                                       /* 0x0b unused */
+#define AUDIO_APBRIDGEA_TYPE_PREPARE_RX                        0x0c
+#define AUDIO_APBRIDGEA_TYPE_START_RX                  0x0d
+#define AUDIO_APBRIDGEA_TYPE_STOP_RX                   0x0e
+#define AUDIO_APBRIDGEA_TYPE_SHUTDOWN_RX               0x0f
+
+#define AUDIO_APBRIDGEA_PCM_FMT_8                      BIT(0)
+#define AUDIO_APBRIDGEA_PCM_FMT_16                     BIT(1)
+#define AUDIO_APBRIDGEA_PCM_FMT_24                     BIT(2)
+#define AUDIO_APBRIDGEA_PCM_FMT_32                     BIT(3)
+#define AUDIO_APBRIDGEA_PCM_FMT_64                     BIT(4)
+
+#define AUDIO_APBRIDGEA_PCM_RATE_5512                  BIT(0)
+#define AUDIO_APBRIDGEA_PCM_RATE_8000                  BIT(1)
+#define AUDIO_APBRIDGEA_PCM_RATE_11025                 BIT(2)
+#define AUDIO_APBRIDGEA_PCM_RATE_16000                 BIT(3)
+#define AUDIO_APBRIDGEA_PCM_RATE_22050                 BIT(4)
+#define AUDIO_APBRIDGEA_PCM_RATE_32000                 BIT(5)
+#define AUDIO_APBRIDGEA_PCM_RATE_44100                 BIT(6)
+#define AUDIO_APBRIDGEA_PCM_RATE_48000                 BIT(7)
+#define AUDIO_APBRIDGEA_PCM_RATE_64000                 BIT(8)
+#define AUDIO_APBRIDGEA_PCM_RATE_88200                 BIT(9)
+#define AUDIO_APBRIDGEA_PCM_RATE_96000                 BIT(10)
+#define AUDIO_APBRIDGEA_PCM_RATE_176400                        BIT(11)
+#define AUDIO_APBRIDGEA_PCM_RATE_192000                        BIT(12)
+
+#define AUDIO_APBRIDGEA_DIRECTION_TX                   BIT(0)
+#define AUDIO_APBRIDGEA_DIRECTION_RX                   BIT(1)
+
+/* The I2S port is passed in the 'index' parameter of the USB request */
+/* The CPort is passed in the 'value' parameter of the USB request */
+
+struct audio_apbridgea_hdr {
+       __u8    type;
+       __le16  i2s_port;
+       __u8    data[0];
+} __packed;
+
+struct audio_apbridgea_set_config_request {
+       struct audio_apbridgea_hdr      hdr;
+       __le32                          format; /* AUDIO_APBRIDGEA_PCM_FMT_* */
+       __le32                          rate;   /* AUDIO_APBRIDGEA_PCM_RATE_* */
+       __le32                          mclk_freq; /* XXX Remove? */
+} __packed;
+
+struct audio_apbridgea_register_cport_request {
+       struct audio_apbridgea_hdr      hdr;
+       __le16                          cport;
+       __u8                            direction;
+} __packed;
+
+struct audio_apbridgea_unregister_cport_request {
+       struct audio_apbridgea_hdr      hdr;
+       __le16                          cport;
+       __u8                            direction;
+} __packed;
+
+struct audio_apbridgea_set_tx_data_size_request {
+       struct audio_apbridgea_hdr      hdr;
+       __le16                          size;
+} __packed;
+
+struct audio_apbridgea_prepare_tx_request {
+       struct audio_apbridgea_hdr      hdr;
+} __packed;
+
+struct audio_apbridgea_start_tx_request {
+       struct audio_apbridgea_hdr      hdr;
+       __le64                          timestamp;
+} __packed;
+
+struct audio_apbridgea_stop_tx_request {
+       struct audio_apbridgea_hdr      hdr;
+} __packed;
+
+struct audio_apbridgea_shutdown_tx_request {
+       struct audio_apbridgea_hdr      hdr;
+} __packed;
+
+struct audio_apbridgea_set_rx_data_size_request {
+       struct audio_apbridgea_hdr      hdr;
+       __le16                          size;
+} __packed;
+
+struct audio_apbridgea_prepare_rx_request {
+       struct audio_apbridgea_hdr      hdr;
+} __packed;
+
+struct audio_apbridgea_start_rx_request {
+       struct audio_apbridgea_hdr      hdr;
+} __packed;
+
+struct audio_apbridgea_stop_rx_request {
+       struct audio_apbridgea_hdr      hdr;
+} __packed;
+
+struct audio_apbridgea_shutdown_rx_request {
+       struct audio_apbridgea_hdr      hdr;
+} __packed;
+
+#endif /*__AUDIO_APBRIDGEA_H */
diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c
new file mode 100644 (file)
index 0000000..2f70295
--- /dev/null
@@ -0,0 +1,1132 @@
+/*
+ * APBridge ALSA SoC dummy codec driver
+ * Copyright 2016 Google Inc.
+ * Copyright 2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <uapi/linux/input.h>
+
+#include "audio_codec.h"
+#include "audio_apbridgea.h"
+#include "audio_manager.h"
+
+static struct gbaudio_codec_info *gbcodec;
+
+static struct gbaudio_data_connection *
+find_data(struct gbaudio_module_info *module, int id)
+{
+       struct gbaudio_data_connection *data;
+
+       list_for_each_entry(data, &module->data_list, list) {
+               if (id == data->id)
+                       return data;
+       }
+       return NULL;
+}
+
+static struct gbaudio_stream_params *
+find_dai_stream_params(struct gbaudio_codec_info *codec, int id, int stream)
+{
+       struct gbaudio_codec_dai *dai;
+
+       list_for_each_entry(dai, &codec->dai_list, list) {
+               if (dai->id == id)
+                       return &dai->params[stream];
+       }
+       return NULL;
+}
+
+static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec,
+                                   struct gbaudio_module_info *module, int id)
+{
+       int module_state, ret = 0;
+       uint16_t data_cport, i2s_port, cportid;
+       uint8_t sig_bits, channels;
+       uint32_t format, rate;
+       struct gbaudio_data_connection *data;
+       struct gbaudio_stream_params *params;
+
+       /* find the dai */
+       data = find_data(module, id);
+       if (!data) {
+               dev_err(module->dev, "%d:DATA connection missing\n", id);
+               return -ENODEV;
+       }
+       module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK];
+
+       params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_PLAYBACK);
+       if (!params) {
+               dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
+               return -EINVAL;
+       }
+
+       /* register cport */
+       if (module_state < GBAUDIO_CODEC_STARTUP) {
+               i2s_port = 0;   /* fixed for now */
+               cportid = data->connection->hd_cport_id;
+               ret = gb_audio_apbridgea_register_cport(data->connection,
+                                               i2s_port, cportid,
+                                               AUDIO_APBRIDGEA_DIRECTION_TX);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "reg_cport failed:%d\n", ret);
+                       return ret;
+               }
+               data->state[SNDRV_PCM_STREAM_PLAYBACK] =
+                       GBAUDIO_CODEC_STARTUP;
+               dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid);
+       }
+
+       /* hw_params */
+       if (module_state < GBAUDIO_CODEC_HWPARAMS) {
+               format = params->format;
+               channels = params->channels;
+               rate = params->rate;
+               sig_bits = params->sig_bits;
+               data_cport = data->connection->intf_cport_id;
+               ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport,
+                                         format, rate, channels, sig_bits);
+               if (ret) {
+                       dev_err_ratelimited(module->dev, "set_pcm failed:%d\n",
+                                           ret);
+                       return ret;
+               }
+               data->state[SNDRV_PCM_STREAM_PLAYBACK] =
+                       GBAUDIO_CODEC_HWPARAMS;
+               dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport);
+       }
+
+       /* prepare */
+       if (module_state < GBAUDIO_CODEC_PREPARE) {
+               data_cport = data->connection->intf_cport_id;
+               ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection,
+                                                  data_cport, 192);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "set_tx_data_size failed:%d\n",
+                                           ret);
+                       return ret;
+               }
+               ret = gb_audio_gb_activate_tx(module->mgmt_connection,
+                                             data_cport);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "activate_tx failed:%d\n", ret);
+                       return ret;
+               }
+               data->state[SNDRV_PCM_STREAM_PLAYBACK] =
+                       GBAUDIO_CODEC_PREPARE;
+               dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport);
+       }
+
+       return 0;
+}
+
+static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id)
+{
+       int ret;
+       uint16_t data_cport, cportid, i2s_port;
+       int module_state;
+       struct gbaudio_data_connection *data;
+
+       /* find the dai */
+       data = find_data(module, id);
+       if (!data) {
+               dev_err(module->dev, "%d:DATA connection missing\n", id);
+               return -ENODEV;
+       }
+       module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK];
+
+       if (module_state > GBAUDIO_CODEC_HWPARAMS) {
+               data_cport = data->connection->intf_cport_id;
+               ret = gb_audio_gb_deactivate_tx(module->mgmt_connection,
+                                               data_cport);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "deactivate_tx failed:%d\n", ret);
+                       return ret;
+               }
+               dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport);
+               data->state[SNDRV_PCM_STREAM_PLAYBACK] =
+                       GBAUDIO_CODEC_HWPARAMS;
+       }
+
+       if (module_state > GBAUDIO_CODEC_SHUTDOWN) {
+               i2s_port = 0;   /* fixed for now */
+               cportid = data->connection->hd_cport_id;
+               ret = gb_audio_apbridgea_unregister_cport(data->connection,
+                                               i2s_port, cportid,
+                                               AUDIO_APBRIDGEA_DIRECTION_TX);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "unregister_cport failed:%d\n",
+                                           ret);
+                       return ret;
+               }
+               dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid);
+               data->state[SNDRV_PCM_STREAM_PLAYBACK] =
+                       GBAUDIO_CODEC_SHUTDOWN;
+       }
+
+       return 0;
+}
+
+static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec,
+                                   struct gbaudio_module_info *module, int id)
+{
+       int module_state, ret = 0;
+       uint16_t data_cport, i2s_port, cportid;
+       uint8_t sig_bits, channels;
+       uint32_t format, rate;
+       struct gbaudio_data_connection *data;
+       struct gbaudio_stream_params *params;
+
+       /* find the dai */
+       data = find_data(module, id);
+       if (!data) {
+               dev_err(module->dev, "%d:DATA connection missing\n", id);
+               return -ENODEV;
+       }
+       module_state = data->state[SNDRV_PCM_STREAM_CAPTURE];
+
+       params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_CAPTURE);
+       if (!params) {
+               dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
+               return -EINVAL;
+       }
+
+       /* register cport */
+       if (module_state < GBAUDIO_CODEC_STARTUP) {
+               i2s_port = 0;   /* fixed for now */
+               cportid = data->connection->hd_cport_id;
+               ret = gb_audio_apbridgea_register_cport(data->connection,
+                                               i2s_port, cportid,
+                                               AUDIO_APBRIDGEA_DIRECTION_RX);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "reg_cport failed:%d\n", ret);
+                       return ret;
+               }
+               data->state[SNDRV_PCM_STREAM_CAPTURE] =
+                       GBAUDIO_CODEC_STARTUP;
+               dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid);
+       }
+
+       /* hw_params */
+       if (module_state < GBAUDIO_CODEC_HWPARAMS) {
+               format = params->format;
+               channels = params->channels;
+               rate = params->rate;
+               sig_bits = params->sig_bits;
+               data_cport = data->connection->intf_cport_id;
+               ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport,
+                                         format, rate, channels, sig_bits);
+               if (ret) {
+                       dev_err_ratelimited(module->dev, "set_pcm failed:%d\n",
+                                           ret);
+                       return ret;
+               }
+               data->state[SNDRV_PCM_STREAM_CAPTURE] =
+                       GBAUDIO_CODEC_HWPARAMS;
+               dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport);
+       }
+
+       /* prepare */
+       if (module_state < GBAUDIO_CODEC_PREPARE) {
+               data_cport = data->connection->intf_cport_id;
+               ret = gb_audio_gb_set_rx_data_size(module->mgmt_connection,
+                                                  data_cport, 192);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "set_rx_data_size failed:%d\n",
+                                           ret);
+                       return ret;
+               }
+               ret = gb_audio_gb_activate_rx(module->mgmt_connection,
+                                             data_cport);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "activate_rx failed:%d\n", ret);
+                       return ret;
+               }
+               data->state[SNDRV_PCM_STREAM_CAPTURE] =
+                       GBAUDIO_CODEC_PREPARE;
+               dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport);
+       }
+
+       return 0;
+}
+
+static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id)
+{
+       int ret;
+       uint16_t data_cport, cportid, i2s_port;
+       int module_state;
+       struct gbaudio_data_connection *data;
+
+       /* find the dai */
+       data = find_data(module, id);
+       if (!data) {
+               dev_err(module->dev, "%d:DATA connection missing\n", id);
+               return -ENODEV;
+       }
+       module_state = data->state[SNDRV_PCM_STREAM_CAPTURE];
+
+       if (module_state > GBAUDIO_CODEC_HWPARAMS) {
+               data_cport = data->connection->intf_cport_id;
+               ret = gb_audio_gb_deactivate_rx(module->mgmt_connection,
+                                               data_cport);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "deactivate_rx failed:%d\n", ret);
+                       return ret;
+               }
+               dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport);
+               data->state[SNDRV_PCM_STREAM_CAPTURE] =
+                       GBAUDIO_CODEC_HWPARAMS;
+       }
+
+       if (module_state > GBAUDIO_CODEC_SHUTDOWN) {
+               i2s_port = 0;   /* fixed for now */
+               cportid = data->connection->hd_cport_id;
+               ret = gb_audio_apbridgea_unregister_cport(data->connection,
+                                               i2s_port, cportid,
+                                               AUDIO_APBRIDGEA_DIRECTION_RX);
+               if (ret) {
+                       dev_err_ratelimited(module->dev,
+                                           "unregister_cport failed:%d\n",
+                                           ret);
+                       return ret;
+               }
+               dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid);
+               data->state[SNDRV_PCM_STREAM_CAPTURE] =
+                       GBAUDIO_CODEC_SHUTDOWN;
+       }
+
+       return 0;
+}
+
+int gbaudio_module_update(struct gbaudio_codec_info *codec,
+                         struct snd_soc_dapm_widget *w,
+                         struct gbaudio_module_info *module, int enable)
+{
+       int dai_id, ret;
+       char intf_name[NAME_SIZE], dir[NAME_SIZE];
+
+       dev_dbg(module->dev, "%s:Module update %s sequence\n", w->name,
+               enable ? "Enable":"Disable");
+
+       if ((w->id != snd_soc_dapm_aif_in) && (w->id != snd_soc_dapm_aif_out)){
+               dev_dbg(codec->dev, "No action required for %s\n", w->name);
+               return 0;
+       }
+
+       /* parse dai_id from AIF widget's stream_name */
+       ret = sscanf(w->sname, "%s %d %s", intf_name, &dai_id, dir);
+       if (ret < 3) {
+               dev_err(codec->dev, "Error while parsing dai_id for %s\n",
+                       w->name);
+               return -EINVAL;
+       }
+
+       mutex_lock(&codec->lock);
+       if (w->id == snd_soc_dapm_aif_in) {
+               if (enable)
+                       ret = gbaudio_module_enable_tx(codec, module, dai_id);
+               else
+                       ret = gbaudio_module_disable_tx(module, dai_id);
+       } else if (w->id == snd_soc_dapm_aif_out) {
+               if (enable)
+                       ret = gbaudio_module_enable_rx(codec, module, dai_id);
+               else
+                       ret = gbaudio_module_disable_rx(module, dai_id);
+       }
+
+       mutex_unlock(&codec->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(gbaudio_module_update);
+
+/*
+ * codec DAI ops
+ */
+static int gbcodec_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
+       struct gbaudio_stream_params *params;
+
+       mutex_lock(&codec->lock);
+
+       if (list_empty(&codec->module_list)) {
+               dev_err(codec->dev, "No codec module available\n");
+               mutex_unlock(&codec->lock);
+               return -ENODEV;
+       }
+
+       params = find_dai_stream_params(codec, dai->id, substream->stream);
+       if (!params) {
+               dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+       params->state = GBAUDIO_CODEC_STARTUP;
+       mutex_unlock(&codec->lock);
+       /* to prevent suspend in case of active audio */
+       pm_stay_awake(dai->dev);
+
+       return 0;
+}
+
+static void gbcodec_shutdown(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
+       struct gbaudio_stream_params *params;
+
+       mutex_lock(&codec->lock);
+
+       if (list_empty(&codec->module_list))
+               dev_info(codec->dev, "No codec module available during shutdown\n");
+
+       params = find_dai_stream_params(codec, dai->id, substream->stream);
+       if (!params) {
+               dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
+               mutex_unlock(&codec->lock);
+               return;
+       }
+       params->state = GBAUDIO_CODEC_SHUTDOWN;
+       mutex_unlock(&codec->lock);
+       pm_relax(dai->dev);
+       return;
+}
+
+static int gbcodec_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *hwparams,
+                            struct snd_soc_dai *dai)
+{
+       int ret;
+       uint8_t sig_bits, channels;
+       uint32_t format, rate;
+       struct gbaudio_module_info *module;
+       struct gbaudio_data_connection *data;
+       struct gb_bundle *bundle;
+       struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
+       struct gbaudio_stream_params *params;
+
+       mutex_lock(&codec->lock);
+
+       if (list_empty(&codec->module_list)) {
+               dev_err(codec->dev, "No codec module available\n");
+               mutex_unlock(&codec->lock);
+               return -ENODEV;
+       }
+
+       /*
+        * assuming, currently only 48000 Hz, 16BIT_LE, stereo
+        * is supported, validate params before configuring codec
+        */
+       if (params_channels(hwparams) != 2) {
+               dev_err(dai->dev, "Invalid channel count:%d\n",
+                       params_channels(hwparams));
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+       channels = params_channels(hwparams);
+
+       if (params_rate(hwparams) != 48000) {
+               dev_err(dai->dev, "Invalid sampling rate:%d\n",
+                       params_rate(hwparams));
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+       rate = GB_AUDIO_PCM_RATE_48000;
+
+       if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) {
+               dev_err(dai->dev, "Invalid format:%d\n",
+                       params_format(hwparams));
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+       format = GB_AUDIO_PCM_FMT_S16_LE;
+
+       /* find the data connection */
+       list_for_each_entry(module, &codec->module_list, list) {
+               data = find_data(module, dai->id);
+               if (data)
+                       break;
+       }
+
+       if (!data) {
+               dev_err(dai->dev, "DATA connection missing\n");
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+
+       params = find_dai_stream_params(codec, dai->id, substream->stream);
+       if (!params) {
+               dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+
+       bundle = to_gb_bundle(module->dev);
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret) {
+               mutex_unlock(&codec->lock);
+               return ret;
+       }
+
+       ret = gb_audio_apbridgea_set_config(data->connection, 0,
+                                           AUDIO_APBRIDGEA_PCM_FMT_16,
+                                           AUDIO_APBRIDGEA_PCM_RATE_48000,
+                                           6144000);
+       if (ret) {
+               dev_err_ratelimited(dai->dev, "%d: Error during set_config\n",
+                                   ret);
+               mutex_unlock(&codec->lock);
+               return ret;
+       }
+
+       gb_pm_runtime_put_noidle(bundle);
+
+       params->state = GBAUDIO_CODEC_HWPARAMS;
+       params->format = format;
+       params->rate = rate;
+       params->channels = channels;
+       params->sig_bits = sig_bits;
+
+       mutex_unlock(&codec->lock);
+       return 0;
+}
+
+static int gbcodec_prepare(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       int ret;
+       struct gbaudio_module_info *module;
+       struct gbaudio_data_connection *data;
+       struct gb_bundle *bundle;
+       struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
+       struct gbaudio_stream_params *params;
+
+       mutex_lock(&codec->lock);
+
+       if (list_empty(&codec->module_list)) {
+               dev_err(codec->dev, "No codec module available\n");
+               mutex_unlock(&codec->lock);
+               return -ENODEV;
+       }
+
+       list_for_each_entry(module, &codec->module_list, list) {
+               /* find the dai */
+               data = find_data(module, dai->id);
+               if (data)
+                       break;
+       }
+       if (!data) {
+               dev_err(dai->dev, "DATA connection missing\n");
+               mutex_unlock(&codec->lock);
+               return -ENODEV;
+       }
+
+       params = find_dai_stream_params(codec, dai->id, substream->stream);
+       if (!params) {
+               dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+
+       bundle = to_gb_bundle(module->dev);
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret) {
+               mutex_unlock(&codec->lock);
+               return ret;
+       }
+
+       switch (substream->stream) {
+       case SNDRV_PCM_STREAM_PLAYBACK:
+               ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0,
+                                                         192);
+               break;
+       case SNDRV_PCM_STREAM_CAPTURE:
+               ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0,
+                                                         192);
+               break;
+       }
+       if (ret) {
+               mutex_unlock(&codec->lock);
+               dev_err_ratelimited(dai->dev, "set_data_size failed:%d\n",
+                                    ret);
+               return ret;
+       }
+
+       gb_pm_runtime_put_noidle(bundle);
+
+       params->state = GBAUDIO_CODEC_PREPARE;
+       mutex_unlock(&codec->lock);
+       return 0;
+}
+
+static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+       int ret;
+       struct gbaudio_data_connection *data;
+       struct gbaudio_module_info *module;
+       struct gb_bundle *bundle;
+       struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev);
+       struct gbaudio_stream_params *params;
+
+
+       dev_dbg(dai->dev, "Mute:%d, Direction:%s\n", mute,
+               stream ? "CAPTURE":"PLAYBACK");
+
+       mutex_lock(&codec->lock);
+
+       params = find_dai_stream_params(codec, dai->id, stream);
+       if (!params) {
+               dev_err(codec->dev, "Failed to fetch dai_stream pointer\n");
+               mutex_unlock(&codec->lock);
+               return -EINVAL;
+       }
+
+       if (list_empty(&codec->module_list)) {
+               dev_err(codec->dev, "No codec module available\n");
+               if (mute) {
+                       params->state = GBAUDIO_CODEC_STOP;
+                       ret = 0;
+               } else {
+                       ret = -ENODEV;
+               }
+               mutex_unlock(&codec->lock);
+               return ret;
+       }
+
+       list_for_each_entry(module, &codec->module_list, list) {
+               /* find the dai */
+               data = find_data(module, dai->id);
+               if (data)
+                       break;
+       }
+       if (!data) {
+               dev_err(dai->dev, "%s:%s DATA connection missing\n",
+                       dai->name, module->name);
+               mutex_unlock(&codec->lock);
+               return -ENODEV;
+       }
+
+       bundle = to_gb_bundle(module->dev);
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret) {
+               mutex_unlock(&codec->lock);
+               return ret;
+       }
+
+       if (!mute && !stream) {/* start playback */
+               ret = gb_audio_apbridgea_prepare_tx(data->connection,
+                                                   0);
+               if (!ret)
+                       ret = gb_audio_apbridgea_start_tx(data->connection,
+                                                         0, 0);
+               params->state = GBAUDIO_CODEC_START;
+       } else if (!mute && stream) {/* start capture */
+               ret = gb_audio_apbridgea_prepare_rx(data->connection,
+                                                   0);
+               if (!ret)
+                       ret = gb_audio_apbridgea_start_rx(data->connection,
+                                                         0);
+               params->state = GBAUDIO_CODEC_START;
+       } else if (mute && !stream) {/* stop playback */
+               ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
+               if (!ret)
+                       ret = gb_audio_apbridgea_shutdown_tx(data->connection,
+                                                            0);
+               params->state = GBAUDIO_CODEC_STOP;
+       } else if (mute && stream) {/* stop capture */
+               ret = gb_audio_apbridgea_stop_rx(data->connection, 0);
+               if (!ret)
+                       ret = gb_audio_apbridgea_shutdown_rx(data->connection,
+                                                            0);
+               params->state = GBAUDIO_CODEC_STOP;
+       } else
+               ret = -EINVAL;
+       if (ret)
+               dev_err_ratelimited(dai->dev,
+                                   "%s:Error during %s %s stream:%d\n",
+                                   module->name, mute ? "Mute" : "Unmute",
+                                   stream ? "Capture" : "Playback", ret);
+
+       gb_pm_runtime_put_noidle(bundle);
+       mutex_unlock(&codec->lock);
+       return ret;
+}
+
+static struct snd_soc_dai_ops gbcodec_dai_ops = {
+       .startup = gbcodec_startup,
+       .shutdown = gbcodec_shutdown,
+       .hw_params = gbcodec_hw_params,
+       .prepare = gbcodec_prepare,
+       .mute_stream = gbcodec_mute_stream,
+};
+
+static struct snd_soc_dai_driver gbaudio_dai[] = {
+       {
+               .name = "apb-i2s0",
+               .id = 0,
+               .playback = {
+                       .stream_name = "I2S 0 Playback",
+                       .rates = SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FORMAT_S16_LE,
+                       .rate_max = 48000,
+                       .rate_min = 48000,
+                       .channels_min = 1,
+                       .channels_max = 2,
+               },
+               .capture = {
+                       .stream_name = "I2S 0 Capture",
+                       .rates = SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FORMAT_S16_LE,
+                       .rate_max = 48000,
+                       .rate_min = 48000,
+                       .channels_min = 1,
+                       .channels_max = 2,
+               },
+               .ops = &gbcodec_dai_ops,
+       },
+};
+
+static int gbaudio_init_jack(struct gbaudio_module_info *module,
+                            struct snd_soc_codec *codec)
+{
+       int ret;
+
+       if (!module->jack_mask)
+               return 0;
+
+       snprintf(module->jack_name, NAME_SIZE, "GB %d Headset Jack",
+                module->dev_id);
+       ret = snd_soc_jack_new(codec, module->jack_name, module->jack_mask,
+                              &module->headset_jack);
+       if (ret) {
+               dev_err(module->dev, "Failed to create new jack\n");
+               return ret;
+       }
+
+       if (!module->button_mask)
+               return 0;
+
+       snprintf(module->button_name, NAME_SIZE, "GB %d Button Jack",
+                module->dev_id);
+       ret = snd_soc_jack_new(codec, module->button_name, module->button_mask,
+                              &module->button_jack);
+       if (ret) {
+               dev_err(module->dev, "Failed to create button jack\n");
+               return ret;
+       }
+
+       /*
+        * Currently, max 4 buttons are supported with following key mapping
+        * BTN_0 = KEY_MEDIA
+        * BTN_1 = KEY_VOICECOMMAND
+        * BTN_2 = KEY_VOLUMEUP
+        * BTN_3 = KEY_VOLUMEDOWN
+        */
+
+       if (module->button_mask & SND_JACK_BTN_0) {
+               ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_0,
+                                      KEY_MEDIA);
+               if (ret) {
+                       dev_err(module->dev, "Failed to set BTN_0\n");
+                       return ret;
+               }
+       }
+
+       if (module->button_mask & SND_JACK_BTN_1) {
+               ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_1,
+                                      KEY_VOICECOMMAND);
+               if (ret) {
+                       dev_err(module->dev, "Failed to set BTN_1\n");
+                       return ret;
+               }
+       }
+
+       if (module->button_mask & SND_JACK_BTN_2) {
+               ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_2,
+                                      KEY_VOLUMEUP);
+               if (ret) {
+                       dev_err(module->dev, "Failed to set BTN_2\n");
+                       return ret;
+               }
+       }
+
+       if (module->button_mask & SND_JACK_BTN_3) {
+               ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_3,
+                                      KEY_VOLUMEDOWN);
+               if (ret) {
+                       dev_err(module->dev, "Failed to set BTN_0\n");
+                       return ret;
+               }
+       }
+
+       /* FIXME
+        * verify if this is really required
+       set_bit(INPUT_PROP_NO_DUMMY_RELEASE,
+               module->button_jack.jack->input_dev->propbit);
+       */
+
+       return 0;
+}
+
+int gbaudio_register_module(struct gbaudio_module_info *module)
+{
+       int ret;
+       struct snd_soc_codec *codec;
+       struct snd_card *card;
+       struct snd_soc_jack *jack = NULL;
+
+       if (!gbcodec) {
+               dev_err(module->dev, "GB Codec not yet probed\n");
+               return -EAGAIN;
+       }
+
+       codec = gbcodec->codec;
+       card = codec->card->snd_card;
+
+       down_write(&card->controls_rwsem);
+
+       if (module->num_dais) {
+               dev_err(gbcodec->dev,
+                       "%d:DAIs not supported via gbcodec driver\n",
+                       module->num_dais);
+               up_write(&card->controls_rwsem);
+               return -EINVAL;
+       }
+
+       ret = gbaudio_init_jack(module, codec);
+       if (ret) {
+               up_write(&card->controls_rwsem);
+               return ret;
+       }
+
+       if (module->dapm_widgets)
+               snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets,
+                                         module->num_dapm_widgets);
+       if (module->controls)
+               snd_soc_add_codec_controls(codec, module->controls,
+                                    module->num_controls);
+       if (module->dapm_routes)
+               snd_soc_dapm_add_routes(&codec->dapm, module->dapm_routes,
+                                       module->num_dapm_routes);
+
+       /* card already instantiated, create widgets here only */
+       if (codec->card->instantiated) {
+               snd_soc_dapm_link_component_dai_widgets(codec->card,
+                                                       &codec->dapm);
+#ifdef CONFIG_SND_JACK
+               /* register jack devices for this module from codec->jack_list */
+               list_for_each_entry(jack, &codec->jack_list, list) {
+                       if ((jack == &module->headset_jack)
+                           || (jack == &module->button_jack))
+                               snd_device_register(codec->card->snd_card,
+                                                   jack->jack);
+               }
+#endif
+       }
+
+       mutex_lock(&gbcodec->lock);
+       list_add(&module->list, &gbcodec->module_list);
+       mutex_unlock(&gbcodec->lock);
+
+       if (codec->card->instantiated)
+               ret = snd_soc_dapm_new_widgets(&codec->dapm);
+       dev_dbg(codec->dev, "Registered %s module\n", module->name);
+
+       up_write(&card->controls_rwsem);
+       return ret;
+}
+EXPORT_SYMBOL(gbaudio_register_module);
+
+static void gbaudio_codec_clean_data_tx(struct gbaudio_data_connection *data)
+{
+       uint16_t i2s_port, cportid;
+       int ret;
+
+       if (list_is_singular(&gbcodec->module_list)) {
+               ret = gb_audio_apbridgea_stop_tx(data->connection, 0);
+               if (ret)
+                       return;
+               ret = gb_audio_apbridgea_shutdown_tx(data->connection,
+                                                    0);
+               if (ret)
+                       return;
+       }
+       i2s_port = 0;   /* fixed for now */
+       cportid = data->connection->hd_cport_id;
+       ret = gb_audio_apbridgea_unregister_cport(data->connection,
+                                                 i2s_port, cportid,
+                                                 AUDIO_APBRIDGEA_DIRECTION_TX);
+       data->state[0] = GBAUDIO_CODEC_SHUTDOWN;
+}
+
+static void gbaudio_codec_clean_data_rx(struct gbaudio_data_connection *data)
+{
+       uint16_t i2s_port, cportid;
+       int ret;
+
+       if (list_is_singular(&gbcodec->module_list)) {
+               ret = gb_audio_apbridgea_stop_rx(data->connection, 0);
+               if (ret)
+                       return;
+               ret = gb_audio_apbridgea_shutdown_rx(data->connection,
+                                                    0);
+               if (ret)
+                       return;
+       }
+       i2s_port = 0;   /* fixed for now */
+       cportid = data->connection->hd_cport_id;
+       ret = gb_audio_apbridgea_unregister_cport(data->connection,
+                                                 i2s_port, cportid,
+                                                 AUDIO_APBRIDGEA_DIRECTION_RX);
+       data->state[1] = GBAUDIO_CODEC_SHUTDOWN;
+}
+
+
+static void gbaudio_codec_cleanup(struct gbaudio_module_info *module)
+{
+       struct gbaudio_data_connection *data;
+       int pb_state, cap_state;
+
+       dev_dbg(gbcodec->dev, "%s: removed, cleanup APBridge\n", module->name);
+       list_for_each_entry(data, &module->data_list, list) {
+               pb_state = data->state[0];
+               cap_state = data->state[1];
+
+               if (pb_state > GBAUDIO_CODEC_SHUTDOWN)
+                       gbaudio_codec_clean_data_tx(data);
+
+               if (cap_state > GBAUDIO_CODEC_SHUTDOWN)
+                       gbaudio_codec_clean_data_rx(data);
+
+       }
+}
+
+void gbaudio_unregister_module(struct gbaudio_module_info *module)
+{
+       struct snd_soc_codec *codec = gbcodec->codec;
+       struct snd_card *card = codec->card->snd_card;
+       struct snd_soc_jack *jack, *next_j;
+       int mask;
+
+       dev_dbg(codec->dev, "Unregister %s module\n", module->name);
+
+       down_write(&card->controls_rwsem);
+       mutex_lock(&gbcodec->lock);
+       gbaudio_codec_cleanup(module);
+       list_del(&module->list);
+       dev_dbg(codec->dev, "Process Unregister %s module\n", module->name);
+       mutex_unlock(&gbcodec->lock);
+
+#ifdef CONFIG_SND_JACK
+       /* free jack devices for this module from codec->jack_list */
+       list_for_each_entry_safe(jack, next_j, &codec->jack_list, list) {
+               if (jack == &module->headset_jack)
+                       mask = GBCODEC_JACK_MASK;
+               else if (jack == &module->button_jack)
+                       mask = GBCODEC_JACK_BUTTON_MASK;
+               else
+                       mask = 0;
+               if (mask) {
+                       dev_dbg(module->dev, "Report %s removal\n",
+                               jack->jack->id);
+                       snd_soc_jack_report(jack, 0, mask);
+                       snd_device_free(codec->card->snd_card, jack->jack);
+                       list_del(&jack->list);
+               }
+       }
+#endif
+
+       if (module->dapm_routes) {
+               dev_dbg(codec->dev, "Removing %d routes\n",
+                       module->num_dapm_routes);
+               snd_soc_dapm_del_routes(&codec->dapm, module->dapm_routes,
+                                       module->num_dapm_routes);
+       }
+       if (module->controls) {
+               dev_dbg(codec->dev, "Removing %d controls\n",
+                       module->num_controls);
+               snd_soc_remove_codec_controls(codec, module->controls,
+                                         module->num_controls);
+       }
+       if (module->dapm_widgets) {
+               dev_dbg(codec->dev, "Removing %d widgets\n",
+                       module->num_dapm_widgets);
+               snd_soc_dapm_free_controls(&codec->dapm, module->dapm_widgets,
+                                          module->num_dapm_widgets);
+       }
+
+       dev_dbg(codec->dev, "Unregistered %s module\n", module->name);
+
+       up_write(&card->controls_rwsem);
+}
+EXPORT_SYMBOL(gbaudio_unregister_module);
+
+/*
+ * codec driver ops
+ */
+static int gbcodec_probe(struct snd_soc_codec *codec)
+{
+       int i;
+       struct gbaudio_codec_info *info;
+       struct gbaudio_codec_dai *dai;
+
+       info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->dev = codec->dev;
+       INIT_LIST_HEAD(&info->module_list);
+       mutex_init(&info->lock);
+       INIT_LIST_HEAD(&info->dai_list);
+
+       /* init dai_list used to maintain runtime stream info */
+       for (i = 0; i < ARRAY_SIZE(gbaudio_dai); i++) {
+               dai = devm_kzalloc(codec->dev, sizeof(*dai), GFP_KERNEL);
+               if (!dai)
+                       return -ENOMEM;
+               dai->id = gbaudio_dai[i].id;
+               list_add(&dai->list, &info->dai_list);
+       }
+
+       info->codec = codec;
+       snd_soc_codec_set_drvdata(codec, info);
+       gbcodec = info;
+
+        device_init_wakeup(codec->dev, 1);
+       return 0;
+}
+
+static int gbcodec_remove(struct snd_soc_codec *codec)
+{
+       /* Empty function for now */
+       return 0;
+}
+
+static u8 gbcodec_reg[GBCODEC_REG_COUNT] = {
+       [GBCODEC_CTL_REG] = GBCODEC_CTL_REG_DEFAULT,
+       [GBCODEC_MUTE_REG] = GBCODEC_MUTE_REG_DEFAULT,
+       [GBCODEC_PB_LVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT,
+       [GBCODEC_PB_RVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT,
+       [GBCODEC_CAP_LVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT,
+       [GBCODEC_CAP_RVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT,
+       [GBCODEC_APB1_MUX_REG] = GBCODEC_APB1_MUX_REG_DEFAULT,
+       [GBCODEC_APB2_MUX_REG] = GBCODEC_APB2_MUX_REG_DEFAULT,
+};
+
+static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg,
+                        unsigned int value)
+{
+       int ret = 0;
+
+       if (reg == SND_SOC_NOPM)
+               return 0;
+
+       BUG_ON(reg >= GBCODEC_REG_COUNT);
+
+       gbcodec_reg[reg] = value;
+       dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value);
+
+       return ret;
+}
+
+static unsigned int gbcodec_read(struct snd_soc_codec *codec,
+                                unsigned int reg)
+{
+       unsigned int val = 0;
+
+       if (reg == SND_SOC_NOPM)
+               return 0;
+
+       BUG_ON(reg >= GBCODEC_REG_COUNT);
+
+       val = gbcodec_reg[reg];
+       dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val);
+
+       return val;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_gbaudio = {
+       .probe  = gbcodec_probe,
+       .remove = gbcodec_remove,
+
+       .read = gbcodec_read,
+       .write = gbcodec_write,
+
+       .reg_cache_size = GBCODEC_REG_COUNT,
+       .reg_cache_default = gbcodec_reg_defaults,
+       .reg_word_size = 1,
+
+       .idle_bias_off = true,
+       .ignore_pmdown_time = 1,
+};
+
+#ifdef CONFIG_PM
+static int gbaudio_codec_suspend(struct device *dev)
+{
+       dev_dbg(dev, "%s: suspend\n", __func__);
+       return 0;
+}
+
+static int gbaudio_codec_resume(struct device *dev)
+{
+       dev_dbg(dev, "%s: resume\n", __func__);
+       return 0;
+}
+
+static const struct dev_pm_ops gbaudio_codec_pm_ops = {
+       .suspend        = gbaudio_codec_suspend,
+       .resume         = gbaudio_codec_resume,
+};
+#endif
+
+static int gbaudio_codec_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbaudio,
+                       gbaudio_dai, ARRAY_SIZE(gbaudio_dai));
+}
+
+static int gbaudio_codec_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id greybus_asoc_machine_of_match[]  = {
+       { .compatible = "toshiba,apb-dummy-codec", },
+       {},
+};
+
+static struct platform_driver gbaudio_codec_driver = {
+       .driver = {
+               .name = "apb-dummy-codec",
+               .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+               .pm = &gbaudio_codec_pm_ops,
+#endif
+               .of_match_table = greybus_asoc_machine_of_match,
+       },
+       .probe = gbaudio_codec_probe,
+       .remove = gbaudio_codec_remove,
+};
+module_platform_driver(gbaudio_codec_driver);
+
+MODULE_DESCRIPTION("APBridge ALSA SoC dummy codec driver");
+MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:apb-dummy-codec");
diff --git a/drivers/staging/greybus/audio_codec.h b/drivers/staging/greybus/audio_codec.h
new file mode 100644 (file)
index 0000000..0a86459
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Greybus audio driver
+ * Copyright 2015 Google Inc.
+ * Copyright 2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef __LINUX_GBAUDIO_CODEC_H
+#define __LINUX_GBAUDIO_CODEC_H
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "greybus.h"
+#include "greybus_protocols.h"
+
+#define NAME_SIZE      32
+#define MAX_DAIS       2       /* APB1, APB2 */
+
+enum {
+       APB1_PCM = 0,
+       APB2_PCM,
+       NUM_CODEC_DAIS,
+};
+
+enum gbcodec_reg_index {
+       GBCODEC_CTL_REG,
+       GBCODEC_MUTE_REG,
+       GBCODEC_PB_LVOL_REG,
+       GBCODEC_PB_RVOL_REG,
+       GBCODEC_CAP_LVOL_REG,
+       GBCODEC_CAP_RVOL_REG,
+       GBCODEC_APB1_MUX_REG,
+       GBCODEC_APB2_MUX_REG,
+       GBCODEC_REG_COUNT
+};
+
+/* device_type should be same as defined in audio.h (Android media layer) */
+enum {
+       GBAUDIO_DEVICE_NONE                     = 0x0,
+       /* reserved bits */
+       GBAUDIO_DEVICE_BIT_IN                   = 0x80000000,
+       GBAUDIO_DEVICE_BIT_DEFAULT              = 0x40000000,
+       /* output devices */
+       GBAUDIO_DEVICE_OUT_SPEAKER              = 0x2,
+       GBAUDIO_DEVICE_OUT_WIRED_HEADSET        = 0x4,
+       GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE      = 0x8,
+       /* input devices */
+       GBAUDIO_DEVICE_IN_BUILTIN_MIC           = GBAUDIO_DEVICE_BIT_IN | 0x4,
+       GBAUDIO_DEVICE_IN_WIRED_HEADSET         = GBAUDIO_DEVICE_BIT_IN | 0x10,
+};
+
+/* bit 0-SPK, 1-HP, 2-DAC,
+ * 4-MIC, 5-HSMIC, 6-MIC2
+ */
+#define GBCODEC_CTL_REG_DEFAULT                0x00
+
+/* bit 0,1 - APB1-PB-L/R
+ * bit 2,3 - APB2-PB-L/R
+ * bit 4,5 - APB1-Cap-L/R
+ * bit 6,7 - APB2-Cap-L/R
+ */
+#define        GBCODEC_MUTE_REG_DEFAULT        0x00
+
+/* 0-127 steps */
+#define        GBCODEC_PB_VOL_REG_DEFAULT      0x00
+#define        GBCODEC_CAP_VOL_REG_DEFAULT     0x00
+
+/* bit 0,1,2 - PB stereo, left, right
+ * bit 8,9,10 - Cap stereo, left, right
+ */
+#define GBCODEC_APB1_MUX_REG_DEFAULT   0x00
+#define GBCODEC_APB2_MUX_REG_DEFAULT   0x00
+
+#define GBCODEC_JACK_MASK              0x0000FFFF
+#define GBCODEC_JACK_BUTTON_MASK       0xFFFF0000
+
+static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = {
+       GBCODEC_CTL_REG_DEFAULT,
+       GBCODEC_MUTE_REG_DEFAULT,
+       GBCODEC_PB_VOL_REG_DEFAULT,
+       GBCODEC_PB_VOL_REG_DEFAULT,
+       GBCODEC_CAP_VOL_REG_DEFAULT,
+       GBCODEC_CAP_VOL_REG_DEFAULT,
+       GBCODEC_APB1_MUX_REG_DEFAULT,
+       GBCODEC_APB2_MUX_REG_DEFAULT,
+};
+
+enum gbaudio_codec_state {
+       GBAUDIO_CODEC_SHUTDOWN = 0,
+       GBAUDIO_CODEC_STARTUP,
+       GBAUDIO_CODEC_HWPARAMS,
+       GBAUDIO_CODEC_PREPARE,
+       GBAUDIO_CODEC_START,
+       GBAUDIO_CODEC_STOP,
+};
+
+struct gbaudio_stream_params {
+       int state;
+       uint8_t sig_bits, channels;
+       uint32_t format, rate;
+};
+
+struct gbaudio_codec_dai {
+       int id;
+       /* runtime params for playback/capture streams */
+       struct gbaudio_stream_params params[2];
+       struct list_head list;
+};
+
+struct gbaudio_codec_info {
+       struct device *dev;
+       struct snd_soc_codec *codec;
+       struct list_head module_list;
+       /* to maintain runtime stream params for each DAI */
+       struct list_head dai_list;
+       struct mutex lock;
+       u8 reg[GBCODEC_REG_COUNT];
+};
+
+struct gbaudio_widget {
+       __u8 id;
+       const char *name;
+       struct list_head list;
+};
+
+struct gbaudio_control {
+       __u8 id;
+       char *name;
+       char *wname;
+       const char * const *texts;
+       int items;
+       struct list_head list;
+};
+
+struct gbaudio_data_connection {
+       int id;
+       __le16 data_cport;
+       struct gb_connection *connection;
+       struct list_head list;
+       /* maintain runtime state for playback/capture stream */
+       int state[2];
+};
+
+/* stream direction */
+#define GB_PLAYBACK    BIT(0)
+#define GB_CAPTURE     BIT(1)
+
+enum gbaudio_module_state {
+       GBAUDIO_MODULE_OFF = 0,
+       GBAUDIO_MODULE_ON,
+};
+
+struct gbaudio_module_info {
+       /* module info */
+       struct device *dev;
+       int dev_id;     /* check if it should be bundle_id/hd_cport_id */
+       int vid;
+       int pid;
+       int slot;
+       int type;
+       int set_uevent;
+       char vstr[NAME_SIZE];
+       char pstr[NAME_SIZE];
+       struct list_head list;
+       /* need to share this info to above user space */
+       int manager_id;
+       char name[NAME_SIZE];
+       unsigned int ip_devices;
+       unsigned int op_devices;
+
+       /* jack related */
+       char jack_name[NAME_SIZE];
+       char button_name[NAME_SIZE];
+       int jack_type;
+       int jack_mask;
+       int button_mask;
+       int button_status;
+       struct snd_soc_jack headset_jack;
+       struct snd_soc_jack button_jack;
+
+       /* connection info */
+       struct gb_connection *mgmt_connection;
+       size_t num_data_connections;
+       struct list_head data_list;
+
+       /* topology related */
+       int num_dais;
+       int num_controls;
+       int num_dapm_widgets;
+       int num_dapm_routes;
+       unsigned long dai_offset;
+       unsigned long widget_offset;
+       unsigned long control_offset;
+       unsigned long route_offset;
+       struct snd_kcontrol_new *controls;
+       struct snd_soc_dapm_widget *dapm_widgets;
+       struct snd_soc_dapm_route *dapm_routes;
+       struct snd_soc_dai_driver *dais;
+
+       struct list_head widget_list;
+       struct list_head ctl_list;
+       struct list_head widget_ctl_list;
+
+       struct gb_audio_topology *topology;
+};
+
+int gbaudio_tplg_parse_data(struct gbaudio_module_info *module,
+                              struct gb_audio_topology *tplg_data);
+void gbaudio_tplg_release(struct gbaudio_module_info *module);
+
+int gbaudio_module_update(struct gbaudio_codec_info *codec,
+                         struct snd_soc_dapm_widget *w,
+                         struct gbaudio_module_info *module,
+                         int enable);
+int gbaudio_register_module(struct gbaudio_module_info *module);
+void gbaudio_unregister_module(struct gbaudio_module_info *module);
+
+/* protocol related */
+extern int gb_audio_gb_get_topology(struct gb_connection *connection,
+                                   struct gb_audio_topology **topology);
+extern int gb_audio_gb_get_control(struct gb_connection *connection,
+                                  uint8_t control_id, uint8_t index,
+                                  struct gb_audio_ctl_elem_value *value);
+extern int gb_audio_gb_set_control(struct gb_connection *connection,
+                                  uint8_t control_id, uint8_t index,
+                                  struct gb_audio_ctl_elem_value *value);
+extern int gb_audio_gb_enable_widget(struct gb_connection *connection,
+                                    uint8_t widget_id);
+extern int gb_audio_gb_disable_widget(struct gb_connection *connection,
+                                     uint8_t widget_id);
+extern int gb_audio_gb_get_pcm(struct gb_connection *connection,
+                              uint16_t data_cport, uint32_t *format,
+                              uint32_t *rate, uint8_t *channels,
+                              uint8_t *sig_bits);
+extern int gb_audio_gb_set_pcm(struct gb_connection *connection,
+                              uint16_t data_cport, uint32_t format,
+                              uint32_t rate, uint8_t channels,
+                              uint8_t sig_bits);
+extern int gb_audio_gb_set_tx_data_size(struct gb_connection *connection,
+                                       uint16_t data_cport, uint16_t size);
+extern int gb_audio_gb_activate_tx(struct gb_connection *connection,
+                                  uint16_t data_cport);
+extern int gb_audio_gb_deactivate_tx(struct gb_connection *connection,
+                                    uint16_t data_cport);
+extern int gb_audio_gb_set_rx_data_size(struct gb_connection *connection,
+                                       uint16_t data_cport, uint16_t size);
+extern int gb_audio_gb_activate_rx(struct gb_connection *connection,
+                                  uint16_t data_cport);
+extern int gb_audio_gb_deactivate_rx(struct gb_connection *connection,
+                                    uint16_t data_cport);
+extern int gb_audio_apbridgea_set_config(struct gb_connection *connection,
+                                        __u16 i2s_port, __u32 format,
+                                        __u32 rate, __u32 mclk_freq);
+extern int gb_audio_apbridgea_register_cport(struct gb_connection *connection,
+                                            __u16 i2s_port, __u16 cportid,
+                                            __u8 direction);
+extern int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection,
+                                              __u16 i2s_port, __u16 cportid,
+                                              __u8 direction);
+extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection,
+                                              __u16 i2s_port, __u16 size);
+extern int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection,
+                                        __u16 i2s_port);
+extern int gb_audio_apbridgea_start_tx(struct gb_connection *connection,
+                                      __u16 i2s_port, __u64 timestamp);
+extern int gb_audio_apbridgea_stop_tx(struct gb_connection *connection,
+                                     __u16 i2s_port);
+extern int gb_audio_apbridgea_shutdown_tx(struct gb_connection *connection,
+                                         __u16 i2s_port);
+extern int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection,
+                                              __u16 i2s_port, __u16 size);
+extern int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection,
+                                        __u16 i2s_port);
+extern int gb_audio_apbridgea_start_rx(struct gb_connection *connection,
+                                      __u16 i2s_port);
+extern int gb_audio_apbridgea_stop_rx(struct gb_connection *connection,
+                                     __u16 i2s_port);
+extern int gb_audio_apbridgea_shutdown_rx(struct gb_connection *connection,
+                                         __u16 i2s_port);
+
+#endif /* __LINUX_GBAUDIO_CODEC_H */
diff --git a/drivers/staging/greybus/audio_gb.c b/drivers/staging/greybus/audio_gb.c
new file mode 100644 (file)
index 0000000..a2f1c92
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Greybus Audio Device Class Protocol helpers
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include "greybus.h"
+#include "greybus_protocols.h"
+#include "operation.h"
+#include "audio_codec.h"
+
+/* TODO: Split into separate calls */
+int gb_audio_gb_get_topology(struct gb_connection *connection,
+                            struct gb_audio_topology **topology)
+{
+       struct gb_audio_get_topology_size_response size_resp;
+       struct gb_audio_topology *topo;
+       uint16_t size;
+       int ret;
+
+       ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY_SIZE,
+                               NULL, 0, &size_resp, sizeof(size_resp));
+       if (ret)
+               return ret;
+
+       size = le16_to_cpu(size_resp.size);
+       if (size < sizeof(*topo))
+               return -ENODATA;
+
+       topo = kzalloc(size, GFP_KERNEL);
+       if (!topo)
+               return -ENOMEM;
+
+       ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY, NULL, 0,
+                               topo, size);
+       if (ret) {
+               kfree(topo);
+               return ret;
+       }
+
+       *topology = topo;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_get_topology);
+
+int gb_audio_gb_get_control(struct gb_connection *connection,
+                           uint8_t control_id, uint8_t index,
+                           struct gb_audio_ctl_elem_value *value)
+{
+       struct gb_audio_get_control_request req;
+       struct gb_audio_get_control_response resp;
+       int ret;
+
+       req.control_id = control_id;
+       req.index = index;
+
+       ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_CONTROL,
+                               &req, sizeof(req), &resp, sizeof(resp));
+       if (ret)
+               return ret;
+
+       memcpy(value, &resp.value, sizeof(*value));
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_get_control);
+
+int gb_audio_gb_set_control(struct gb_connection *connection,
+                           uint8_t control_id, uint8_t index,
+                           struct gb_audio_ctl_elem_value *value)
+{
+       struct gb_audio_set_control_request req;
+
+       req.control_id = control_id;
+       req.index = index;
+       memcpy(&req.value, value, sizeof(req.value));
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_CONTROL,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_set_control);
+
+int gb_audio_gb_enable_widget(struct gb_connection *connection,
+                             uint8_t widget_id)
+{
+       struct gb_audio_enable_widget_request req;
+
+       req.widget_id = widget_id;
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_ENABLE_WIDGET,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_enable_widget);
+
+int gb_audio_gb_disable_widget(struct gb_connection *connection,
+                              uint8_t widget_id)
+{
+       struct gb_audio_disable_widget_request req;
+
+       req.widget_id = widget_id;
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_DISABLE_WIDGET,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_disable_widget);
+
+int gb_audio_gb_get_pcm(struct gb_connection *connection, uint16_t data_cport,
+                       uint32_t *format, uint32_t *rate, uint8_t *channels,
+                       uint8_t *sig_bits)
+{
+       struct gb_audio_get_pcm_request req;
+       struct gb_audio_get_pcm_response resp;
+       int ret;
+
+       req.data_cport = cpu_to_le16(data_cport);
+
+       ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_PCM,
+                               &req, sizeof(req), &resp, sizeof(resp));
+       if (ret)
+               return ret;
+
+       *format = le32_to_cpu(resp.format);
+       *rate = le32_to_cpu(resp.rate);
+       *channels = resp.channels;
+       *sig_bits = resp.sig_bits;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_get_pcm);
+
+int gb_audio_gb_set_pcm(struct gb_connection *connection, uint16_t data_cport,
+                       uint32_t format, uint32_t rate, uint8_t channels,
+                       uint8_t sig_bits)
+{
+       struct gb_audio_set_pcm_request req;
+
+       req.data_cport = cpu_to_le16(data_cport);
+       req.format = cpu_to_le32(format);
+       req.rate = cpu_to_le32(rate);
+       req.channels = channels;
+       req.sig_bits = sig_bits;
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_PCM,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_set_pcm);
+
+int gb_audio_gb_set_tx_data_size(struct gb_connection *connection,
+                                uint16_t data_cport, uint16_t size)
+{
+       struct gb_audio_set_tx_data_size_request req;
+
+       req.data_cport = cpu_to_le16(data_cport);
+       req.size = cpu_to_le16(size);
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_TX_DATA_SIZE,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_set_tx_data_size);
+
+int gb_audio_gb_activate_tx(struct gb_connection *connection,
+                           uint16_t data_cport)
+{
+       struct gb_audio_activate_tx_request req;
+
+       req.data_cport = cpu_to_le16(data_cport);
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_ACTIVATE_TX,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_activate_tx);
+
+int gb_audio_gb_deactivate_tx(struct gb_connection *connection,
+                             uint16_t data_cport)
+{
+       struct gb_audio_deactivate_tx_request req;
+
+       req.data_cport = cpu_to_le16(data_cport);
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_DEACTIVATE_TX,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_deactivate_tx);
+
+int gb_audio_gb_set_rx_data_size(struct gb_connection *connection,
+                                uint16_t data_cport, uint16_t size)
+{
+       struct gb_audio_set_rx_data_size_request req;
+
+       req.data_cport = cpu_to_le16(data_cport);
+       req.size = cpu_to_le16(size);
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_RX_DATA_SIZE,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_set_rx_data_size);
+
+int gb_audio_gb_activate_rx(struct gb_connection *connection,
+                           uint16_t data_cport)
+{
+       struct gb_audio_activate_rx_request req;
+
+       req.data_cport = cpu_to_le16(data_cport);
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_ACTIVATE_RX,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_activate_rx);
+
+int gb_audio_gb_deactivate_rx(struct gb_connection *connection,
+                             uint16_t data_cport)
+{
+       struct gb_audio_deactivate_rx_request req;
+
+       req.data_cport = cpu_to_le16(data_cport);
+
+       return gb_operation_sync(connection, GB_AUDIO_TYPE_DEACTIVATE_RX,
+                                &req, sizeof(req), NULL, 0);
+}
+EXPORT_SYMBOL_GPL(gb_audio_gb_deactivate_rx);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("greybus:audio-gb");
+MODULE_DESCRIPTION("Greybus Audio Device Class Protocol library");
+MODULE_AUTHOR("Mark Greer <mgreer@animalcreek.com>");
diff --git a/drivers/staging/greybus/audio_manager.c b/drivers/staging/greybus/audio_manager.c
new file mode 100644 (file)
index 0000000..aa6508b
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rwlock.h>
+#include <linux/idr.h>
+
+#include "audio_manager.h"
+#include "audio_manager_private.h"
+
+static struct kset *manager_kset;
+
+static LIST_HEAD(modules_list);
+static DECLARE_RWSEM(modules_rwsem);
+static DEFINE_IDA(module_id);
+
+/* helpers */
+static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id)
+{
+       struct gb_audio_manager_module *module;
+
+       if (id < 0)
+               return NULL;
+
+       list_for_each_entry(module, &modules_list, list) {
+               if (module->id == id)
+                       return module;
+       }
+
+       return NULL;
+}
+
+/* public API */
+int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc)
+{
+       struct gb_audio_manager_module *module;
+       int id;
+       int err;
+
+       id = ida_simple_get(&module_id, 0, 0, GFP_KERNEL);
+       err = gb_audio_manager_module_create(&module, manager_kset,
+                                            id, desc);
+       if (err) {
+               ida_simple_remove(&module_id, id);
+               return err;
+       }
+
+       /* Add it to the list */
+       down_write(&modules_rwsem);
+       list_add_tail(&module->list, &modules_list);
+       up_write(&modules_rwsem);
+
+       return module->id;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_add);
+
+int gb_audio_manager_remove(int id)
+{
+       struct gb_audio_manager_module *module;
+
+       down_write(&modules_rwsem);
+
+       module = gb_audio_manager_get_locked(id);
+       if (!module) {
+               up_write(&modules_rwsem);
+               return -EINVAL;
+       }
+       list_del(&module->list);
+       kobject_put(&module->kobj);
+       up_write(&modules_rwsem);
+       ida_simple_remove(&module_id, id);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_remove);
+
+void gb_audio_manager_remove_all(void)
+{
+       struct gb_audio_manager_module *module, *next;
+       int is_empty = 1;
+
+       down_write(&modules_rwsem);
+
+       list_for_each_entry_safe(module, next, &modules_list, list) {
+               list_del(&module->list);
+               kobject_put(&module->kobj);
+               ida_simple_remove(&module_id, module->id);
+       }
+
+       is_empty = list_empty(&modules_list);
+
+       up_write(&modules_rwsem);
+
+       if (!is_empty)
+               pr_warn("Not all nodes were deleted\n");
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all);
+
+struct gb_audio_manager_module *gb_audio_manager_get_module(int id)
+{
+       struct gb_audio_manager_module *module;
+
+       down_read(&modules_rwsem);
+       module = gb_audio_manager_get_locked(id);
+       kobject_get(&module->kobj);
+       up_read(&modules_rwsem);
+       return module;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_get_module);
+
+void gb_audio_manager_put_module(struct gb_audio_manager_module *module)
+{
+       kobject_put(&module->kobj);
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_put_module);
+
+int gb_audio_manager_dump_module(int id)
+{
+       struct gb_audio_manager_module *module;
+
+       down_read(&modules_rwsem);
+       module = gb_audio_manager_get_locked(id);
+       up_read(&modules_rwsem);
+
+       if (!module)
+               return -EINVAL;
+
+       gb_audio_manager_module_dump(module);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module);
+
+void gb_audio_manager_dump_all(void)
+{
+       struct gb_audio_manager_module *module;
+       int count = 0;
+
+       down_read(&modules_rwsem);
+       list_for_each_entry(module, &modules_list, list) {
+               gb_audio_manager_module_dump(module);
+               count++;
+       }
+       up_read(&modules_rwsem);
+
+       pr_info("Number of connected modules: %d\n", count);
+}
+EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all);
+
+/*
+ * module init/deinit
+ */
+static int __init manager_init(void)
+{
+       manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL,
+                                          kernel_kobj);
+       if (!manager_kset)
+               return -ENOMEM;
+
+#ifdef GB_AUDIO_MANAGER_SYSFS
+       gb_audio_manager_sysfs_init(&manager_kset->kobj);
+#endif
+
+       return 0;
+}
+
+static void __exit manager_exit(void)
+{
+       gb_audio_manager_remove_all();
+       kset_unregister(manager_kset);
+       ida_destroy(&module_id);
+}
+
+module_init(manager_init);
+module_exit(manager_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>");
diff --git a/drivers/staging/greybus/audio_manager.h b/drivers/staging/greybus/audio_manager.h
new file mode 100644 (file)
index 0000000..c4ca097
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef _GB_AUDIO_MANAGER_H_
+#define _GB_AUDIO_MANAGER_H_
+
+#include <linux/kobject.h>
+#include <linux/list.h>
+
+#define GB_AUDIO_MANAGER_NAME "gb_audio_manager"
+#define GB_AUDIO_MANAGER_MODULE_NAME_LEN 64
+#define GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "63"
+
+struct gb_audio_manager_module_descriptor {
+       char name[GB_AUDIO_MANAGER_MODULE_NAME_LEN];
+       int slot;
+       int vid;
+       int pid;
+       int cport;
+       unsigned int ip_devices;
+       unsigned int op_devices;
+};
+
+struct gb_audio_manager_module {
+       struct kobject kobj;
+       struct list_head list;
+       int id;
+       struct gb_audio_manager_module_descriptor desc;
+};
+
+/*
+ * Creates a new gb_audio_manager_module_descriptor, using the specified
+ * descriptor.
+ *
+ * Returns a negative result on error, or the id of the newly created module.
+ *
+ */
+int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc);
+
+/*
+ * Removes a connected gb_audio_manager_module_descriptor for the specified ID.
+ *
+ * Returns zero on success, or a negative value on error.
+ */
+int gb_audio_manager_remove(int id);
+
+/*
+ * Removes all connected gb_audio_modules
+ *
+ * Returns zero on success, or a negative value on error.
+ */
+void gb_audio_manager_remove_all(void);
+
+/*
+ * Retrieves a gb_audio_manager_module_descriptor for the specified id.
+ * Returns the gb_audio_manager_module_descriptor structure,
+ * or NULL if there is no module with the specified ID.
+ */
+struct gb_audio_manager_module *gb_audio_manager_get_module(int id);
+
+/*
+ * Decreases the refcount of the module, obtained by the get function.
+ * Modules are removed via gb_audio_manager_remove
+ */
+void gb_audio_manager_put_module(struct gb_audio_manager_module *module);
+
+/*
+ * Dumps the module for the specified id
+ * Return 0 on success
+ */
+int gb_audio_manager_dump_module(int id);
+
+/*
+ * Dumps all connected modules
+ */
+void gb_audio_manager_dump_all(void);
+
+#endif /* _GB_AUDIO_MANAGER_H_ */
diff --git a/drivers/staging/greybus/audio_manager_module.c b/drivers/staging/greybus/audio_manager_module.c
new file mode 100644 (file)
index 0000000..a10e96a
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/slab.h>
+
+#include "audio_manager.h"
+#include "audio_manager_private.h"
+
+#define to_gb_audio_module_attr(x)     \
+               container_of(x, struct gb_audio_manager_module_attribute, attr)
+#define to_gb_audio_module(x)          \
+               container_of(x, struct gb_audio_manager_module, kobj)
+
+struct gb_audio_manager_module_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct gb_audio_manager_module *module,
+                       struct gb_audio_manager_module_attribute *attr,
+                       char *buf);
+       ssize_t (*store)(struct gb_audio_manager_module *module,
+                        struct gb_audio_manager_module_attribute *attr,
+                        const char *buf, size_t count);
+};
+
+static ssize_t gb_audio_module_attr_show(
+       struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       struct gb_audio_manager_module_attribute *attribute;
+       struct gb_audio_manager_module *module;
+
+       attribute = to_gb_audio_module_attr(attr);
+       module = to_gb_audio_module(kobj);
+
+       if (!attribute->show)
+               return -EIO;
+
+       return attribute->show(module, attribute, buf);
+}
+
+static ssize_t gb_audio_module_attr_store(struct kobject *kobj,
+                                         struct attribute *attr,
+                                         const char *buf, size_t len)
+{
+       struct gb_audio_manager_module_attribute *attribute;
+       struct gb_audio_manager_module *module;
+
+       attribute = to_gb_audio_module_attr(attr);
+       module = to_gb_audio_module(kobj);
+
+       if (!attribute->store)
+               return -EIO;
+
+       return attribute->store(module, attribute, buf, len);
+}
+
+static const struct sysfs_ops gb_audio_module_sysfs_ops = {
+       .show = gb_audio_module_attr_show,
+       .store = gb_audio_module_attr_store,
+};
+
+static void gb_audio_module_release(struct kobject *kobj)
+{
+       struct gb_audio_manager_module *module = to_gb_audio_module(kobj);
+
+       pr_info("Destroying audio module #%d\n", module->id);
+       /* TODO -> delete from list */
+       kfree(module);
+}
+
+static ssize_t gb_audio_module_name_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s", module->desc.name);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_name_attribute =
+       __ATTR(name, 0664, gb_audio_module_name_show, NULL);
+
+static ssize_t gb_audio_module_slot_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.slot);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_slot_attribute =
+       __ATTR(slot, 0664, gb_audio_module_slot_show, NULL);
+
+static ssize_t gb_audio_module_vid_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.vid);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_vid_attribute =
+       __ATTR(vid, 0664, gb_audio_module_vid_show, NULL);
+
+static ssize_t gb_audio_module_pid_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.pid);
+}
+
+static struct gb_audio_manager_module_attribute gb_audio_module_pid_attribute =
+       __ATTR(pid, 0664, gb_audio_module_pid_show, NULL);
+
+static ssize_t gb_audio_module_cport_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d", module->desc.cport);
+}
+
+static struct gb_audio_manager_module_attribute
+                                       gb_audio_module_cport_attribute =
+       __ATTR(cport, 0664, gb_audio_module_cport_show, NULL);
+
+static ssize_t gb_audio_module_ip_devices_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "0x%X", module->desc.ip_devices);
+}
+
+static struct gb_audio_manager_module_attribute
+                                       gb_audio_module_ip_devices_attribute =
+       __ATTR(ip_devices, 0664, gb_audio_module_ip_devices_show, NULL);
+
+static ssize_t gb_audio_module_op_devices_show(
+       struct gb_audio_manager_module *module,
+       struct gb_audio_manager_module_attribute *attr, char *buf)
+{
+       return sprintf(buf, "0x%X", module->desc.op_devices);
+}
+
+static struct gb_audio_manager_module_attribute
+                                       gb_audio_module_op_devices_attribute =
+       __ATTR(op_devices, 0664, gb_audio_module_op_devices_show, NULL);
+
+static struct attribute *gb_audio_module_default_attrs[] = {
+       &gb_audio_module_name_attribute.attr,
+       &gb_audio_module_slot_attribute.attr,
+       &gb_audio_module_vid_attribute.attr,
+       &gb_audio_module_pid_attribute.attr,
+       &gb_audio_module_cport_attribute.attr,
+       &gb_audio_module_ip_devices_attribute.attr,
+       &gb_audio_module_op_devices_attribute.attr,
+       NULL,   /* need to NULL terminate the list of attributes */
+};
+
+static struct kobj_type gb_audio_module_type = {
+       .sysfs_ops = &gb_audio_module_sysfs_ops,
+       .release = gb_audio_module_release,
+       .default_attrs = gb_audio_module_default_attrs,
+};
+
+static void send_add_uevent(struct gb_audio_manager_module *module)
+{
+       char name_string[128];
+       char slot_string[64];
+       char vid_string[64];
+       char pid_string[64];
+       char cport_string[64];
+       char ip_devices_string[64];
+       char op_devices_string[64];
+
+       char *envp[] = {
+               name_string,
+               slot_string,
+               vid_string,
+               pid_string,
+               cport_string,
+               ip_devices_string,
+               op_devices_string,
+               NULL
+       };
+
+       snprintf(name_string, 128, "NAME=%s", module->desc.name);
+       snprintf(slot_string, 64, "SLOT=%d", module->desc.slot);
+       snprintf(vid_string, 64, "VID=%d", module->desc.vid);
+       snprintf(pid_string, 64, "PID=%d", module->desc.pid);
+       snprintf(cport_string, 64, "CPORT=%d", module->desc.cport);
+       snprintf(ip_devices_string, 64, "I/P DEVICES=0x%X",
+                module->desc.ip_devices);
+       snprintf(op_devices_string, 64, "O/P DEVICES=0x%X",
+                module->desc.op_devices);
+
+       kobject_uevent_env(&module->kobj, KOBJ_ADD, envp);
+}
+
+int gb_audio_manager_module_create(
+       struct gb_audio_manager_module **module,
+       struct kset *manager_kset,
+       int id, struct gb_audio_manager_module_descriptor *desc)
+{
+       int err;
+       struct gb_audio_manager_module *m;
+
+       m = kzalloc(sizeof(*m), GFP_ATOMIC);
+       if (!m)
+               return -ENOMEM;
+
+       /* Initialize the node */
+       INIT_LIST_HEAD(&m->list);
+
+       /* Set the module id */
+       m->id = id;
+
+       /* Copy the provided descriptor */
+       memcpy(&m->desc, desc, sizeof(*desc));
+
+       /* set the kset */
+       m->kobj.kset = manager_kset;
+
+       /*
+        * Initialize and add the kobject to the kernel.  All the default files
+        * will be created here.  As we have already specified a kset for this
+        * kobject, we don't have to set a parent for the kobject, the kobject
+        * will be placed beneath that kset automatically.
+        */
+       err = kobject_init_and_add(&m->kobj, &gb_audio_module_type, NULL, "%d",
+                                  id);
+       if (err) {
+               pr_err("failed initializing kobject for audio module #%d\n",
+                      id);
+               kobject_put(&m->kobj);
+               return err;
+       }
+
+       /*
+        * Notify the object was created
+        */
+       send_add_uevent(m);
+
+       *module = m;
+       pr_info("Created audio module #%d\n", id);
+       return 0;
+}
+
+void gb_audio_manager_module_dump(struct gb_audio_manager_module *module)
+{
+       pr_info("audio module #%d name=%s slot=%d vid=%d pid=%d cport=%d i/p devices=0x%X o/p devices=0x%X\n",
+               module->id,
+               module->desc.name,
+               module->desc.slot,
+               module->desc.vid,
+               module->desc.pid,
+               module->desc.cport,
+               module->desc.ip_devices,
+               module->desc.op_devices);
+}
diff --git a/drivers/staging/greybus/audio_manager_private.h b/drivers/staging/greybus/audio_manager_private.h
new file mode 100644 (file)
index 0000000..079ce95
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#ifndef _GB_AUDIO_MANAGER_PRIVATE_H_
+#define _GB_AUDIO_MANAGER_PRIVATE_H_
+
+#include <linux/kobject.h>
+
+#include "audio_manager.h"
+
+int gb_audio_manager_module_create(
+       struct gb_audio_manager_module **module,
+       struct kset *manager_kset,
+       int id, struct gb_audio_manager_module_descriptor *desc);
+
+/* module destroyed via kobject_put */
+
+void gb_audio_manager_module_dump(struct gb_audio_manager_module *module);
+
+/* sysfs control */
+void gb_audio_manager_sysfs_init(struct kobject *kobj);
+
+#endif /* _GB_AUDIO_MANAGER_PRIVATE_H_ */
diff --git a/drivers/staging/greybus/audio_manager_sysfs.c b/drivers/staging/greybus/audio_manager_sysfs.c
new file mode 100644 (file)
index 0000000..d8bf859
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Greybus operations
+ *
+ * Copyright 2015-2016 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include <linux/string.h>
+#include <linux/sysfs.h>
+
+#include "audio_manager.h"
+#include "audio_manager_private.h"
+
+static ssize_t manager_sysfs_add_store(
+       struct kobject *kobj, struct kobj_attribute *attr,
+       const char *buf, size_t count)
+{
+       struct gb_audio_manager_module_descriptor desc = { {0} };
+
+       int num = sscanf(buf,
+                       "name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s "
+                       "slot=%d vid=%d pid=%d cport=%d i/p devices=0x%X"
+                       "o/p devices=0x%X",
+                       desc.name, &desc.slot, &desc.vid, &desc.pid,
+                       &desc.cport, &desc.ip_devices, &desc.op_devices);
+
+       if (num != 7)
+               return -EINVAL;
+
+       num = gb_audio_manager_add(&desc);
+       if (num < 0)
+               return -EINVAL;
+
+       return count;
+}
+
+static struct kobj_attribute manager_add_attribute =
+       __ATTR(add, 0664, NULL, manager_sysfs_add_store);
+
+static ssize_t manager_sysfs_remove_store(
+       struct kobject *kobj, struct kobj_attribute *attr,
+       const char *buf, size_t count)
+{
+       int id;
+
+       int num = sscanf(buf, "%d", &id);
+
+       if (num != 1)
+               return -EINVAL;
+
+       num = gb_audio_manager_remove(id);
+       if (num)
+               return num;
+
+       return count;
+}
+
+static struct kobj_attribute manager_remove_attribute =
+       __ATTR(remove, 0664, NULL, manager_sysfs_remove_store);
+
+static ssize_t manager_sysfs_dump_store(
+       struct kobject *kobj, struct kobj_attribute *attr,
+       const char *buf, size_t count)
+{
+       int id;
+
+       int num = sscanf(buf, "%d", &id);
+
+       if (num == 1) {
+               num = gb_audio_manager_dump_module(id);
+               if (num)
+                       return num;
+       } else if (!strncmp("all", buf, 3))
+               gb_audio_manager_dump_all();
+       else
+               return -EINVAL;
+
+       return count;
+}
+
+static struct kobj_attribute manager_dump_attribute =
+       __ATTR(dump, 0664, NULL, manager_sysfs_dump_store);
+
+static void manager_sysfs_init_attribute(
+               struct kobject *kobj, struct kobj_attribute *kattr)
+{
+       int err;
+
+       err = sysfs_create_file(kobj, &kattr->attr);
+       if (err) {
+               pr_warn("creating the sysfs entry for %s failed: %d\n",
+                       kattr->attr.name, err);
+       }
+}
+
+void gb_audio_manager_sysfs_init(struct kobject *kobj)
+{
+       manager_sysfs_init_attribute(kobj, &manager_add_attribute);
+       manager_sysfs_init_attribute(kobj, &manager_remove_attribute);
+       manager_sysfs_init_attribute(kobj, &manager_dump_attribute);
+}
diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c
new file mode 100644 (file)
index 0000000..411735d
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Greybus audio driver
+ * Copyright 2015 Google Inc.
+ * Copyright 2015 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "audio_codec.h"
+#include "audio_apbridgea.h"
+#include "audio_manager.h"
+
+/*
+ * gb_snd management functions
+ */
+
+static int gbaudio_request_jack(struct gbaudio_module_info *module,
+                                 struct gb_audio_jack_event_request *req)
+{
+       int report;
+       struct snd_jack *jack = module->headset_jack.jack;
+       struct snd_jack *btn_jack = module->button_jack.jack;
+
+       if (!jack) {
+               dev_err_ratelimited(module->dev,
+                       "Invalid jack event received:type: %u, event: %u\n",
+                       req->jack_attribute, req->event);
+               return -EINVAL;
+       }
+
+       dev_warn_ratelimited(module->dev,
+                            "Jack Event received: type: %u, event: %u\n",
+                            req->jack_attribute, req->event);
+
+       if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) {
+               module->jack_type = 0;
+               if (btn_jack && module->button_status) {
+                       snd_soc_jack_report(&module->button_jack, 0,
+                                           module->button_mask);
+                       module->button_status = 0;
+               }
+               snd_soc_jack_report(&module->headset_jack, 0,
+                                   module->jack_mask);
+               return 0;
+       }
+
+       report = req->jack_attribute & module->jack_mask;
+       if (!report) {
+               dev_err_ratelimited(module->dev,
+                       "Invalid jack event received:type: %u, event: %u\n",
+                       req->jack_attribute, req->event);
+               return -EINVAL;
+       }
+
+       if (module->jack_type)
+               dev_warn_ratelimited(module->dev,
+                                    "Modifying jack from %d to %d\n",
+                                    module->jack_type, report);
+
+       module->jack_type = report;
+       snd_soc_jack_report(&module->headset_jack, report, module->jack_mask);
+
+       return 0;
+}
+
+static int gbaudio_request_button(struct gbaudio_module_info *module,
+                                 struct gb_audio_button_event_request *req)
+{
+       int soc_button_id, report;
+       struct snd_jack *btn_jack = module->button_jack.jack;
+
+       if (!btn_jack) {
+               dev_err_ratelimited(module->dev,
+                       "Invalid button event received:type: %u, event: %u\n",
+                       req->button_id, req->event);
+               return -EINVAL;
+       }
+
+       dev_warn_ratelimited(module->dev,
+                            "Button Event received: id: %u, event: %u\n",
+                            req->button_id, req->event);
+
+       /* currently supports 4 buttons only */
+       if (!module->jack_type) {
+               dev_err_ratelimited(module->dev,
+                                   "Jack not present. Bogus event!!\n");
+               return -EINVAL;
+       }
+
+       report = module->button_status & module->button_mask;
+       soc_button_id = 0;
+
+       switch (req->button_id) {
+       case 1:
+               soc_button_id = SND_JACK_BTN_0 & module->button_mask;
+               break;
+
+       case 2:
+               soc_button_id = SND_JACK_BTN_1 & module->button_mask;
+               break;
+
+       case 3:
+               soc_button_id = SND_JACK_BTN_2 & module->button_mask;
+               break;
+
+       case 4:
+               soc_button_id = SND_JACK_BTN_3 & module->button_mask;
+               break;
+       }
+
+       if (!soc_button_id) {
+               dev_err_ratelimited(module->dev,
+                                   "Invalid button request received\n");
+               return -EINVAL;
+       }
+
+       if (req->event == GB_AUDIO_BUTTON_EVENT_PRESS)
+               report = report | soc_button_id;
+       else
+               report = report & ~soc_button_id;
+
+       module->button_status = report;
+
+       snd_soc_jack_report(&module->button_jack, report, module->button_mask);
+
+       return 0;
+}
+
+static int gbaudio_request_stream(struct gbaudio_module_info *module,
+                                 struct gb_audio_streaming_event_request *req)
+{
+       dev_warn(module->dev, "Audio Event received: cport: %u, event: %u\n",
+                req->data_cport, req->event);
+
+       return 0;
+}
+
+static int gbaudio_codec_request_handler(struct gb_operation *op)
+{
+       struct gb_connection *connection = op->connection;
+       struct gbaudio_module_info *module =
+               greybus_get_drvdata(connection->bundle);
+       struct gb_operation_msg_hdr *header = op->request->header;
+       struct gb_audio_streaming_event_request *stream_req;
+       struct gb_audio_jack_event_request *jack_req;
+       struct gb_audio_button_event_request *button_req;
+       int ret;
+
+       switch (header->type) {
+       case GB_AUDIO_TYPE_STREAMING_EVENT:
+               stream_req = op->request->payload;
+               ret = gbaudio_request_stream(module, stream_req);
+               break;
+
+       case GB_AUDIO_TYPE_JACK_EVENT:
+               jack_req = op->request->payload;
+               ret = gbaudio_request_jack(module, jack_req);
+               break;
+
+       case GB_AUDIO_TYPE_BUTTON_EVENT:
+               button_req = op->request->payload;
+               ret = gbaudio_request_button(module, button_req);
+               break;
+
+       default:
+               dev_err_ratelimited(&connection->bundle->dev,
+                                   "Invalid Audio Event received\n");
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule,
+                               struct greybus_descriptor_cport *cport_desc,
+                               struct gb_bundle *bundle)
+{
+       struct gb_connection *connection;
+
+       /* Management Cport */
+       if (gbmodule->mgmt_connection) {
+               dev_err(&bundle->dev,
+                       "Can't have multiple Management connections\n");
+               return -ENODEV;
+       }
+
+       connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
+                                         gbaudio_codec_request_handler);
+       if (IS_ERR(connection))
+               return PTR_ERR(connection);
+
+       greybus_set_drvdata(bundle, gbmodule);
+       gbmodule->mgmt_connection = connection;
+
+       return 0;
+}
+
+static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule,
+                               struct greybus_descriptor_cport *cport_desc,
+                               struct gb_bundle *bundle)
+{
+       struct gb_connection *connection;
+       struct gbaudio_data_connection *dai;
+
+       dai = devm_kzalloc(gbmodule->dev, sizeof(*dai), GFP_KERNEL);
+       if (!dai) {
+               dev_err(gbmodule->dev, "DAI Malloc failure\n");
+               return -ENOMEM;
+       }
+
+       connection = gb_connection_create_offloaded(bundle,
+                                       le16_to_cpu(cport_desc->id),
+                                       GB_CONNECTION_FLAG_CSD);
+       if (IS_ERR(connection)) {
+               devm_kfree(gbmodule->dev, dai);
+               return PTR_ERR(connection);
+       }
+
+       greybus_set_drvdata(bundle, gbmodule);
+       dai->id = 0;
+       dai->data_cport = connection->intf_cport_id;
+       dai->connection = connection;
+       list_add(&dai->list, &gbmodule->data_list);
+
+       return 0;
+}
+
+/*
+ * This is the basic hook get things initialized and registered w/ gb
+ */
+
+static int gb_audio_probe(struct gb_bundle *bundle,
+                         const struct greybus_bundle_id *id)
+{
+       struct device *dev = &bundle->dev;
+       struct gbaudio_module_info *gbmodule;
+       struct greybus_descriptor_cport *cport_desc;
+       struct gb_audio_manager_module_descriptor desc;
+       struct gbaudio_data_connection *dai, *_dai;
+       int ret, i;
+       struct gb_audio_topology *topology;
+
+       /* There should be at least one Management and one Data cport */
+       if (bundle->num_cports < 2)
+               return -ENODEV;
+
+       /*
+        * There can be only one Management connection and any number of data
+        * connections.
+        */
+       gbmodule = devm_kzalloc(dev, sizeof(*gbmodule), GFP_KERNEL);
+       if (!gbmodule)
+               return -ENOMEM;
+
+       gbmodule->num_data_connections = bundle->num_cports - 1;
+       INIT_LIST_HEAD(&gbmodule->data_list);
+       INIT_LIST_HEAD(&gbmodule->widget_list);
+       INIT_LIST_HEAD(&gbmodule->ctl_list);
+       INIT_LIST_HEAD(&gbmodule->widget_ctl_list);
+       gbmodule->dev = dev;
+       snprintf(gbmodule->name, NAME_SIZE, "%s.%s", dev->driver->name,
+                dev_name(dev));
+       greybus_set_drvdata(bundle, gbmodule);
+
+       /* Create all connections */
+       for (i = 0; i < bundle->num_cports; i++) {
+               cport_desc = &bundle->cport_desc[i];
+
+               switch (cport_desc->protocol_id) {
+               case GREYBUS_PROTOCOL_AUDIO_MGMT:
+                       ret = gb_audio_add_mgmt_connection(gbmodule, cport_desc,
+                                                          bundle);
+                       if (ret)
+                               goto destroy_connections;
+                       break;
+               case GREYBUS_PROTOCOL_AUDIO_DATA:
+                       ret = gb_audio_add_data_connection(gbmodule, cport_desc,
+                                                          bundle);
+                       if (ret)
+                               goto destroy_connections;
+                       break;
+               default:
+                       dev_err(dev, "Unsupported protocol: 0x%02x\n",
+                               cport_desc->protocol_id);
+                       ret = -ENODEV;
+                       goto destroy_connections;
+               }
+       }
+
+       /* There must be a management cport */
+       if (!gbmodule->mgmt_connection) {
+               ret = -EINVAL;
+               dev_err(dev, "Missing management connection\n");
+               goto destroy_connections;
+       }
+
+       /* Initialize management connection */
+       ret = gb_connection_enable(gbmodule->mgmt_connection);
+       if (ret) {
+               dev_err(dev, "%d: Error while enabling mgmt connection\n", ret);
+               goto destroy_connections;
+       }
+       gbmodule->dev_id = gbmodule->mgmt_connection->intf->interface_id;
+
+       /*
+        * FIXME: malloc for topology happens via audio_gb driver
+        * should be done within codec driver itself
+        */
+       ret = gb_audio_gb_get_topology(gbmodule->mgmt_connection, &topology);
+       if (ret) {
+               dev_err(dev, "%d:Error while fetching topology\n", ret);
+               goto disable_connection;
+       }
+
+       /* process topology data */
+       ret = gbaudio_tplg_parse_data(gbmodule, topology);
+       if (ret) {
+               dev_err(dev, "%d:Error while parsing topology data\n",
+                         ret);
+               goto free_topology;
+       }
+       gbmodule->topology = topology;
+
+       /* Initialize data connections */
+       list_for_each_entry(dai, &gbmodule->data_list, list) {
+               ret = gb_connection_enable(dai->connection);
+               if (ret) {
+                       dev_err(dev,
+                               "%d:Error while enabling %d:data connection\n",
+                               ret, dai->data_cport);
+                       goto disable_data_connection;
+               }
+       }
+
+       /* register module with gbcodec */
+       ret = gbaudio_register_module(gbmodule);
+       if (ret)
+               goto disable_data_connection;
+
+       /* inform above layer for uevent */
+       dev_dbg(dev, "Inform set_event:%d to above layer\n", 1);
+       /* prepare for the audio manager */
+       strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN);
+       desc.slot = 1; /* todo */
+       desc.vid = 2; /* todo */
+       desc.pid = 3; /* todo */
+       desc.cport = gbmodule->dev_id;
+       desc.op_devices = gbmodule->op_devices;
+       desc.ip_devices = gbmodule->ip_devices;
+       gbmodule->manager_id = gb_audio_manager_add(&desc);
+
+       dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       return 0;
+
+disable_data_connection:
+       list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list)
+               gb_connection_disable(dai->connection);
+       gbaudio_tplg_release(gbmodule);
+       gbmodule->topology = NULL;
+
+free_topology:
+       kfree(topology);
+
+disable_connection:
+       gb_connection_disable(gbmodule->mgmt_connection);
+
+destroy_connections:
+       list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) {
+               gb_connection_destroy(dai->connection);
+               list_del(&dai->list);
+               devm_kfree(dev, dai);
+       }
+
+       if (gbmodule->mgmt_connection)
+               gb_connection_destroy(gbmodule->mgmt_connection);
+
+       devm_kfree(dev, gbmodule);
+
+       return ret;
+}
+
+static void gb_audio_disconnect(struct gb_bundle *bundle)
+{
+       struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
+       struct gbaudio_data_connection *dai, *_dai;
+
+       gb_pm_runtime_get_sync(bundle);
+
+       /* cleanup module related resources first */
+       gbaudio_unregister_module(gbmodule);
+
+       /* inform uevent to above layers */
+       gb_audio_manager_remove(gbmodule->manager_id);
+
+       gbaudio_tplg_release(gbmodule);
+       kfree(gbmodule->topology);
+       gbmodule->topology = NULL;
+       gb_connection_disable(gbmodule->mgmt_connection);
+       list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) {
+               gb_connection_disable(dai->connection);
+               gb_connection_destroy(dai->connection);
+               list_del(&dai->list);
+               devm_kfree(gbmodule->dev, dai);
+       }
+       gb_connection_destroy(gbmodule->mgmt_connection);
+       gbmodule->mgmt_connection = NULL;
+
+       devm_kfree(&bundle->dev, gbmodule);
+}
+
+static const struct greybus_bundle_id gb_audio_id_table[] = {
+       { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) },
+       { }
+};
+MODULE_DEVICE_TABLE(greybus, gb_audio_id_table);
+
+#ifdef CONFIG_PM_RUNTIME
+static int gb_audio_suspend(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
+       struct gbaudio_data_connection *dai;
+
+       list_for_each_entry(dai, &gbmodule->data_list, list)
+               gb_connection_disable(dai->connection);
+
+       gb_connection_disable(gbmodule->mgmt_connection);
+
+       return 0;
+}
+
+static int gb_audio_resume(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
+       struct gbaudio_data_connection *dai;
+       int ret;
+
+       ret = gb_connection_enable(gbmodule->mgmt_connection);
+       if (ret) {
+               dev_err(dev, "%d:Error while enabling mgmt connection\n", ret);
+               return ret;
+       }
+
+       list_for_each_entry(dai, &gbmodule->data_list, list) {
+               ret = gb_connection_enable(dai->connection);
+               if (ret) {
+                       dev_err(dev,
+                               "%d:Error while enabling %d:data connection\n",
+                               ret, dai->data_cport);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops gb_audio_pm_ops = {
+       SET_RUNTIME_PM_OPS(gb_audio_suspend, gb_audio_resume, NULL)
+};
+
+static struct greybus_driver gb_audio_driver = {
+       .name           = "gb-audio",
+       .probe          = gb_audio_probe,
+       .disconnect     = gb_audio_disconnect,
+       .id_table       = gb_audio_id_table,
+       .driver.pm      = &gb_audio_pm_ops,
+};
+module_greybus_driver(gb_audio_driver);
+
+MODULE_DESCRIPTION("Greybus Audio module driver");
+MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:gbaudio-module");
diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c
new file mode 100644 (file)
index 0000000..5eef536
--- /dev/null
@@ -0,0 +1,1442 @@
+/*
+ * Greybus audio driver
+ * Copyright 2015-2016 Google Inc.
+ * Copyright 2015-2016 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#include "audio_codec.h"
+#include "greybus_protocols.h"
+
+#define GBAUDIO_INVALID_ID     0xFF
+
+/* mixer control */
+struct gb_mixer_control {
+       int min, max;
+       unsigned int reg, rreg, shift, rshift, invert;
+};
+
+struct gbaudio_ctl_pvt {
+       unsigned int ctl_id;
+       unsigned int data_cport;
+       unsigned int access;
+       unsigned int vcount;
+       struct gb_audio_ctl_elem_info *info;
+};
+
+static struct gbaudio_module_info *find_gb_module(
+                                       struct gbaudio_codec_info *codec,
+                                       char const *name)
+{
+       int dev_id, ret;
+       char begin[NAME_SIZE];
+       struct gbaudio_module_info *module;
+
+       if (!name)
+               return NULL;
+
+       ret = sscanf(name, "%s %d", begin, &dev_id);
+       dev_dbg(codec->dev, "%s:Find module#%d\n", __func__, dev_id);
+
+       mutex_lock(&codec->lock);
+       list_for_each_entry(module, &codec->module_list, list) {
+               if (module->dev_id == dev_id) {
+                       mutex_unlock(&codec->lock);
+                       return module;
+               }
+       }
+       mutex_unlock(&codec->lock);
+       dev_warn(codec->dev, "%s: module#%d missing in codec list\n", name,
+                dev_id);
+       return NULL;
+}
+
+static const char *gbaudio_map_controlid(struct gbaudio_module_info *module,
+                                        __u8 control_id, __u8 index)
+{
+       struct gbaudio_control *control;
+
+       if (control_id == GBAUDIO_INVALID_ID)
+               return NULL;
+
+       list_for_each_entry(control, &module->ctl_list, list) {
+               if (control->id == control_id) {
+                       if (index == GBAUDIO_INVALID_ID)
+                               return control->name;
+                       if (index >= control->items)
+                               return NULL;
+                       return control->texts[index];
+               }
+       }
+       list_for_each_entry(control, &module->widget_ctl_list, list) {
+               if (control->id == control_id) {
+                       if (index == GBAUDIO_INVALID_ID)
+                               return control->name;
+                       if (index >= control->items)
+                               return NULL;
+                       return control->texts[index];
+               }
+       }
+       return NULL;
+}
+
+static int gbaudio_map_controlname(struct gbaudio_module_info *module,
+                                  const char *name)
+{
+       struct gbaudio_control *control;
+
+       list_for_each_entry(control, &module->ctl_list, list) {
+               if (!strncmp(control->name, name, NAME_SIZE))
+                       return control->id;
+       }
+
+       dev_warn(module->dev, "%s: missing in modules controls list\n", name);
+
+       return -EINVAL;
+}
+
+static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module,
+                                   const char *name)
+{
+       struct gbaudio_control *control;
+
+       list_for_each_entry(control, &module->widget_ctl_list, list) {
+               if (!strncmp(control->wname, name, NAME_SIZE))
+                       return control->id;
+       }
+       dev_warn(module->dev, "%s: missing in modules controls list\n", name);
+
+       return -EINVAL;
+}
+
+static int gbaudio_map_widgetname(struct gbaudio_module_info *module,
+                                 const char *name)
+{
+       struct gbaudio_widget *widget;
+       list_for_each_entry(widget, &module->widget_list, list) {
+               if (!strncmp(widget->name, name, NAME_SIZE))
+                       return widget->id;
+       }
+       dev_warn(module->dev, "%s: missing in modules widgets list\n", name);
+
+       return -EINVAL;
+}
+
+static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module,
+                                       __u8 widget_id)
+{
+       struct gbaudio_widget *widget;
+
+       list_for_each_entry(widget, &module->widget_list, list) {
+               if (widget->id == widget_id)
+                       return widget->name;
+       }
+       return NULL;
+}
+
+static const char **gb_generate_enum_strings(struct gbaudio_module_info *gb,
+                                            struct gb_audio_enumerated *gbenum)
+{
+       const char **strings;
+       int i;
+       __u8 *data;
+
+       strings = devm_kzalloc(gb->dev, sizeof(char *) * gbenum->items,
+                              GFP_KERNEL);
+       data = gbenum->names;
+
+       for (i = 0; i < gbenum->items; i++) {
+               strings[i] = (const char *)data;
+               while (*data != '\0')
+                       data++;
+               data++;
+       }
+
+       return strings;
+}
+
+static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol,
+                    struct snd_ctl_elem_info *uinfo)
+{
+       unsigned int max;
+       const char *name;
+       struct gbaudio_ctl_pvt *data;
+       struct gb_audio_ctl_elem_info *info;
+       struct gbaudio_module_info *module;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
+       data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
+       info = (struct gb_audio_ctl_elem_info *)data->info;
+
+       if (!info) {
+               dev_err(module->dev, "NULL info for %s\n", uinfo->id.name);
+               return -EINVAL;
+       }
+
+       /* update uinfo */
+       uinfo->access = data->access;
+       uinfo->count = data->vcount;
+       uinfo->type = (snd_ctl_elem_type_t)info->type;
+
+       switch (info->type) {
+       case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN:
+       case GB_AUDIO_CTL_ELEM_TYPE_INTEGER:
+               uinfo->value.integer.min = info->value.integer.min;
+               uinfo->value.integer.max = info->value.integer.max;
+               break;
+       case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
+               max = info->value.enumerated.items;
+               uinfo->value.enumerated.items = max;
+               if (uinfo->value.enumerated.item > max - 1)
+                       uinfo->value.enumerated.item = max - 1;
+               module = find_gb_module(gbcodec, kcontrol->id.name);
+               if (!module)
+                       return -EINVAL;
+               name = gbaudio_map_controlid(module, data->ctl_id,
+                                            uinfo->value.enumerated.item);
+               strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE);
+               break;
+       default:
+               dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n",
+                       info->type, kcontrol->id.name);
+               break;
+       }
+       return 0;
+}
+
+static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       int ret;
+       struct gb_audio_ctl_elem_info *info;
+       struct gbaudio_ctl_pvt *data;
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct gbaudio_module_info *module;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
+
+       dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
+       info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
+                                     GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       if (ret) {
+               dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
+                                   __func__, kcontrol->id.name);
+               return ret;
+       }
+
+       /* update ucontrol */
+       switch (info->type) {
+       case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN:
+       case GB_AUDIO_CTL_ELEM_TYPE_INTEGER:
+               ucontrol->value.integer.value[0] =
+                       gbvalue.value.integer_value[0];
+               if (data->vcount == 2)
+                       ucontrol->value.integer.value[1] =
+                               gbvalue.value.integer_value[1];
+               break;
+       case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
+               ucontrol->value.enumerated.item[0] =
+                       gbvalue.value.enumerated_item[0];
+               if (data->vcount == 2)
+                       ucontrol->value.enumerated.item[1] =
+                               gbvalue.value.enumerated_item[1];
+               break;
+       default:
+               dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n",
+                       info->type, kcontrol->id.name);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       int ret = 0;
+       struct gb_audio_ctl_elem_info *info;
+       struct gbaudio_ctl_pvt *data;
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct gbaudio_module_info *module;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
+
+       dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
+       info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
+
+       /* update ucontrol */
+       switch (info->type) {
+       case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN:
+       case GB_AUDIO_CTL_ELEM_TYPE_INTEGER:
+               gbvalue.value.integer_value[0] =
+                       ucontrol->value.integer.value[0];
+               if (data->vcount == 2)
+                       gbvalue.value.integer_value[1] =
+                               ucontrol->value.integer.value[1];
+               break;
+       case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
+               gbvalue.value.enumerated_item[0] =
+                       ucontrol->value.enumerated.item[0];
+               if (data->vcount == 2)
+                       gbvalue.value.enumerated_item[1] =
+                               ucontrol->value.enumerated.item[1];
+               break;
+       default:
+               dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n",
+                       info->type, kcontrol->id.name);
+               ret = -EINVAL;
+               break;
+       }
+
+       if (ret)
+               return ret;
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id,
+                                     GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       if (ret) {
+               dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
+                                   __func__, kcontrol->id.name);
+       }
+
+       return ret;
+}
+
+#define SOC_MIXER_GB(xname, kcount, data) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .count = kcount, .info = gbcodec_mixer_ctl_info, \
+       .get = gbcodec_mixer_ctl_get, .put = gbcodec_mixer_ctl_put, \
+       .private_value = (unsigned long)data }
+
+/*
+ * although below callback functions seems redundant to above functions.
+ * same are kept to allow provision for different handling in case
+ * of DAPM related sequencing, etc.
+ */
+static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol,
+                    struct snd_ctl_elem_info *uinfo)
+{
+       int platform_max, platform_min;
+       struct gbaudio_ctl_pvt *data;
+       struct gb_audio_ctl_elem_info *info;
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = widget->codec;
+
+       dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
+       data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
+       info = (struct gb_audio_ctl_elem_info *)data->info;
+
+       /* update uinfo */
+       platform_max = info->value.integer.max;
+       platform_min = info->value.integer.min;
+
+       if (platform_max == 1 &&
+           !strnstr(kcontrol->id.name, " Volume", NAME_SIZE))
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+       uinfo->count = data->vcount;
+       uinfo->value.integer.min = 0;
+       if (info->value.integer.min < 0 &&
+           (uinfo->type == SNDRV_CTL_ELEM_TYPE_INTEGER))
+               uinfo->value.integer.max = platform_max - platform_min;
+       else
+               uinfo->value.integer.max = platform_max;
+
+       return 0;
+}
+
+static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       int ret;
+       struct gb_audio_ctl_elem_info *info;
+       struct gbaudio_ctl_pvt *data;
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct gbaudio_module_info *module;
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = widget->codec;
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
+
+       dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
+       info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
+
+       if (data->vcount == 2)
+               dev_warn(widget->dapm->dev,
+                        "GB: Control '%s' is stereo, which is not supported\n",
+                        kcontrol->id.name);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
+                                     GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       if (ret) {
+               dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
+                                   __func__, kcontrol->id.name);
+               return ret;
+       }
+       /* update ucontrol */
+       ucontrol->value.integer.value[0] = gbvalue.value.integer_value[0];
+
+       return ret;
+}
+
+static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       int ret, wi, max, connect;
+       unsigned int mask, val;
+       struct gb_audio_ctl_elem_info *info;
+       struct gbaudio_ctl_pvt *data;
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct gbaudio_module_info *module;
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = widget->codec;
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
+
+       dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
+       info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
+
+       if (data->vcount == 2)
+               dev_warn(widget->dapm->dev,
+                        "GB: Control '%s' is stereo, which is not supported\n",
+                        kcontrol->id.name);
+
+       max = info->value.integer.max;
+       mask = (1 << fls(max)) - 1;
+       val = (ucontrol->value.integer.value[0] & mask);
+       connect = !!val;
+
+       /* update ucontrol */
+       if (gbvalue.value.integer_value[0] != val) {
+               for (wi = 0; wi < wlist->num_widgets; wi++) {
+                       widget = wlist->widgets[wi];
+
+                       widget->value = val;
+                       widget->dapm->update = NULL;
+                       snd_soc_dapm_mixer_update_power(widget, kcontrol,
+                                                       connect);
+               }
+               gbvalue.value.integer_value[0] =
+                       ucontrol->value.integer.value[0];
+
+               ret = gb_pm_runtime_get_sync(bundle);
+               if (ret)
+                       return ret;
+
+               ret = gb_audio_gb_set_control(module->mgmt_connection,
+                                             data->ctl_id,
+                                             GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+               gb_pm_runtime_put_autosuspend(bundle);
+
+               if (ret) {
+                       dev_err_ratelimited(codec->dev,
+                                           "%d:Error in %s for %s\n", ret,
+                                           __func__, kcontrol->id.name);
+               }
+       }
+
+       return ret;
+}
+
+#define SOC_DAPM_MIXER_GB(xname, kcount, data) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .count = kcount, .info = gbcodec_mixer_dapm_ctl_info, \
+       .get = gbcodec_mixer_dapm_ctl_get, .put = gbcodec_mixer_dapm_ctl_put, \
+       .private_value = (unsigned long)data}
+
+static int gbcodec_event_spk(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *k, int event)
+{
+       /* Ensure GB speaker is connected */
+
+       return 0;
+}
+
+static int gbcodec_event_hp(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *k, int event)
+{
+       /* Ensure GB module supports jack slot */
+
+       return 0;
+}
+
+static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *k, int event)
+{
+       /* Ensure GB module supports jack slot */
+
+       return 0;
+}
+
+static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w)
+{
+       int ret = 0;
+
+       switch (w->type) {
+       case snd_soc_dapm_spk:
+       case snd_soc_dapm_hp:
+       case snd_soc_dapm_mic:
+       case snd_soc_dapm_output:
+       case snd_soc_dapm_input:
+               if (w->ncontrols)
+                       ret = -EINVAL;
+               break;
+       case snd_soc_dapm_switch:
+       case snd_soc_dapm_mux:
+               if (w->ncontrols != 1)
+                       ret = -EINVAL;
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       int ret, ctl_id;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct gbaudio_module_info *module;
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
+
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       ctl_id = gbaudio_map_controlname(module, kcontrol->id.name);
+       if (ctl_id < 0)
+               return -EINVAL;
+
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id,
+                                     GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       if (ret) {
+               dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
+                                   __func__, kcontrol->id.name);
+               return ret;
+       }
+
+       ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0];
+       if (e->shift_l != e->shift_r)
+               ucontrol->value.enumerated.item[1] =
+                       gbvalue.value.enumerated_item[1];
+
+       return 0;
+}
+
+static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       int ret, ctl_id;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct gbaudio_module_info *module;
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
+
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       ctl_id = gbaudio_map_controlname(module, kcontrol->id.name);
+       if (ctl_id < 0)
+               return -EINVAL;
+
+       if (ucontrol->value.enumerated.item[0] > e->max - 1)
+               return -EINVAL;
+       gbvalue.value.enumerated_item[0] = ucontrol->value.enumerated.item[0];
+
+       if (e->shift_l != e->shift_r) {
+               if (ucontrol->value.enumerated.item[1] > e->max - 1)
+                       return -EINVAL;
+               gbvalue.value.enumerated_item[1] =
+                       ucontrol->value.enumerated.item[1];
+       }
+
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id,
+                                     GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       if (ret) {
+               dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
+                                   __func__, kcontrol->id.name);
+       }
+
+       return ret;
+}
+
+static int gbaudio_tplg_create_enum_kctl(struct gbaudio_module_info *gb,
+                                        struct snd_kcontrol_new *kctl,
+                                        struct gb_audio_control *ctl)
+{
+       struct soc_enum *gbe;
+       struct gb_audio_enumerated *gb_enum;
+       int i;
+
+       gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL);
+       if (!gbe)
+               return -ENOMEM;
+
+       gb_enum = &ctl->info.value.enumerated;
+
+       /* since count=1, and reg is dummy */
+       gbe->max = gb_enum->items;
+       gbe->texts = gb_generate_enum_strings(gb, gb_enum);
+
+       /* debug enum info */
+       dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items,
+                gb_enum->names_length);
+       for (i = 0; i < gb_enum->items; i++)
+               dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]);
+
+       *kctl = (struct snd_kcontrol_new)
+               SOC_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_ctl_get,
+                            gbcodec_enum_ctl_put);
+       return 0;
+}
+
+static int gbaudio_tplg_create_kcontrol(struct gbaudio_module_info *gb,
+                                       struct snd_kcontrol_new *kctl,
+                                       struct gb_audio_control *ctl)
+{
+       int ret = 0;
+       struct gbaudio_ctl_pvt *ctldata;
+
+       switch (ctl->iface) {
+       case SNDRV_CTL_ELEM_IFACE_MIXER:
+               switch (ctl->info.type) {
+               case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
+                       ret = gbaudio_tplg_create_enum_kctl(gb, kctl, ctl);
+                       break;
+               default:
+                       ctldata = devm_kzalloc(gb->dev,
+                                              sizeof(struct gbaudio_ctl_pvt),
+                                              GFP_KERNEL);
+                       if (!ctldata)
+                               return -ENOMEM;
+                       ctldata->ctl_id = ctl->id;
+                       ctldata->data_cport = ctl->data_cport;
+                       ctldata->access = ctl->access;
+                       ctldata->vcount = ctl->count_values;
+                       ctldata->info = &ctl->info;
+                       *kctl = (struct snd_kcontrol_new)
+                               SOC_MIXER_GB(ctl->name, ctl->count, ctldata);
+                       ctldata = NULL;
+                       break;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dev_dbg(gb->dev, "%s:%d control created\n", ctl->name, ctl->id);
+       return ret;
+}
+
+static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       int ret, ctl_id;
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct gbaudio_module_info *module;
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct snd_soc_codec *codec = widget->codec;
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct gb_bundle *bundle;
+
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name);
+       if (ctl_id < 0)
+               return -EINVAL;
+
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id,
+                                     GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       if (ret) {
+               dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
+                                   __func__, kcontrol->id.name);
+               return ret;
+       }
+
+       ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0];
+       if (e->shift_l != e->shift_r)
+               ucontrol->value.enumerated.item[1] =
+                       gbvalue.value.enumerated_item[1];
+
+       return 0;
+}
+
+static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       int ret, wi, ctl_id;
+       unsigned int val, mux, change;
+       unsigned int mask;
+       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct gb_audio_ctl_elem_value gbvalue;
+       struct gbaudio_module_info *module;
+       struct snd_soc_codec *codec = widget->codec;
+       struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct gb_bundle *bundle;
+
+       if (ucontrol->value.enumerated.item[0] > e->max - 1)
+               return -EINVAL;
+
+       module = find_gb_module(gb, kcontrol->id.name);
+       if (!module)
+               return -EINVAL;
+
+       ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name);
+       if (ctl_id < 0)
+               return -EINVAL;
+
+       change = 0;
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id,
+                                     GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       if (ret) {
+               dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
+                                   __func__, kcontrol->id.name);
+               return ret;
+       }
+
+       mux = ucontrol->value.enumerated.item[0];
+       val = mux << e->shift_l;
+       mask = e->mask << e->shift_l;
+
+       if (gbvalue.value.enumerated_item[0] !=
+           ucontrol->value.enumerated.item[0]) {
+               change = 1;
+               gbvalue.value.enumerated_item[0] =
+                       ucontrol->value.enumerated.item[0];
+       }
+
+       if (e->shift_l != e->shift_r) {
+               if (ucontrol->value.enumerated.item[1] > e->max - 1)
+                       return -EINVAL;
+               val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+               mask |= e->mask << e->shift_r;
+               if (gbvalue.value.enumerated_item[1] !=
+                   ucontrol->value.enumerated.item[1]) {
+                       change = 1;
+                       gbvalue.value.enumerated_item[1] =
+                               ucontrol->value.enumerated.item[1];
+               }
+       }
+
+       if (change) {
+               ret = gb_pm_runtime_get_sync(bundle);
+               if (ret)
+                       return ret;
+
+               ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id,
+                                             GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+               gb_pm_runtime_put_autosuspend(bundle);
+
+               if (ret) {
+                       dev_err_ratelimited(codec->dev,
+                                           "%d:Error in %s for %s\n", ret,
+                                           __func__, kcontrol->id.name);
+               }
+               for (wi = 0; wi < wlist->num_widgets; wi++) {
+                       widget = wlist->widgets[wi];
+
+                       widget->value = val;
+                       widget->dapm->update = NULL;
+                       snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+               }
+       }
+
+       return change;
+}
+
+static int gbaudio_tplg_create_enum_ctl(struct gbaudio_module_info *gb,
+                                       struct snd_kcontrol_new *kctl,
+                                       struct gb_audio_control *ctl)
+{
+       struct soc_enum *gbe;
+       struct gb_audio_enumerated *gb_enum;
+       int i;
+
+       gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL);
+       if (!gbe)
+               return -ENOMEM;
+
+       gb_enum = &ctl->info.value.enumerated;
+
+       /* since count=1, and reg is dummy */
+       gbe->max = gb_enum->items;
+       gbe->texts = gb_generate_enum_strings(gb, gb_enum);
+
+       /* debug enum info */
+       dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items,
+                gb_enum->names_length);
+       for (i = 0; i < gb_enum->items; i++)
+               dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]);
+
+       *kctl = (struct snd_kcontrol_new)
+               SOC_DAPM_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_dapm_ctl_get,
+                                 gbcodec_enum_dapm_ctl_put);
+       return 0;
+}
+
+static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_module_info *gb,
+                                            struct snd_kcontrol_new *kctl,
+                                            struct gb_audio_control *ctl)
+{
+       struct gbaudio_ctl_pvt *ctldata;
+
+       ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt),
+                              GFP_KERNEL);
+       if (!ctldata)
+               return -ENOMEM;
+       ctldata->ctl_id = ctl->id;
+       ctldata->data_cport = ctl->data_cport;
+       ctldata->access = ctl->access;
+       ctldata->vcount = ctl->count_values;
+       ctldata->info = &ctl->info;
+       *kctl = (struct snd_kcontrol_new)
+               SOC_DAPM_MIXER_GB(ctl->name, ctl->count, ctldata);
+
+       return 0;
+}
+
+static int gbaudio_tplg_create_wcontrol(struct gbaudio_module_info *gb,
+                                            struct snd_kcontrol_new *kctl,
+                                            struct gb_audio_control *ctl)
+{
+       int ret;
+
+       switch (ctl->iface) {
+       case SNDRV_CTL_ELEM_IFACE_MIXER:
+               switch (ctl->info.type) {
+               case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED:
+                       ret = gbaudio_tplg_create_enum_ctl(gb, kctl, ctl);
+                       break;
+               default:
+                       ret = gbaudio_tplg_create_mixer_ctl(gb, kctl, ctl);
+                       break;
+               }
+               break;
+       default:
+               return -EINVAL;
+
+       }
+
+       dev_dbg(gb->dev, "%s:%d DAPM control created, ret:%d\n", ctl->name,
+               ctl->id, ret);
+       return ret;
+}
+
+static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *kcontrol, int event)
+{
+       int wid;
+       int ret;
+       struct snd_soc_codec *codec = w->codec;
+       struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
+       struct gbaudio_module_info *module;
+       struct gb_bundle *bundle;
+
+       dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+       /* Find relevant module */
+       module = find_gb_module(gbcodec, w->name);
+       if (!module)
+               return -EINVAL;
+
+       /* map name to widget id */
+       wid = gbaudio_map_widgetname(module, w->name);
+       if (wid < 0) {
+               dev_err(codec->dev, "Invalid widget name:%s\n", w->name);
+               return -EINVAL;
+       }
+
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid);
+               if (!ret)
+                       ret = gbaudio_module_update(gbcodec, w, module, 1);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               ret = gb_audio_gb_disable_widget(module->mgmt_connection, wid);
+               if (!ret)
+                       ret = gbaudio_module_update(gbcodec, w, module, 0);
+               break;
+       }
+       if (ret)
+               dev_err_ratelimited(codec->dev,
+                                   "%d: widget, event:%d failed:%d\n", wid,
+                                   event, ret);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
+       return ret;
+}
+
+static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module,
+                                     struct snd_soc_dapm_widget *dw,
+                                     struct gb_audio_widget *w, int *w_size)
+{
+       int i, ret, csize;
+       struct snd_kcontrol_new *widget_kctls;
+       struct gb_audio_control *curr;
+       struct gbaudio_control *control, *_control;
+       size_t size;
+       char temp_name[NAME_SIZE];
+
+       ret = gbaudio_validate_kcontrol_count(w);
+       if (ret) {
+               dev_err(module->dev, "Inavlid kcontrol count=%d for %s\n",
+                       w->ncontrols, w->name);
+               return ret;
+       }
+
+       /* allocate memory for kcontrol */
+       if (w->ncontrols) {
+               size = sizeof(struct snd_kcontrol_new) * w->ncontrols;
+               widget_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL);
+               if (!widget_kctls)
+                       return -ENOMEM;
+       }
+
+       *w_size = sizeof(struct gb_audio_widget);
+
+       /* create relevant kcontrols */
+       curr = w->ctl;
+       for (i = 0; i < w->ncontrols; i++) {
+               ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i],
+                                                  curr);
+               if (ret) {
+                       dev_err(module->dev,
+                               "%s:%d type widget_ctl not supported\n",
+                               curr->name, curr->iface);
+                       goto error;
+               }
+               control = devm_kzalloc(module->dev,
+                                      sizeof(struct gbaudio_control),
+                                      GFP_KERNEL);
+               if (!control) {
+                       ret = -ENOMEM;
+                       goto error;
+               }
+               control->id = curr->id;
+               control->name = curr->name;
+               control->wname = w->name;
+
+               if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) {
+                       struct gb_audio_enumerated *gbenum =
+                               &curr->info.value.enumerated;
+
+                       csize = offsetof(struct gb_audio_control, info);
+                       csize += offsetof(struct gb_audio_ctl_elem_info, value);
+                       csize += offsetof(struct gb_audio_enumerated, names);
+                       csize += gbenum->names_length;
+                       control->texts = (const char * const *)
+                               gb_generate_enum_strings(module, gbenum);
+                       control->items = gbenum->items;
+               } else
+                       c