greybus: Merge branch 'master' of github.com:gregkh/greybus

This commit is contained in:
Greg Kroah-Hartman 2015-05-26 08:39:33 -07:00
commit d67a39ab38
21 changed files with 683 additions and 143 deletions

View File

@ -25,6 +25,7 @@ gb-phy-y := gpbridge.o \
audio-gb-cmds.o
# Prefix all modules with gb-
gb-svc-y := svc.o
gb-vibrator-y := vibrator.o
gb-battery-y := battery.o
gb-loopback-y := loopback.o
@ -33,6 +34,7 @@ gb-es1-y := es1.o
gb-es2-y := es2.o
obj-m += greybus.o
obj-m += gb-svc.o
obj-m += gb-phy.o
obj-m += gb-vibrator.o
obj-m += gb-battery.o

View File

@ -1,3 +1,12 @@
/*
* Greybus audio Digital Audio Interface (DAI) driver
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/interrupt.h>
@ -11,8 +20,8 @@
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <sound/simple_card.h>
#include "greybus.h"
#include "gpbridge.h"
#include "audio.h"
/*

View File

@ -1,6 +1,15 @@
/*
* Greybus audio commands
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include "greybus.h"
#include "gpbridge.h"
#include "audio.h"
#define GB_I2S_MGMT_VERSION_MAJOR 0x00
@ -89,21 +98,12 @@ int gb_i2s_mgmt_set_samples_per_message(
&request, sizeof(request), NULL, 0);
}
/*
* XXX This is sort of a generic "setup" function which probably needs
* to be broken up, and tied into the constraints.
*
* I'm on the fence if we should just dictate that we only support
* 48k, 16bit, 2 channel, and avoid doign the whole probe for configurations
* and then picking one.
*/
int gb_i2s_mgmt_setup(struct gb_connection *connection)
int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev,
struct gb_connection *connection)
{
struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg;
struct gb_i2s_mgmt_set_configuration_request set_cfg;
struct gb_i2s_mgmt_configuration *cfg;
size_t size;
int i, ret;
int ret;
size = sizeof(*get_cfg) +
(CONFIG_COUNT_MAX * sizeof(get_cfg->config[0]));
@ -116,70 +116,79 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection)
size);
if (ret) {
pr_err("get_supported_config failed: %d\n", ret);
goto free_get_cfg;
goto err_free_get_cfg;
}
/* Pick 48KHz 16-bits/channel */
for (i = 0, cfg = get_cfg->config; i < CONFIG_COUNT_MAX; i++, cfg++) {
if ((le32_to_cpu(cfg->sample_frequency) == GB_SAMPLE_RATE) &&
(cfg->num_channels == 2) &&
(cfg->bytes_per_channel == 2) &&
(cfg->byte_order & GB_I2S_MGMT_BYTE_ORDER_LE) &&
(le32_to_cpu(cfg->spatial_locations) ==
(GB_I2S_MGMT_SPATIAL_LOCATION_FL |
GB_I2S_MGMT_SPATIAL_LOCATION_FR)) &&
(le32_to_cpu(cfg->ll_protocol) & GB_I2S_MGMT_PROTOCOL_I2S) &&
snd_dev->i2s_configs = get_cfg;
return 0;
err_free_get_cfg:
kfree(get_cfg);
return ret;
}
void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev)
{
kfree(snd_dev->i2s_configs);
snd_dev->i2s_configs = NULL;
}
int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans,
int bytes_per_chan, int is_le)
{
struct gb_i2s_mgmt_set_configuration_request set_cfg;
struct gb_i2s_mgmt_configuration *cfg;
int i, ret;
u8 byte_order = GB_I2S_MGMT_BYTE_ORDER_NA;
if (bytes_per_chan > 1) {
if (is_le)
byte_order = GB_I2S_MGMT_BYTE_ORDER_LE;
else
byte_order = GB_I2S_MGMT_BYTE_ORDER_BE;
}
for (i = 0, cfg = snd_dev->i2s_configs->config;
i < CONFIG_COUNT_MAX;
i++, cfg++) {
if ((cfg->sample_frequency == cpu_to_le32(rate)) &&
(cfg->num_channels == chans) &&
(cfg->bytes_per_channel == bytes_per_chan) &&
(cfg->byte_order & byte_order) &&
(cfg->ll_protocol &
cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S)) &&
(cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) &&
(cfg->ll_bclk_role & GB_I2S_MGMT_ROLE_MASTER) &&
(cfg->ll_wclk_role & GB_I2S_MGMT_ROLE_MASTER) &&
(cfg->ll_wclk_polarity & GB_I2S_MGMT_POLARITY_NORMAL) &&
(cfg->ll_wclk_change_edge & GB_I2S_MGMT_EDGE_FALLING) &&
(cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_FALLING) &&
(cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_RISING) &&
(cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_RISING) &&
(cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_FALLING) &&
(cfg->ll_data_offset == 1))
break;
}
if (i >= CONFIG_COUNT_MAX) {
pr_err("No valid configuration\n");
ret = -EINVAL;
goto free_get_cfg;
return -EINVAL;
}
memcpy(&set_cfg, cfg, sizeof(set_cfg));
set_cfg.config.byte_order = GB_I2S_MGMT_BYTE_ORDER_LE;
set_cfg.config.byte_order = byte_order;
set_cfg.config.ll_protocol = cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S);
set_cfg.config.ll_mclk_role = GB_I2S_MGMT_ROLE_MASTER;
set_cfg.config.ll_bclk_role = GB_I2S_MGMT_ROLE_MASTER;
set_cfg.config.ll_wclk_role = GB_I2S_MGMT_ROLE_MASTER;
set_cfg.config.ll_wclk_polarity = GB_I2S_MGMT_POLARITY_NORMAL;
set_cfg.config.ll_wclk_change_edge = GB_I2S_MGMT_EDGE_RISING;
set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_FALLING;
set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_RISING;
set_cfg.config.ll_wclk_change_edge = GB_I2S_MGMT_EDGE_FALLING;
set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_RISING;
set_cfg.config.ll_wclk_rx_edge = GB_I2S_MGMT_EDGE_FALLING;
ret = gb_i2s_mgmt_set_configuration(connection, &set_cfg);
if (ret) {
ret = gb_i2s_mgmt_set_configuration(snd_dev->mgmt_connection, &set_cfg);
if (ret)
pr_err("set_configuration failed: %d\n", ret);
goto free_get_cfg;
}
ret = gb_i2s_mgmt_set_samples_per_message(connection,
CONFIG_SAMPLES_PER_MSG);
if (ret) {
pr_err("set_samples_per_msg failed: %d\n", ret);
goto free_get_cfg;
}
/* XXX Add start delay here (probably 1ms) */
ret = gb_i2s_mgmt_activate_cport(connection,
CONFIG_I2S_REMOTE_DATA_CPORT);
if (ret) {
pr_err("activate_cport failed: %d\n", ret);
goto free_get_cfg;
}
free_get_cfg:
kfree(get_cfg);
return ret;
}

View File

@ -1,3 +1,12 @@
/*
* Greybus audio Pulse Code Modulation (PCM) driver
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/interrupt.h>
@ -11,8 +20,8 @@
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <sound/simple_card.h>
#include "greybus.h"
#include "gpbridge.h"
#include "audio.h"
/*
@ -32,15 +41,33 @@ static void gb_pcm_work(struct work_struct *work)
struct snd_pcm_substream *substream = snd_dev->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int stride, frames, oldptr;
int period_elapsed;
int period_elapsed, ret;
char *address;
long len;
if (!snd_dev)
return;
if (!atomic_read(&snd_dev->running))
if (!atomic_read(&snd_dev->running)) {
if (snd_dev->cport_active) {
ret = gb_i2s_mgmt_deactivate_cport(
snd_dev->mgmt_connection,
snd_dev->i2s_tx_connection->bundle_cport_id);
if (ret) /* XXX Do what else with failure? */
pr_err("deactivate_cport failed: %d\n", ret);
snd_dev->cport_active = false;
}
return;
} else if (!snd_dev->cport_active) {
ret = gb_i2s_mgmt_activate_cport(snd_dev->mgmt_connection,
snd_dev->i2s_tx_connection->bundle_cport_id);
if (ret)
pr_err("activate_cport failed: %d\n", ret);
snd_dev->cport_active = true;
}
address = runtime->dma_area + snd_dev->hwptr_done;
@ -88,6 +115,7 @@ static enum hrtimer_restart gb_pcm_timer_function(struct hrtimer *hrtimer)
void gb_pcm_hrtimer_start(struct gb_snd *snd_dev)
{
atomic_set(&snd_dev->running, 1);
queue_work(snd_dev->workqueue, &snd_dev->work); /* Activates CPort */
hrtimer_start(&snd_dev->timer, ns_to_ktime(CONFIG_PERIOD_NS),
HRTIMER_MODE_REL);
}
@ -96,6 +124,7 @@ void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev)
{
atomic_set(&snd_dev->running, 0);
hrtimer_cancel(&snd_dev->timer);
queue_work(snd_dev->workqueue, &snd_dev->work); /* Deactivates CPort */
}
static int gb_pcm_hrtimer_init(struct gb_snd *snd_dev)
@ -200,6 +229,21 @@ static int gb_pcm_close(struct snd_pcm_substream *substream)
static int gb_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct gb_snd *snd_dev;
int rate, chans, bytes_per_chan, is_le, ret;
snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
rate = params_rate(hw_params);
chans = params_channels(hw_params);
bytes_per_chan = snd_pcm_format_width(params_format(hw_params)) / 8;
is_le = snd_pcm_format_little_endian(params_format(hw_params));
ret = gb_i2s_mgmt_set_cfg(snd_dev, rate, chans, bytes_per_chan, is_le);
if (ret)
return ret;
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}

View File

@ -1,24 +1,35 @@
/*
* Greybus audio driver
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/i2c.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include <sound/simple_card.h>
#include "greybus.h"
#include "gpbridge.h"
#include "audio.h"
#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data"
#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt"
#define RT5647_I2C_ADAPTER_NR 6
#define RT5647_I2C_ADDR 0x1b
/*
* gb_snd management functions
*/
@ -107,13 +118,17 @@ static struct asoc_simple_card_info *setup_card_info(int device_count)
obj->card_info.platform = obj->platform_name;
obj->card_info.cpu_dai.name = obj->dai_name;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
obj->card_info.cpu_dai.fmt = GB_FMTS;
obj->card_info.cpu_dai.fmt = SND_SOC_DAIFMT_CBM_CFM;
#endif
#if USE_RT5645
obj->card_info.daifmt = GB_FMTS;
sprintf(obj->codec_name, "rt5645.%s", "6-001b"); /* XXX do i2c bus addr dynamically */
obj->card_info.daifmt = SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_I2S;
sprintf(obj->codec_name, "rt5645.%d-%04x", RT5647_I2C_ADAPTER_NR,
RT5647_I2C_ADDR);
obj->card_info.codec_dai.name = "rt5645-aif1";
obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBM_CFM;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
obj->card_info.codec_dai.fmt = SND_SOC_DAIFMT_CBS_CFS;
#endif
obj->card_info.codec_dai.sysclk = 12288000;
#else
sprintf(obj->codec_name, "spdif-dit");
@ -150,6 +165,9 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection)
struct gb_snd *snd_dev;
struct platform_device *codec, *dai;
struct asoc_simple_card_info *simple_card;
#if USE_RT5645
struct i2c_board_info rt5647_info;
#endif
unsigned long flags;
int ret;
@ -212,6 +230,18 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection)
goto out_get_ver;
}
#if USE_RT5645
rt5647_info.addr = RT5647_I2C_ADDR;
strlcpy(rt5647_info.type, "rt5647", I2C_NAME_SIZE);
snd_dev->rt5647 = i2c_new_device(i2c_get_adapter(RT5647_I2C_ADAPTER_NR),
&rt5647_info);
if (!snd_dev->rt5647) {
pr_err("can't create rt5647 i2c device\n");
goto out_get_ver;
}
#endif
return 0;
out_get_ver:
@ -231,6 +261,10 @@ static void gb_i2s_transmitter_connection_exit(struct gb_connection *connection)
snd_dev = (struct gb_snd *)connection->private;
#if USE_RT5645
i2c_unregister_device(snd_dev->rt5647);
#endif
platform_device_unregister(&snd_dev->card);
platform_device_unregister(&snd_dev->cpu_dai);
platform_device_unregister(snd_dev->codec);
@ -261,19 +295,30 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection)
goto err_free_snd_dev;
}
gb_i2s_mgmt_setup(connection);
ret = gb_i2s_mgmt_get_cfgs(snd_dev, connection);
if (ret) {
pr_err("can't get i2s configurations: %d\n", ret);
goto err_free_snd_dev;
}
ret = gb_i2s_mgmt_set_samples_per_message(snd_dev->mgmt_connection,
CONFIG_SAMPLES_PER_MSG);
if (ret) {
pr_err("set_samples_per_msg failed: %d\n", ret);
goto err_free_i2s_configs;
}
snd_dev->send_data_req_buf = kzalloc(SEND_DATA_BUF_LEN, GFP_KERNEL);
if (!snd_dev->send_data_req_buf) {
ret = -ENOMEM;
goto err_deactivate_cport;
goto err_free_i2s_configs;
}
return 0;
err_deactivate_cport:
gb_i2s_mgmt_deactivate_cport(connection, CONFIG_I2S_REMOTE_DATA_CPORT);
err_free_i2s_configs:
gb_i2s_mgmt_free_cfgs(snd_dev);
err_free_snd_dev:
gb_free_snd(snd_dev);
return ret;
@ -282,12 +327,8 @@ err_free_snd_dev:
static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection)
{
struct gb_snd *snd_dev = (struct gb_snd *)connection->private;
int ret;
ret = gb_i2s_mgmt_deactivate_cport(connection,
CONFIG_I2S_REMOTE_DATA_CPORT);
if (ret)
pr_err("deactivate_cport failed: %d\n", ret);
gb_i2s_mgmt_free_cfgs(snd_dev);
kfree(snd_dev->send_data_req_buf);
snd_dev->send_data_req_buf = NULL;

View File

@ -1,14 +1,23 @@
/*
* Greybus audio
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#ifndef __GB_AUDIO_H
#define __GB_AUDIO_H
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
#include "greybus.h"
#include "gpbridge.h"
#include "greybus.h"
#define GB_SAMPLE_RATE 48000
#define GB_RATES SNDRV_PCM_RATE_48000
@ -20,9 +29,7 @@
#define CONFIG_SAMPLES_PER_MSG 48L
#define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */
#define CONFIG_COUNT_MAX 32
#define CONFIG_I2S_REMOTE_DATA_CPORT 7 /* XXX shouldn't be hardcoded...*/
#define RT5647_SLAVE_ADDR 0x1b /* from toshiba/quanta code */
#define CONFIG_COUNT_MAX 20
/* Switch between dummy spdif and jetson rt5645 codec */
#define USE_RT5645 0
@ -42,9 +49,12 @@ struct gb_snd {
struct platform_device cpu_dai;
struct platform_device *codec;
struct asoc_simple_card_info *simple_card_info;
struct i2c_client *rt5647;
struct gb_connection *mgmt_connection;
struct gb_connection *i2s_tx_connection;
struct gb_connection *i2s_rx_connection;
struct gb_i2s_mgmt_get_supported_configurations_response
*i2s_configs;
char *send_data_req_buf;
long send_data_sample_count;
int gb_bundle_id;
@ -52,6 +62,7 @@ struct gb_snd {
struct snd_pcm_substream *substream;
struct hrtimer timer;
atomic_t running;
bool cport_active;
struct workqueue_struct *workqueue;
struct work_struct work;
int hwptr_done;
@ -78,7 +89,11 @@ int gb_i2s_mgmt_set_configuration(struct gb_connection *connection,
struct gb_i2s_mgmt_set_configuration_request *set_cfg);
int gb_i2s_mgmt_set_samples_per_message(struct gb_connection *connection,
uint16_t samples_per_message);
int gb_i2s_mgmt_setup(struct gb_connection *connection);
int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev,
struct gb_connection *connection);
void gb_i2s_mgmt_free_cfgs(struct gb_snd *snd_dev);
int gb_i2s_mgmt_set_cfg(struct gb_snd *snd_dev, int rate, int chans,
int bytes_per_chan, int is_le);
int gb_i2s_send_data(struct gb_connection *connection, void *req_buf,
void *source_addr, size_t len, int sample_num);

View File

@ -1,8 +1,8 @@
/*
* Greybus "Core"
*
* Copyright 2014 Google Inc.
* Copyright 2014 Linaro Ltd.
* Copyright 2014-2015 Google Inc.
* Copyright 2014-2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
@ -185,7 +185,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
if ((!driver->message_send) || (!driver->message_cancel) ||
(!driver->submit_svc)) {
pr_err("Must implement all greybus_host_driver callbacks!\n");
return NULL;
return ERR_PTR(-EINVAL);
}
if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
@ -205,7 +205,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
if (!hd)
return NULL;
return ERR_PTR(-ENOMEM);
kref_init(&hd->kref);
hd->parent = parent;
@ -215,16 +215,24 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
ida_init(&hd->cport_id_map);
hd->buffer_size_max = buffer_size_max;
hd->endo = gb_endo_create(hd);
if (!hd->endo) {
greybus_remove_hd(hd);
return NULL;
}
return hd;
}
EXPORT_SYMBOL_GPL(greybus_create_hd);
int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id,
u8 ap_intf_id)
{
struct gb_endo *endo;
endo = gb_endo_create(hd, endo_id, ap_intf_id);
if (IS_ERR(endo))
return PTR_ERR(endo);
hd->endo = endo;
return 0;
}
EXPORT_SYMBOL_GPL(greybus_endo_setup);
void greybus_remove_hd(struct greybus_host_device *hd)
{
/*

View File

@ -1,8 +1,8 @@
/*
* Greybus endo code
*
* Copyright 2015 Google Inc.
* Copyright 2014 Linaro Ltd.
* Copyright 2014-2015 Google Inc.
* Copyright 2014-2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
@ -29,36 +29,82 @@
#define endo_back_left_ribs(id, ribs) (((id) >> (ribs)) & ENDO_BACK_SIDE_RIBS_MASK(ribs))
#define endo_back_right_ribs(id, ribs) ((id) & ENDO_BACK_SIDE_RIBS_MASK(ribs))
/*
* An Endo has interface block positions on the front and back.
* Each has numeric ID, starting with 1 (interface 0 represents
* the SVC within the Endo itself). The maximum interface ID is the
* also the number of non-SVC interfaces possible on the endo.
*
* Total number of interfaces:
* - Front: 4
* - Back left: max_ribs + 1
* - Back right: max_ribs + 1
*/
#define max_endo_interface_id(endo_layout) \
(4 + ((endo_layout)->max_ribs + 1) * 2)
/* endo sysfs attributes */
static ssize_t serial_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
static ssize_t svc_serial_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gb_endo *endo = to_gb_endo(dev);
return sprintf(buf, "%s", &endo->svc.serial_number[0]);
return sprintf(buf, "%s", &endo->svc_info.serial_number[0]);
}
static DEVICE_ATTR_RO(serial_number);
static DEVICE_ATTR_RO(svc_serial_number);
static ssize_t version_show(struct device *dev, struct device_attribute *attr,
char *buf)
static ssize_t svc_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gb_endo *endo = to_gb_endo(dev);
return sprintf(buf, "%s", &endo->svc.version[0]);
return sprintf(buf, "%s", &endo->svc_info.version[0]);
}
static DEVICE_ATTR_RO(version);
static DEVICE_ATTR_RO(svc_version);
static struct attribute *endo_attrs[] = {
&dev_attr_serial_number.attr,
&dev_attr_version.attr,
static struct attribute *svc_attrs[] = {
&dev_attr_svc_serial_number.attr,
&dev_attr_svc_version.attr,
NULL,
};
static const struct attribute_group endo_group = {
.attrs = endo_attrs,
static const struct attribute_group svc_group = {
.attrs = svc_attrs,
.name = "SVC",
};
static ssize_t endo_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gb_endo *endo = to_gb_endo(dev);
return sprintf(buf, "0x%04x", endo->id);
}
static DEVICE_ATTR_RO(endo_id);
static ssize_t ap_intf_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct gb_endo *endo = to_gb_endo(dev);
return sprintf(buf, "0x%02x", endo->ap_intf_id);
}
static DEVICE_ATTR_RO(ap_intf_id);
static struct attribute *endo_attrs[] = {
&dev_attr_endo_id.attr,
&dev_attr_ap_intf_id.attr,
NULL,
};
static const struct attribute_group endo_group = {
.attrs = endo_attrs,
.name = "Endo",
};
static const struct attribute_group *endo_groups[] = {
&endo_group,
&svc_group,
NULL,
};
@ -361,19 +407,12 @@ static int create_modules(struct gb_endo *endo)
int prev_module_id = 0;
int interface_id;
int module_id;
int interfaces;
int max_id;
/*
* Total number of interfaces:
* - Front: 4
* - Back:
* - Left: max_ribs + 1
* - Right: max_ribs + 1
*/
interfaces = 4 + (endo->layout.max_ribs + 1) * 2;
max_id = max_endo_interface_id(&endo->layout);
/* Find module corresponding to each interface */
for (interface_id = 1; interface_id <= interfaces; interface_id++) {
for (interface_id = 1; interface_id <= max_id; interface_id++) {
module_id = endo_get_module_id(endo, interface_id);
if (WARN_ON(!module_id))
@ -409,8 +448,8 @@ static int gb_endo_register(struct greybus_host_device *hd,
// FIXME
// Get the version and serial number from the SVC, right now we are
// using "fake" numbers.
strcpy(&endo->svc.serial_number[0], "042");
strcpy(&endo->svc.version[0], "0.0");
strcpy(&endo->svc_info.serial_number[0], "042");
strcpy(&endo->svc_info.version[0], "0.0");
dev_set_name(&endo->dev, "endo-0x%04x", endo->id);
retval = device_add(&endo->dev);
@ -423,24 +462,31 @@ static int gb_endo_register(struct greybus_host_device *hd,
return retval;
}
struct gb_endo *gb_endo_create(struct greybus_host_device *hd)
struct gb_endo *gb_endo_create(struct greybus_host_device *hd, u16 endo_id,
u8 ap_intf_id)
{
struct gb_endo *endo;
int retval;
u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC
endo = kzalloc(sizeof(*endo), GFP_KERNEL);
if (!endo)
return NULL;
return ERR_PTR(-ENOMEM);
/* First check if the value supplied is a valid endo id */
if (gb_endo_validate_id(hd, &endo->layout, endo_id))
if (gb_endo_validate_id(hd, &endo->layout, endo_id)) {
retval = -EINVAL;
goto free_endo;
}
if (ap_intf_id > max_endo_interface_id(&endo->layout)) {
retval = -EINVAL;
goto free_endo;
}
endo->id = endo_id;
endo->ap_intf_id = ap_intf_id;
/* Register Endo device */
if (gb_endo_register(hd, endo))
retval = gb_endo_register(hd, endo);
if (retval)
goto free_endo;
/* Create modules/interfaces */
@ -454,7 +500,8 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd)
free_endo:
kfree(endo);
return NULL;
return ERR_PTR(retval);
}
void gb_endo_remove(struct gb_endo *endo)

View File

@ -2,6 +2,7 @@
* Greybus endo code
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
@ -10,7 +11,7 @@
#define __ENDO_H
/* Greybus "public" definitions" */
struct gb_svc {
struct gb_svc_info {
u8 serial_number[10];
u8 version[10];
};
@ -36,10 +37,11 @@ struct endo_layout {
};
struct gb_endo {
struct endo_layout layout;
struct device dev;
struct gb_svc svc;
struct endo_layout layout;
struct gb_svc_info svc_info;
u16 id;
u8 ap_intf_id;
};
#define to_gb_endo(d) container_of(d, struct gb_endo, dev)
@ -47,7 +49,8 @@ struct gb_endo {
/* Greybus "private" definitions */
struct greybus_host_device;
struct gb_endo *gb_endo_create(struct greybus_host_device *hd);
struct gb_endo *gb_endo_create(struct greybus_host_device *hd,
u16 endo_id, u8 ap_intf_id);
void gb_endo_remove(struct gb_endo *endo);
u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id);

View File

@ -1,8 +1,8 @@
/*
* Greybus "AP" USB driver
* Greybus "AP" USB driver for "ES1" controller chips
*
* Copyright 2014 Google Inc.
* Copyright 2014 Linaro Ltd.
* Copyright 2014-2015 Google Inc.
* Copyright 2014-2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
@ -551,14 +551,16 @@ static int ap_probe(struct usb_interface *interface,
bool bulk_out_found = false;
int retval = -ENOMEM;
int i;
u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC
u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC
u8 svc_interval = 0;
udev = usb_get_dev(interface_to_usbdev(interface));
hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX);
if (!hd) {
if (IS_ERR(hd)) {
usb_put_dev(udev);
return -ENOMEM;
return PTR_ERR(hd);
}
es1 = hd_to_es1(hd);
@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface,
gb_debugfs_get(), es1,
&apb1_log_enable_fops);
/*
* XXX Soon this will be initiated later, with a combination
* XXX of a Control protocol probe operation and a
* XXX subsequent Control protocol connected operation for
* XXX the SVC connection. At that point we know we're
* XXX properly connected to an Endo.
*/
retval = greybus_endo_setup(hd, endo_id, ap_intf_id);
if (retval)
goto error;
return 0;
error:
ap_disconnect(interface);

View File

@ -551,14 +551,16 @@ static int ap_probe(struct usb_interface *interface,
bool bulk_out_found = false;
int retval = -ENOMEM;
int i;
u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC
u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC
u8 svc_interval = 0;
udev = usb_get_dev(interface_to_usbdev(interface));
hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX);
if (!hd) {
if (IS_ERR(hd)) {
usb_put_dev(udev);
return -ENOMEM;
return PTR_ERR(hd);
}
es1 = hd_to_es1(hd);
@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface,
gb_debugfs_get(), es1,
&apb1_log_enable_fops);
/*
* XXX Soon this will be initiated later, with a combination
* XXX of a Control protocol probe operation and a
* XXX subsequent Control protocol connected operation for
* XXX the SVC connection. At that point we know we're
* XXX properly connected to an Endo.
*/
retval = greybus_endo_setup(hd, endo_id, ap_intf_id);
if (retval)
goto error;
return 0;
error:
ap_disconnect(interface);

View File

@ -14,7 +14,6 @@
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include "greybus.h"
#include "gpbridge.h"
struct gb_gpio_line {
/* The following has to be an array of line_max entries */

View File

@ -1,8 +1,8 @@
/*
* Greybus driver and device API
*
* Copyright 2014 Google Inc.
* Copyright 2014 Linaro Ltd.
* Copyright 2014-2015 Google Inc.
* Copyright 2014-2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
@ -23,8 +23,10 @@
#include "kernel_ver.h"
#include "greybus_id.h"
#include "greybus_manifest.h"
#include "greybus_protocols.h"
#include "manifest.h"
#include "endo.h"
#include "svc.h"
#include "module.h"
#include "interface.h"
#include "bundle.h"
@ -107,6 +109,8 @@ struct greybus_host_device {
struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd,
struct device *parent,
size_t buffer_size_max);
int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id,
u8 ap_intf_id);
void greybus_remove_hd(struct greybus_host_device *hd);
struct greybus_driver {

View File

@ -4,8 +4,8 @@
* See "Greybus Application Protocol" document (version 0.1) for
* details on these values and structures.
*
* Copyright 2014 Google Inc.
* Copyright 2014 Linaro Ltd.
* Copyright 2014-2015 Google Inc.
* Copyright 2014-2015 Linaro Ltd.
*
* Released under the GPLv2 and BSD licenses.
*/
@ -42,6 +42,7 @@ enum greybus_protocol {
GREYBUS_PROTOCOL_LOOPBACK = 0x11,
GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12,
GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13,
GREYBUS_PROTOCOL_SVC = 0x14,
/* ... */
GREYBUS_PROTOCOL_RAW = 0xfe,
GREYBUS_PROTOCOL_VENDOR = 0xff,

View File

@ -489,4 +489,61 @@ struct gb_spi_transfer_response {
__u8 data[0]; /* inbound data */
};
/* Version of the Greybus SVC protocol we support */
#define GB_SVC_VERSION_MAJOR 0x00
#define GB_SVC_VERSION_MINOR 0x01
/* Greybus SVC request types */
#define GB_SVC_TYPE_INVALID 0x00
#define GB_SVC_TYPE_PROTOCOL_VERSION 0x01
#define GB_SVC_TYPE_INTF_DEVICE_ID 0x02
#define GB_SVC_TYPE_INTF_HOTPLUG 0x03
#define GB_SVC_TYPE_INTF_HOT_UNPLUG 0x04
#define GB_SVC_TYPE_INTF_RESET 0x05
#define GB_SVC_TYPE_CONN_CREATE 0x06
#define GB_SVC_TYPE_CONN_DESTROY 0x07
struct gb_svc_intf_device_id_request {
__u8 intf_id;
__u8 device_id;
};
/* device id response has no payload */
struct gb_svc_intf_hotplug_request {
__u8 intf_id;
struct {
__le32 unipro_mfg_id;
__le32 unipro_prod_id;
__le32 ara_vend_id;
__le32 ara_prod_id;
} data;
};
/* hotplug response has no payload */
struct gb_svc_intf_hot_unplug_request {
__u8 intf_id;
};
/* hot unplug response has no payload */
struct gb_svc_intf_reset_request {
__u8 intf_id;
};
/* interface reset response has no payload */
struct gb_svc_conn_create_request {
__u8 intf1_id;
__u16 cport1_id;
__u8 intf2_id;
__u16 cport2_id;
};
/* connection create response has no payload */
struct gb_svc_conn_destroy_request {
__u8 intf1_id;
__u16 cport1_id;
__u8 intf2_id;
__u16 cport2_id;
};
/* connection destroy response has no payload */
#endif /* __GB_GPBRIDGE_H__ */

View File

@ -13,8 +13,6 @@
#include <linux/i2c.h>
#include "greybus.h"
#include "gpbridge.h"
struct gb_i2c_device {
struct gb_connection *connection;

View File

@ -108,7 +108,7 @@ int __init gb_##__protocol##_init(void) \
{ \
return gb_protocol_register(&__protocol); \
} \
void __exit gb_##__protocol##_exit(void) \
void gb_##__protocol##_exit(void) \
{ \
gb_protocol_deregister(&__protocol); \
} \

View File

@ -11,8 +11,8 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pwm.h>
#include "greybus.h"
#include "gpbridge.h"
struct gb_pwm_chip {
struct gb_connection *connection;

View File

@ -14,7 +14,6 @@
#include <linux/spi/spi.h>
#include "greybus.h"
#include "gpbridge.h"
struct gb_spi {
struct gb_connection *connection;

View File

@ -0,0 +1,256 @@
/*
* SVC Greybus driver.
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "greybus.h"
#include "greybus_protocols.h"
struct gb_svc {
struct gb_connection *connection;
u8 version_major;
u8 version_minor;
};
/* Define get_version() routine */
define_get_version(gb_svc, SVC);
static int intf_device_id_operation(struct gb_svc *svc,
u8 intf_id, u8 device_id)
{
struct gb_svc_intf_device_id_request request;
request.intf_id = intf_id;
request.device_id = device_id;
return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
&request, sizeof(request), NULL, 0);
}
static int intf_reset_operation(struct gb_svc *svc, u8 intf_id)
{
struct gb_svc_intf_reset_request request;
request.intf_id = intf_id;
return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
&request, sizeof(request), NULL, 0);
}
static int connection_create_operation(struct gb_svc *svc,
u8 intf1_id, u16 cport1_id,
u8 intf2_id, u16 cport2_id)
{
struct gb_svc_conn_create_request request;
request.intf1_id = intf1_id;
request.cport1_id = cport1_id;
request.intf2_id = intf2_id;
request.cport2_id = cport2_id;
return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
&request, sizeof(request), NULL, 0);
}
static int connection_destroy_operation(struct gb_svc *svc,
u8 intf1_id, u16 cport1_id,
u8 intf2_id, u16 cport2_id)
{
struct gb_svc_conn_destroy_request request;
request.intf1_id = intf1_id;
request.cport1_id = cport1_id;
request.intf2_id = intf2_id;
request.cport2_id = cport2_id;
return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY,
&request, sizeof(request), NULL, 0);
}
int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
{
return intf_device_id_operation(svc, intf_id, device_id);
}
EXPORT_SYMBOL_GPL(gb_svc_intf_device_id);
int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
{
return intf_reset_operation(svc, intf_id);
}
EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
int gb_svc_connection_create(struct gb_svc *svc,
u8 intf1_id, u16 cport1_id,
u8 intf2_id, u16 cport2_id)
{
return connection_create_operation(svc, intf1_id, cport1_id,
intf2_id, cport2_id);
}
EXPORT_SYMBOL_GPL(gb_svc_connection_create);
int gb_svc_connection_destroy(struct gb_svc *svc,
u8 intf1_id, u16 cport1_id,
u8 intf2_id, u16 cport2_id)
{
return connection_destroy_operation(svc, intf1_id, cport1_id,
intf2_id, cport2_id);
}
EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
{
struct gb_message *request = op->request;
struct gb_svc_intf_hotplug_request *hotplug;
u8 intf_id;
u32 unipro_mfg_id;
u32 unipro_prod_id;
u32 ara_vend_id;
u32 ara_prod_id;
if (request->payload_size < sizeof(*hotplug)) {
dev_err(&op->connection->dev,
"short hotplug request received\n");
return -EINVAL;
}
hotplug = request->payload;
/*
* Grab the information we need.
*
* XXX I'd really like to acknowledge receipt, and then
* XXX continue processing the request. There's no need
* XXX for the SVC to wait. In fact, it might be best to
* XXX have the SVC get acknowledgement before we proceed.
* */
intf_id = hotplug->intf_id;
unipro_mfg_id = hotplug->data.unipro_mfg_id;
unipro_prod_id = hotplug->data.unipro_prod_id;
ara_vend_id = hotplug->data.ara_vend_id;
ara_prod_id = hotplug->data.ara_prod_id;
/* FIXME Set up the interface here; may required firmware download */
return 0;
}
static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
{
struct gb_message *request = op->request;
struct gb_svc_intf_hot_unplug_request *hot_unplug;
u8 intf_id;
if (request->payload_size < sizeof(*hot_unplug)) {
dev_err(&op->connection->dev,
"short hot unplug request received\n");
return -EINVAL;
}
hot_unplug = request->payload;
intf_id = hot_unplug->intf_id;
/* FIXME Tear down the interface here */
return 0;
}
static int gb_svc_intf_reset_recv(struct gb_operation *op)
{
struct gb_message *request = op->request;
struct gb_svc_intf_reset_request *reset;
u8 intf_id;
if (request->payload_size < sizeof(*reset)) {
dev_err(&op->connection->dev,
"short reset request received\n");
return -EINVAL;
}
reset = request->payload;
intf_id = reset->intf_id;
/* FIXME Reset the interface here */
return 0;
}
static int gb_svc_request_recv(u8 type, struct gb_operation *op)
{
switch (type) {
case GB_SVC_TYPE_INTF_HOTPLUG:
return gb_svc_intf_hotplug_recv(op);
case GB_SVC_TYPE_INTF_HOT_UNPLUG:
return gb_svc_intf_hot_unplug_recv(op);
case GB_SVC_TYPE_INTF_RESET:
return gb_svc_intf_reset_recv(op);
default:
dev_err(&op->connection->dev,
"unsupported request: %hhu\n", type);
return -EINVAL;
}
}
/*
* Do initial setup of the SVC.
*/
static int gb_svc_device_setup(struct gb_svc *gb_svc)
{
/* First thing we need to do is check the version */
return get_version(gb_svc);
}
static int gb_svc_connection_init(struct gb_connection *connection)
{
struct gb_svc *svc;
int ret;
svc = kzalloc(sizeof(*svc), GFP_KERNEL);
if (!svc)
return -ENOMEM;
svc->connection = connection;
connection->private = svc;
ret = gb_svc_device_setup(svc);
if (ret)
kfree(svc);
return ret;
}
static void gb_svc_connection_exit(struct gb_connection *connection)
{
struct gb_svc *svc = connection->private;
if (!svc)
return;
kfree(svc);
}
static struct gb_protocol svc_protocol = {
.name = "svc",
.id = GREYBUS_PROTOCOL_SVC,
.major = 0,
.minor = 1,
.connection_init = gb_svc_connection_init,
.connection_exit = gb_svc_connection_exit,
.request_recv = gb_svc_request_recv,
};
int gb_svc_protocol_init(void)
{
return gb_protocol_register(&svc_protocol);
}
void gb_svc_protocol_exit(void)
{
gb_protocol_deregister(&svc_protocol);
}

View File

@ -0,0 +1,22 @@
/*
* Greybus SVC code
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#ifndef __SVC_H
#define __SVC_H
struct gb_svc;
int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id);
int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id);
int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id,
u8 intf2_id, u16 cport2_id);
int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id,
u8 intf2_id, u16 cport2_id);
#endif /* __SVC_H */