greybus: Merge branch 'master' of github.com:gregkh/greybus
This commit is contained in:
commit
d67a39ab38
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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__ */
|
|
@ -13,8 +13,6 @@
|
|||
#include <linux/i2c.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "gpbridge.h"
|
||||
|
||||
|
||||
struct gb_i2c_device {
|
||||
struct gb_connection *connection;
|
||||
|
|
|
@ -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); \
|
||||
} \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "gpbridge.h"
|
||||
|
||||
struct gb_spi {
|
||||
struct gb_connection *connection;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue