Commit d67a39ab authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

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

parents d52b35f6 30c6d9d7
...@@ -25,6 +25,7 @@ gb-phy-y := gpbridge.o \ ...@@ -25,6 +25,7 @@ gb-phy-y := gpbridge.o \
audio-gb-cmds.o audio-gb-cmds.o
# Prefix all modules with gb- # Prefix all modules with gb-
gb-svc-y := svc.o
gb-vibrator-y := vibrator.o gb-vibrator-y := vibrator.o
gb-battery-y := battery.o gb-battery-y := battery.o
gb-loopback-y := loopback.o gb-loopback-y := loopback.o
...@@ -33,6 +34,7 @@ gb-es1-y := es1.o ...@@ -33,6 +34,7 @@ gb-es1-y := es1.o
gb-es2-y := es2.o gb-es2-y := es2.o
obj-m += greybus.o obj-m += greybus.o
obj-m += gb-svc.o
obj-m += gb-phy.o obj-m += gb-phy.o
obj-m += gb-vibrator.o obj-m += gb-vibrator.o
obj-m += gb-battery.o obj-m += gb-battery.o
......
/*
* 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/kernel.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -11,8 +20,8 @@ ...@@ -11,8 +20,8 @@
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
#include <sound/simple_card.h> #include <sound/simple_card.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
#include "audio.h" #include "audio.h"
/* /*
......
/*
* Greybus audio commands
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
#include "audio.h" #include "audio.h"
#define GB_I2S_MGMT_VERSION_MAJOR 0x00 #define GB_I2S_MGMT_VERSION_MAJOR 0x00
...@@ -89,21 +98,12 @@ int gb_i2s_mgmt_set_samples_per_message( ...@@ -89,21 +98,12 @@ int gb_i2s_mgmt_set_samples_per_message(
&request, sizeof(request), NULL, 0); &request, sizeof(request), NULL, 0);
} }
/* int gb_i2s_mgmt_get_cfgs(struct gb_snd *snd_dev,
* XXX This is sort of a generic "setup" function which probably needs struct gb_connection *connection)
* 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)
{ {
struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg; 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; size_t size;
int i, ret; int ret;
size = sizeof(*get_cfg) + size = sizeof(*get_cfg) +
(CONFIG_COUNT_MAX * sizeof(get_cfg->config[0])); (CONFIG_COUNT_MAX * sizeof(get_cfg->config[0]));
...@@ -116,70 +116,79 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection) ...@@ -116,70 +116,79 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection)
size); size);
if (ret) { if (ret) {
pr_err("get_supported_config failed: %d\n", ret); pr_err("get_supported_config failed: %d\n", ret);
goto free_get_cfg; goto err_free_get_cfg;
}
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;
} }
/* Pick 48KHz 16-bits/channel */ for (i = 0, cfg = snd_dev->i2s_configs->config;
for (i = 0, cfg = get_cfg->config; i < CONFIG_COUNT_MAX; i++, cfg++) { i < CONFIG_COUNT_MAX;
if ((le32_to_cpu(cfg->sample_frequency) == GB_SAMPLE_RATE) && i++, cfg++) {
(cfg->num_channels == 2) && if ((cfg->sample_frequency == cpu_to_le32(rate)) &&
(cfg->bytes_per_channel == 2) && (cfg->num_channels == chans) &&
(cfg->byte_order & GB_I2S_MGMT_BYTE_ORDER_LE) && (cfg->bytes_per_channel == bytes_per_chan) &&
(le32_to_cpu(cfg->spatial_locations) == (cfg->byte_order & byte_order) &&
(GB_I2S_MGMT_SPATIAL_LOCATION_FL | (cfg->ll_protocol &
GB_I2S_MGMT_SPATIAL_LOCATION_FR)) && cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S)) &&
(le32_to_cpu(cfg->ll_protocol) & GB_I2S_MGMT_PROTOCOL_I2S) &&
(cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) && (cfg->ll_mclk_role & GB_I2S_MGMT_ROLE_MASTER) &&
(cfg->ll_bclk_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_role & GB_I2S_MGMT_ROLE_MASTER) &&
(cfg->ll_wclk_polarity & GB_I2S_MGMT_POLARITY_NORMAL) && (cfg->ll_wclk_polarity & GB_I2S_MGMT_POLARITY_NORMAL) &&
(cfg->ll_wclk_change_edge & GB_I2S_MGMT_EDGE_FALLING) && (cfg->ll_wclk_change_edge & GB_I2S_MGMT_EDGE_FALLING) &&
(cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_FALLING) && (cfg->ll_wclk_tx_edge & GB_I2S_MGMT_EDGE_RISING) &&
(cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_RISING) && (cfg->ll_wclk_rx_edge & GB_I2S_MGMT_EDGE_FALLING) &&
(cfg->ll_data_offset == 1)) (cfg->ll_data_offset == 1))
break; break;
} }
if (i >= CONFIG_COUNT_MAX) { if (i >= CONFIG_COUNT_MAX) {
pr_err("No valid configuration\n"); pr_err("No valid configuration\n");
ret = -EINVAL; return -EINVAL;
goto free_get_cfg;
} }
memcpy(&set_cfg, cfg, sizeof(set_cfg)); 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_protocol = cpu_to_le32(GB_I2S_MGMT_PROTOCOL_I2S);
set_cfg.config.ll_mclk_role = GB_I2S_MGMT_ROLE_MASTER; 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_bclk_role = GB_I2S_MGMT_ROLE_MASTER;
set_cfg.config.ll_wclk_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_polarity = GB_I2S_MGMT_POLARITY_NORMAL;
set_cfg.config.ll_wclk_change_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_FALLING; set_cfg.config.ll_wclk_tx_edge = GB_I2S_MGMT_EDGE_RISING;
set_cfg.config.ll_wclk_rx_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); ret = gb_i2s_mgmt_set_configuration(snd_dev->mgmt_connection, &set_cfg);
if (ret) { if (ret)
pr_err("set_configuration failed: %d\n", 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; return ret;
} }
......
/*
* 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/kernel.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -11,8 +20,8 @@ ...@@ -11,8 +20,8 @@
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
#include <sound/simple_card.h> #include <sound/simple_card.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
#include "audio.h" #include "audio.h"
/* /*
...@@ -32,15 +41,33 @@ static void gb_pcm_work(struct work_struct *work) ...@@ -32,15 +41,33 @@ static void gb_pcm_work(struct work_struct *work)
struct snd_pcm_substream *substream = snd_dev->substream; struct snd_pcm_substream *substream = snd_dev->substream;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int stride, frames, oldptr; unsigned int stride, frames, oldptr;
int period_elapsed; int period_elapsed, ret;
char *address; char *address;
long len; long len;
if (!snd_dev) if (!snd_dev)
return; 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; 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; address = runtime->dma_area + snd_dev->hwptr_done;
...@@ -88,6 +115,7 @@ static enum hrtimer_restart gb_pcm_timer_function(struct hrtimer *hrtimer) ...@@ -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) void gb_pcm_hrtimer_start(struct gb_snd *snd_dev)
{ {
atomic_set(&snd_dev->running, 1); 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_start(&snd_dev->timer, ns_to_ktime(CONFIG_PERIOD_NS),
HRTIMER_MODE_REL); HRTIMER_MODE_REL);
} }
...@@ -96,6 +124,7 @@ void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev) ...@@ -96,6 +124,7 @@ void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev)
{ {
atomic_set(&snd_dev->running, 0); atomic_set(&snd_dev->running, 0);
hrtimer_cancel(&snd_dev->timer); 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) 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) ...@@ -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, static int gb_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) 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, return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params)); params_buffer_bytes(hw_params));
} }
......
/*
* Greybus audio driver
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/i2c.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
#include <sound/simple_card.h> #include <sound/simple_card.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
#include "audio.h" #include "audio.h"
#define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data" #define GB_AUDIO_DATA_DRIVER_NAME "gb_audio_data"
#define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt" #define GB_AUDIO_MGMT_DRIVER_NAME "gb_audio_mgmt"
#define RT5647_I2C_ADAPTER_NR 6
#define RT5647_I2C_ADDR 0x1b
/* /*
* gb_snd management functions * gb_snd management functions
*/ */
...@@ -107,13 +118,17 @@ static struct asoc_simple_card_info *setup_card_info(int device_count) ...@@ -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.platform = obj->platform_name;
obj->card_info.cpu_dai.name = obj->dai_name; obj->card_info.cpu_dai.name = obj->dai_name;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) #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 #endif
#if USE_RT5645 #if USE_RT5645
obj->card_info.daifmt = GB_FMTS; obj->card_info.daifmt = SND_SOC_DAIFMT_NB_NF |
sprintf(obj->codec_name, "rt5645.%s", "6-001b"); /* XXX do i2c bus addr dynamically */ 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.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; obj->card_info.codec_dai.sysclk = 12288000;
#else #else
sprintf(obj->codec_name, "spdif-dit"); sprintf(obj->codec_name, "spdif-dit");
...@@ -150,6 +165,9 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) ...@@ -150,6 +165,9 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection)
struct gb_snd *snd_dev; struct gb_snd *snd_dev;
struct platform_device *codec, *dai; struct platform_device *codec, *dai;
struct asoc_simple_card_info *simple_card; struct asoc_simple_card_info *simple_card;
#if USE_RT5645
struct i2c_board_info rt5647_info;
#endif
unsigned long flags; unsigned long flags;
int ret; int ret;
...@@ -212,6 +230,18 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection) ...@@ -212,6 +230,18 @@ static int gb_i2s_transmitter_connection_init(struct gb_connection *connection)
goto out_get_ver; 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; return 0;
out_get_ver: out_get_ver:
...@@ -231,6 +261,10 @@ static void gb_i2s_transmitter_connection_exit(struct gb_connection *connection) ...@@ -231,6 +261,10 @@ static void gb_i2s_transmitter_connection_exit(struct gb_connection *connection)
snd_dev = (struct gb_snd *)connection->private; 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->card);
platform_device_unregister(&snd_dev->cpu_dai); platform_device_unregister(&snd_dev->cpu_dai);
platform_device_unregister(snd_dev->codec); platform_device_unregister(snd_dev->codec);
...@@ -261,19 +295,30 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) ...@@ -261,19 +295,30 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection)
goto err_free_snd_dev; 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); snd_dev->send_data_req_buf = kzalloc(SEND_DATA_BUF_LEN, GFP_KERNEL);
if (!snd_dev->send_data_req_buf) { if (!snd_dev->send_data_req_buf) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_deactivate_cport; goto err_free_i2s_configs;
} }
return 0; return 0;
err_deactivate_cport: err_free_i2s_configs:
gb_i2s_mgmt_deactivate_cport(connection, CONFIG_I2S_REMOTE_DATA_CPORT); gb_i2s_mgmt_free_cfgs(snd_dev);
err_free_snd_dev: err_free_snd_dev:
gb_free_snd(snd_dev); gb_free_snd(snd_dev);
return ret; return ret;
...@@ -282,12 +327,8 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection) ...@@ -282,12 +327,8 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection)
static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection) static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection)
{ {
struct gb_snd *snd_dev = (struct gb_snd *)connection->private; struct gb_snd *snd_dev = (struct gb_snd *)connection->private;
int ret;
ret = gb_i2s_mgmt_deactivate_cport(connection, gb_i2s_mgmt_free_cfgs(snd_dev);
CONFIG_I2S_REMOTE_DATA_CPORT);
if (ret)
pr_err("deactivate_cport failed: %d\n", ret);
kfree(snd_dev->send_data_req_buf); kfree(snd_dev->send_data_req_buf);
snd_dev->send_data_req_buf = NULL; snd_dev->send_data_req_buf = NULL;
......
/*
* Greybus audio
*
* Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#ifndef __GB_AUDIO_H #ifndef __GB_AUDIO_H
#define __GB_AUDIO_H #define __GB_AUDIO_H
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <sound/soc.h> #include <sound/soc.h>
#include "greybus.h"
#include "gpbridge.h"
#include "greybus.h"
#define GB_SAMPLE_RATE 48000 #define GB_SAMPLE_RATE 48000
#define GB_RATES SNDRV_PCM_RATE_48000 #define GB_RATES SNDRV_PCM_RATE_48000
...@@ -20,9 +29,7 @@ ...@@ -20,9 +29,7 @@
#define CONFIG_SAMPLES_PER_MSG 48L #define CONFIG_SAMPLES_PER_MSG 48L
#define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */ #define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */
#define CONFIG_COUNT_MAX 32 #define CONFIG_COUNT_MAX 20
#define CONFIG_I2S_REMOTE_DATA_CPORT 7 /* XXX shouldn't be hardcoded...*/
#define RT5647_SLAVE_ADDR 0x1b /* from toshiba/quanta code */
/* Switch between dummy spdif and jetson rt5645 codec */ /* Switch between dummy spdif and jetson rt5645 codec */
#define USE_RT5645 0 #define USE_RT5645 0
...@@ -42,9 +49,12 @@ struct gb_snd { ...@@ -42,9 +49,12 @@ struct gb_snd {
struct platform_device cpu_dai; struct platform_device cpu_dai;
struct platform_device *codec; struct platform_device *codec;
struct asoc_simple_card_info *simple_card_info; struct asoc_simple_card_info *simple_card_info;
struct i2c_client *rt5647;
struct gb_connection *mgmt_connection; struct gb_connection *mgmt_connection;
struct gb_connection *i2s_tx_connection; struct gb_connection *i2s_tx_connection;
struct gb_connection *i2s_rx_connection; struct gb_connection *i2s_rx_connection;
struct gb_i2s_mgmt_get_supported_configurations_response
*i2s_configs;
char *send_data_req_buf; char *send_data_req_buf;
long send_data_sample_count; long send_data_sample_count;
int gb_bundle_id; int gb_bundle_id;
...@@ -52,6 +62,7 @@ struct gb_snd { ...@@ -52,6 +62,7 @@ struct gb_snd {
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
struct hrtimer timer; struct hrtimer timer;
atomic_t running; atomic_t running;
bool cport_active;
struct workqueue_struct *workqueue; struct workqueue_struct *workqueue;
struct work_struct work; struct work_struct work;
int hwptr_done; int hwptr_done;
...@@ -78,7 +89,11 @@ int gb_i2s_mgmt_set_configuration(struct gb_connection *connection, ...@@ -78,7 +89,11 @@ int gb_i2s_mgmt_set_configuration(struct gb_connection *connection,
struct gb_i2s_mgmt_set_configuration_request *set_cfg); struct gb_i2s_mgmt_set_configuration_request *set_cfg);
int gb_i2s_mgmt_set_samples_per_message(struct gb_connection *connection, int gb_i2s_mgmt_set_samples_per_message(struct gb_connection *connection,
uint16_t samples_per_message); 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, int gb_i2s_send_data(struct gb_connection *connection, void *req_buf,
void *source_addr, size_t len, int sample_num); void *source_addr, size_t len, int sample_num);
......
/* /*
* Greybus "Core" * Greybus "Core"
* *
* Copyright 2014 Google Inc. * Copyright 2014-2015 Google Inc.
* Copyright 2014 Linaro Ltd. * Copyright 2014-2015 Linaro Ltd.
* *
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
...@@ -185,7 +185,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ...@@ -185,7 +185,7 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
if ((!driver->message_send) || (!driver->message_cancel) || if ((!driver->message_send) || (!driver->message_cancel) ||
(!driver->submit_svc)) { (!driver->submit_svc)) {
pr_err("Must implement all greybus_host_driver callbacks!\n"); 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) { 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 ...@@ -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); hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
if (!hd) if (!hd)
return NULL; return ERR_PTR(-ENOMEM);
kref_init(&hd->kref); kref_init(&hd->kref);
hd->parent = parent; hd->parent = parent;
...@@ -215,16 +215,24 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ...@@ -215,16 +215,24 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
ida_init(&hd->cport_id_map); ida_init(&hd->cport_id_map);
hd->buffer_size_max = buffer_size_max; hd->buffer_size_max = buffer_size_max;
hd->endo = gb_endo_create(hd);
if (!hd->endo) {
greybus_remove_hd(hd);
return NULL;
}
return hd; return hd;
} }
EXPORT_SYMBOL_GPL(greybus_create_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) void greybus_remove_hd(struct greybus_host_device *hd)
{ {
/* /*
......
/* /*
* Greybus endo code * Greybus endo code
* *
* Copyright 2015 Google Inc. * Copyright 2014-2015 Google Inc.
* Copyright 2014 Linaro Ltd. * Copyright 2014-2015 Linaro Ltd.
* *
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
...@@ -29,36 +29,82 @@ ...@@ -29,36 +29,82 @@
#define endo_back_left_ribs(id, ribs) (((id) >> (ribs)) & ENDO_BACK_SIDE_RIBS_MASK(ribs)) #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)) #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 */ /* endo sysfs attributes */
static ssize_t serial_number_show(struct device *dev, 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_info.serial_number[0]);
}
static DEVICE_ATTR_RO(svc_serial_number);
static ssize_t svc_version_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct gb_endo *endo = to_gb_endo(dev); struct gb_endo *endo = to_gb_endo(dev);
return sprintf(buf, "%s", &endo->svc.serial_number[0]); return sprintf(buf, "%s", &endo->svc_info.version[0]);
} }
static DEVICE_ATTR_RO(serial_number); static DEVICE_ATTR_RO(svc_version);
static struct attribute *svc_attrs[] = {
&dev_attr_svc_serial_number.attr,
&dev_attr_svc_version.attr,
NULL,
};
static ssize_t version_show(struct device *dev, struct device_attribute *attr, static const struct attribute_group svc_group = {
char *buf) .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); struct gb_endo *endo = to_gb_endo(dev);
return sprintf(buf, "%s", &endo->svc.version[0]); return sprintf(buf, "0x%04x", endo->id);
} }
static DEVICE_ATTR_RO(version); 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[] = { static struct attribute *endo_attrs[] = {
&dev_attr_serial_number.attr, &dev_attr_endo_id.attr,
&dev_attr_version.attr, &dev_attr_ap_intf_id.attr,
NULL, NULL,
}; };
static const struct attribute_group endo_group = { static const struct attribute_group endo_group = {
.attrs = endo_attrs, .attrs = endo_attrs,
.name = "SVC", .name = "Endo",
}; };
static const struct attribute_group *endo_groups[] = { static const struct attribute_group *endo_groups[] = {
&endo_group, &endo_group,
&svc_group,
NULL, NULL,
}; };
...@@ -361,19 +407,12 @@ static int create_modules(struct gb_endo *endo) ...@@ -361,19 +407,12 @@ static int create_modules(struct gb_endo *endo)
int prev_module_id = 0; int prev_module_id = 0;
int interface_id; int interface_id;
int module_id; int module_id;
int interfaces; int max_id;
/* max_id = max_endo_interface_id(&endo->layout);
* Total number of interfaces:
* - Front: 4
* - Back:
* - Left: max_ribs + 1
* - Right: max_ribs + 1
*/
interfaces = 4 + (endo->layout.max_ribs + 1) * 2;
/* Find module corresponding to each interface */ /* 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); module_id = endo_get_module_id(endo, interface_id);
if (WARN_ON(!module_id)) if (WARN_ON(!module_id))
...@@ -409,8 +448,8 @@ static int gb_endo_register(struct greybus_host_device *hd, ...@@ -409,8 +448,8 @@ static int gb_endo_register(struct greybus_host_device *hd,
// FIXME // FIXME
// Get the version and serial number from the SVC, right now we are // Get the version and serial number from the SVC, right now we are
// using "fake" numbers. // using "fake" numbers.
strcpy(&endo->svc.serial_number[0], "042"); strcpy(&endo->svc_info.serial_number[0], "042");
strcpy(&endo->svc.version[0], "0.0"); strcpy(&endo->svc_info.version[0], "0.0");
dev_set_name(&endo->dev, "endo-0x%04x", endo->id); dev_set_name(&endo->dev, "endo-0x%04x", endo->id);
retval = device_add(&endo->dev); retval = device_add(&endo->dev);
...@@ -423,24 +462,31 @@ static int gb_endo_register(struct greybus_host_device *hd, ...@@ -423,24 +462,31 @@ static int gb_endo_register(struct greybus_host_device *hd,
return retval; 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; struct gb_endo *endo;
int retval; int retval;
u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC
endo = kzalloc(sizeof(*endo), GFP_KERNEL); endo = kzalloc(sizeof(*endo), GFP_KERNEL);
if (!endo) if (!endo)
return NULL; return ERR_PTR(-ENOMEM);
/* First check if the value supplied is a valid endo id */ /* 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; goto free_endo;
}
if (ap_intf_id > max_endo_interface_id(&endo->layout)) {
retval = -EINVAL;
goto free_endo;
}
endo->id = endo_id; endo->id = endo_id;
endo->ap_intf_id = ap_intf_id;
/* Register Endo device */ /* Register Endo device */
if (gb_endo_register(hd, endo)) retval = gb_endo_register(hd, endo);
if (retval)
goto free_endo; goto free_endo;
/* Create modules/interfaces */ /* Create modules/interfaces */
...@@ -454,7 +500,8 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd) ...@@ -454,7 +500,8 @@ struct gb_endo *gb_endo_create(struct greybus_host_device *hd)
free_endo: free_endo:
kfree(endo); kfree(endo);
return NULL;
return ERR_PTR(retval);
} }
void gb_endo_remove(struct gb_endo *endo) void gb_endo_remove(struct gb_endo *endo)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Greybus endo code * Greybus endo code
* *
* Copyright 2015 Google Inc. * Copyright 2015 Google Inc.
* Copyright 2015 Linaro Ltd.
* *
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
...@@ -10,7 +11,7 @@ ...@@ -10,7 +11,7 @@
#define __ENDO_H #define __ENDO_H
/* Greybus "public" definitions" */ /* Greybus "public" definitions" */
struct gb_svc { struct gb_svc_info {
u8 serial_number[10]; u8 serial_number[10];
u8 version[10]; u8 version[10];
}; };
...@@ -36,10 +37,11 @@ struct endo_layout { ...@@ -36,10 +37,11 @@ struct endo_layout {
}; };
struct gb_endo { struct gb_endo {
struct endo_layout layout;
struct device dev; struct device dev;
struct gb_svc svc; struct endo_layout layout;
struct gb_svc_info svc_info;
u16 id; u16 id;
u8 ap_intf_id;
}; };
#define to_gb_endo(d) container_of(d, struct gb_endo, dev) #define to_gb_endo(d) container_of(d, struct gb_endo, dev)
...@@ -47,7 +49,8 @@ struct gb_endo { ...@@ -47,7 +49,8 @@ struct gb_endo {
/* Greybus "private" definitions */ /* Greybus "private" definitions */
struct greybus_host_device; 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); void gb_endo_remove(struct gb_endo *endo);
u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id); u8 endo_get_module_id(struct gb_endo *endo, u8 interface_id);
......
/* /*
* Greybus "AP" USB driver * Greybus "AP" USB driver for "ES1" controller chips
* *
* Copyright 2014 Google Inc. * Copyright 2014-2015 Google Inc.
* Copyright 2014 Linaro Ltd. * Copyright 2014-2015 Linaro Ltd.
* *
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
...@@ -551,14 +551,16 @@ static int ap_probe(struct usb_interface *interface, ...@@ -551,14 +551,16 @@ static int ap_probe(struct usb_interface *interface,
bool bulk_out_found = false; bool bulk_out_found = false;
int retval = -ENOMEM; int retval = -ENOMEM;
int i; 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; u8 svc_interval = 0;
udev = usb_get_dev(interface_to_usbdev(interface)); udev = usb_get_dev(interface_to_usbdev(interface));
hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX);
if (!hd) { if (IS_ERR(hd)) {
usb_put_dev(udev); usb_put_dev(udev);
return -ENOMEM; return PTR_ERR(hd);
} }
es1 = hd_to_es1(hd); es1 = hd_to_es1(hd);
...@@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface, ...@@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface,
gb_debugfs_get(), es1, gb_debugfs_get(), es1,
&apb1_log_enable_fops); &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; return 0;
error: error:
ap_disconnect(interface); ap_disconnect(interface);
......
...@@ -551,14 +551,16 @@ static int ap_probe(struct usb_interface *interface, ...@@ -551,14 +551,16 @@ static int ap_probe(struct usb_interface *interface,
bool bulk_out_found = false; bool bulk_out_found = false;
int retval = -ENOMEM; int retval = -ENOMEM;
int i; 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; u8 svc_interval = 0;
udev = usb_get_dev(interface_to_usbdev(interface)); udev = usb_get_dev(interface_to_usbdev(interface));
hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX); hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX);
if (!hd) { if (IS_ERR(hd)) {
usb_put_dev(udev); usb_put_dev(udev);
return -ENOMEM; return PTR_ERR(hd);
} }
es1 = hd_to_es1(hd); es1 = hd_to_es1(hd);
...@@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface, ...@@ -659,6 +661,17 @@ static int ap_probe(struct usb_interface *interface,
gb_debugfs_get(), es1, gb_debugfs_get(), es1,
&apb1_log_enable_fops); &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; return 0;
error: error:
ap_disconnect(interface); ap_disconnect(interface);
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
struct gb_gpio_line { struct gb_gpio_line {
/* The following has to be an array of line_max entries */ /* The following has to be an array of line_max entries */
......
/* /*
* Greybus driver and device API * Greybus driver and device API
* *
* Copyright 2014 Google Inc. * Copyright 2014-2015 Google Inc.
* Copyright 2014 Linaro Ltd. * Copyright 2014-2015 Linaro Ltd.
* *
* Released under the GPLv2 only. * Released under the GPLv2 only.
*/ */
...@@ -23,8 +23,10 @@ ...@@ -23,8 +23,10 @@
#include "kernel_ver.h" #include "kernel_ver.h"
#include "greybus_id.h" #include "greybus_id.h"
#include "greybus_manifest.h" #include "greybus_manifest.h"
#include "greybus_protocols.h"
#include "manifest.h" #include "manifest.h"
#include "endo.h" #include "endo.h"
#include "svc.h"
#include "module.h" #include "module.h"
#include "interface.h" #include "interface.h"
#include "bundle.h" #include "bundle.h"
...@@ -107,6 +109,8 @@ struct greybus_host_device { ...@@ -107,6 +109,8 @@ struct greybus_host_device {
struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd, struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *hd,
struct device *parent, struct device *parent,
size_t buffer_size_max); 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); void greybus_remove_hd(struct greybus_host_device *hd);
struct greybus_driver { struct greybus_driver {
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
* See "Greybus Application Protocol" document (version 0.1) for * See "Greybus Application Protocol" document (version 0.1) for
* details on these values and structures. * details on these values and structures.
* *
* Copyright 2014 Google Inc. * Copyright 2014-2015 Google Inc.
* Copyright 2014 Linaro Ltd. * Copyright 2014-2015 Linaro Ltd.
* *
* Released under the GPLv2 and BSD licenses. * Released under the GPLv2 and BSD licenses.
*/ */
...@@ -42,6 +42,7 @@ enum greybus_protocol { ...@@ -42,6 +42,7 @@ enum greybus_protocol {
GREYBUS_PROTOCOL_LOOPBACK = 0x11, GREYBUS_PROTOCOL_LOOPBACK = 0x11,
GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12, GREYBUS_PROTOCOL_I2S_RECEIVER = 0x12,
GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13, GREYBUS_PROTOCOL_I2S_TRANSMITTER = 0x13,
GREYBUS_PROTOCOL_SVC = 0x14,
/* ... */ /* ... */
GREYBUS_PROTOCOL_RAW = 0xfe, GREYBUS_PROTOCOL_RAW = 0xfe,
GREYBUS_PROTOCOL_VENDOR = 0xff, GREYBUS_PROTOCOL_VENDOR = 0xff,
......
...@@ -489,4 +489,61 @@ struct gb_spi_transfer_response { ...@@ -489,4 +489,61 @@ struct gb_spi_transfer_response {
__u8 data[0]; /* inbound data */ __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__ */ #endif /* __GB_GPBRIDGE_H__ */
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
struct gb_i2c_device { struct gb_i2c_device {
struct gb_connection *connection; struct gb_connection *connection;
......
...@@ -108,7 +108,7 @@ int __init gb_##__protocol##_init(void) \ ...@@ -108,7 +108,7 @@ int __init gb_##__protocol##_init(void) \
{ \ { \
return gb_protocol_register(&__protocol); \ return gb_protocol_register(&__protocol); \
} \ } \
void __exit gb_##__protocol##_exit(void) \ void gb_##__protocol##_exit(void) \
{ \ { \
gb_protocol_deregister(&__protocol); \ gb_protocol_deregister(&__protocol); \
} \ } \
......
...@@ -11,8 +11,8 @@ ...@@ -11,8 +11,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pwm.h> #include <linux/pwm.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
struct gb_pwm_chip { struct gb_pwm_chip {
struct gb_connection *connection; struct gb_connection *connection;
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include "greybus.h" #include "greybus.h"
#include "gpbridge.h"
struct gb_spi { struct gb_spi {
struct gb_connection *connection; struct gb_connection *connection;
......
/*
* 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);
}
/*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment