greybus: audio: Remove I2S Bridged-PHY Protcol based audio driver
The Greybus I2S Bridged-PHY Protocol is now deprecated so remove the audio driver that is based on it. CC: Vaibhav Agarwal <vaibhav.agarwal@linaro.org> Signed-off-by: Mark Greer <mgreer@animalcreek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
d6e139bc15
commit
ddc88eff2b
@ -21,11 +21,7 @@ gb-phy-y := gpbridge.o \
|
||||
hid.o \
|
||||
i2c.o \
|
||||
spi.o \
|
||||
usb.o \
|
||||
audio.o \
|
||||
audio-pcm.o \
|
||||
audio-dai.o \
|
||||
audio-gb-cmds.o
|
||||
usb.o
|
||||
|
||||
# Prefix all modules with gb-
|
||||
gb-vibrator-y := vibrator.o
|
||||
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#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 "audio.h"
|
||||
|
||||
/*
|
||||
* This is the greybus cpu dai logic. It really doesn't do much
|
||||
* other then provide the TRIGGER_START/STOP hooks that start
|
||||
* and stop the timer sending audio data in the pcm logic.
|
||||
*/
|
||||
|
||||
|
||||
static int gb_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct gb_snd *snd_dev;
|
||||
|
||||
|
||||
snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
gb_pcm_hrtimer_start(snd_dev);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
gb_pcm_hrtimer_stop(snd_dev);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX This is annoying, if we don't have a set_fmt function
|
||||
* the subsystem returns -ENOTSUPP, which causes applications
|
||||
* to fail, so add a dummy function here.
|
||||
*/
|
||||
static int gb_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dai_ops gb_dai_ops = {
|
||||
.trigger = gb_dai_trigger,
|
||||
.set_fmt = gb_dai_set_fmt,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver gb_cpu_dai = {
|
||||
.name = "gb-cpu-dai",
|
||||
.playback = {
|
||||
.rates = GB_RATES,
|
||||
.formats = GB_FMTS,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &gb_dai_ops,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver gb_soc_component = {
|
||||
.name = "gb-component",
|
||||
};
|
||||
|
||||
static int gb_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gb_snd *snd_dev;
|
||||
int ret;
|
||||
|
||||
snd_dev = (struct gb_snd *)pdev->dev.platform_data;
|
||||
dev_set_drvdata(&pdev->dev, snd_dev);
|
||||
|
||||
ret = snd_soc_register_component(&pdev->dev, &gb_soc_component,
|
||||
&gb_cpu_dai, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gb_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver gb_audio_plat_driver = {
|
||||
.driver = {
|
||||
.name = "gb-dai-audio",
|
||||
},
|
||||
.probe = gb_plat_probe,
|
||||
.remove = gb_plat_remove,
|
||||
};
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Greybus audio commands
|
||||
*
|
||||
* Copyright 2015 Google Inc.
|
||||
* Copyright 2015 Linaro Ltd.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "greybus.h"
|
||||
#include "audio.h"
|
||||
|
||||
/***********************************
|
||||
* GB I2S helper functions
|
||||
***********************************/
|
||||
int gb_i2s_mgmt_activate_cport(struct gb_connection *connection,
|
||||
uint16_t cport)
|
||||
{
|
||||
struct gb_i2s_mgmt_activate_cport_request request;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.cport = cpu_to_le16(cport);
|
||||
|
||||
return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_ACTIVATE_CPORT,
|
||||
&request, sizeof(request), NULL, 0);
|
||||
}
|
||||
|
||||
int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection,
|
||||
uint16_t cport)
|
||||
{
|
||||
struct gb_i2s_mgmt_deactivate_cport_request request;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.cport = cpu_to_le16(cport);
|
||||
|
||||
return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_DEACTIVATE_CPORT,
|
||||
&request, sizeof(request), NULL, 0);
|
||||
}
|
||||
|
||||
int gb_i2s_mgmt_get_supported_configurations(
|
||||
struct gb_connection *connection,
|
||||
struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg,
|
||||
size_t size)
|
||||
{
|
||||
return gb_operation_sync(connection,
|
||||
GB_I2S_MGMT_TYPE_GET_SUPPORTED_CONFIGURATIONS,
|
||||
NULL, 0, get_cfg, size);
|
||||
}
|
||||
|
||||
int gb_i2s_mgmt_set_configuration(struct gb_connection *connection,
|
||||
struct gb_i2s_mgmt_set_configuration_request *set_cfg)
|
||||
{
|
||||
return gb_operation_sync(connection, GB_I2S_MGMT_TYPE_SET_CONFIGURATION,
|
||||
set_cfg, sizeof(*set_cfg), NULL, 0);
|
||||
}
|
||||
|
||||
int gb_i2s_mgmt_set_samples_per_message(
|
||||
struct gb_connection *connection,
|
||||
uint16_t samples_per_message)
|
||||
{
|
||||
struct gb_i2s_mgmt_set_samples_per_message_request request;
|
||||
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.samples_per_message = cpu_to_le16(samples_per_message);
|
||||
|
||||
return gb_operation_sync(connection,
|
||||
GB_I2S_MGMT_TYPE_SET_SAMPLES_PER_MESSAGE,
|
||||
&request, sizeof(request), NULL, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
size = sizeof(*get_cfg) +
|
||||
(CONFIG_COUNT_MAX * sizeof(get_cfg->config[0]));
|
||||
|
||||
get_cfg = kzalloc(size, GFP_KERNEL);
|
||||
if (!get_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = gb_i2s_mgmt_get_supported_configurations(connection, get_cfg,
|
||||
size);
|
||||
if (ret) {
|
||||
pr_err("get_supported_config failed: %d\n", ret);
|
||||
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;
|
||||
}
|
||||
|
||||
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_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");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&set_cfg, cfg, sizeof(set_cfg));
|
||||
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_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(snd_dev->mgmt_connection, &set_cfg);
|
||||
if (ret)
|
||||
pr_err("set_configuration failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gb_i2s_send_data(struct gb_connection *connection,
|
||||
void *req_buf, void *source_addr,
|
||||
size_t len, int sample_num)
|
||||
{
|
||||
struct gb_i2s_send_data_request *gb_req;
|
||||
int ret;
|
||||
|
||||
gb_req = req_buf;
|
||||
gb_req->sample_number = cpu_to_le32(sample_num);
|
||||
|
||||
memcpy((void *)&gb_req->data[0], source_addr, len);
|
||||
|
||||
if (len < MAX_SEND_DATA_LEN)
|
||||
for (; len < MAX_SEND_DATA_LEN; len++)
|
||||
gb_req->data[len] = gb_req->data[len - SAMPLE_SIZE];
|
||||
|
||||
gb_req->size = cpu_to_le32(len);
|
||||
|
||||
ret = gb_operation_sync(connection, GB_I2S_DATA_TYPE_SEND_DATA,
|
||||
(void *) gb_req, SEND_DATA_BUF_LEN, NULL, 0);
|
||||
return ret;
|
||||
}
|
@ -1,308 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#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 "audio.h"
|
||||
|
||||
/*
|
||||
* timer/workqueue logic for pushing pcm data.
|
||||
*
|
||||
* Since when we are playing audio, we don't get any
|
||||
* status or feedback from the codec, we have to use a
|
||||
* hrtimer to trigger sending data to the remote codec.
|
||||
* However since the hrtimer runs in irq context, so we
|
||||
* have to schedule a workqueue to actually send the
|
||||
* greybus data.
|
||||
*/
|
||||
|
||||
static void gb_pcm_work(struct work_struct *work)
|
||||
{
|
||||
struct gb_snd *snd_dev = container_of(work, struct gb_snd, work);
|
||||
struct snd_pcm_substream *substream = snd_dev->substream;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
unsigned int stride, frames, oldptr;
|
||||
int period_elapsed, ret;
|
||||
char *address;
|
||||
long len;
|
||||
|
||||
if (!snd_dev)
|
||||
return;
|
||||
|
||||
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->intf_cport_id);
|
||||
if (ret) /* XXX Do what else with failure? */
|
||||
pr_err("deactivate_cport failed: %d\n", ret);
|
||||
|
||||
snd_dev->cport_active = false;
|
||||
snd_dev->send_data_sample_count = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (!snd_dev->cport_active) {
|
||||
ret = gb_i2s_mgmt_activate_cport(snd_dev->mgmt_connection,
|
||||
snd_dev->i2s_tx_connection->intf_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;
|
||||
|
||||
len = frames_to_bytes(runtime,
|
||||
runtime->buffer_size) - snd_dev->hwptr_done;
|
||||
len = min(len, MAX_SEND_DATA_LEN);
|
||||
gb_i2s_send_data(snd_dev->i2s_tx_connection, snd_dev->send_data_req_buf,
|
||||
address, len, snd_dev->send_data_sample_count);
|
||||
|
||||
snd_dev->send_data_sample_count += CONFIG_SAMPLES_PER_MSG;
|
||||
|
||||
stride = runtime->frame_bits >> 3;
|
||||
frames = len/stride;
|
||||
|
||||
snd_pcm_stream_lock(substream);
|
||||
oldptr = snd_dev->hwptr_done;
|
||||
snd_dev->hwptr_done += len;
|
||||
if (snd_dev->hwptr_done >= runtime->buffer_size * stride)
|
||||
snd_dev->hwptr_done -= runtime->buffer_size * stride;
|
||||
|
||||
frames = (len + (oldptr % stride)) / stride;
|
||||
|
||||
period_elapsed = 0;
|
||||
snd_dev->transfer_done += frames;
|
||||
if (snd_dev->transfer_done >= runtime->period_size) {
|
||||
snd_dev->transfer_done -= runtime->period_size;
|
||||
period_elapsed = 1;
|
||||
}
|
||||
|
||||
snd_pcm_stream_unlock(substream);
|
||||
if (period_elapsed)
|
||||
snd_pcm_period_elapsed(snd_dev->substream);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart gb_pcm_timer_function(struct hrtimer *hrtimer)
|
||||
{
|
||||
struct gb_snd *snd_dev = container_of(hrtimer, struct gb_snd, timer);
|
||||
|
||||
if (!atomic_read(&snd_dev->running))
|
||||
return HRTIMER_NORESTART;
|
||||
queue_work(snd_dev->workqueue, &snd_dev->work);
|
||||
hrtimer_forward_now(hrtimer, ns_to_ktime(CONFIG_PERIOD_NS));
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
hrtimer_init(&snd_dev->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
snd_dev->timer.function = gb_pcm_timer_function;
|
||||
atomic_set(&snd_dev->running, 0);
|
||||
snd_dev->workqueue = alloc_workqueue("gb-audio", WQ_HIGHPRI, 0);
|
||||
if (!snd_dev->workqueue)
|
||||
return -ENOMEM;
|
||||
INIT_WORK(&snd_dev->work, gb_pcm_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Core gb pcm structure
|
||||
*/
|
||||
static struct snd_pcm_hardware gb_plat_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.formats = GB_FMTS,
|
||||
.rates = GB_RATES,
|
||||
.rate_min = 8000,
|
||||
.rate_max = GB_SAMPLE_RATE,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
/* XXX - All the values below are junk */
|
||||
.buffer_bytes_max = 64 * 1024,
|
||||
.period_bytes_min = 32,
|
||||
.period_bytes_max = 8192,
|
||||
.periods_min = 2,
|
||||
.periods_max = 32,
|
||||
};
|
||||
|
||||
static snd_pcm_uframes_t gb_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct gb_snd *snd_dev;
|
||||
|
||||
snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
|
||||
return snd_dev->hwptr_done / (substream->runtime->frame_bits >> 3);
|
||||
}
|
||||
|
||||
static int gb_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct gb_snd *snd_dev;
|
||||
|
||||
snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
snd_dev->hwptr_done = 0;
|
||||
snd_dev->transfer_done = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates[] = {GB_SAMPLE_RATE};
|
||||
static struct snd_pcm_hw_constraint_list constraints_rates = {
|
||||
.count = ARRAY_SIZE(rates),
|
||||
.list = rates,
|
||||
.mask = 0,
|
||||
};
|
||||
|
||||
static int gb_pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct gb_snd *snd_dev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
snd_dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
|
||||
spin_lock_irqsave(&snd_dev->lock, flags);
|
||||
runtime->private_data = snd_dev;
|
||||
snd_dev->substream = substream;
|
||||
ret = gb_pcm_hrtimer_init(snd_dev);
|
||||
spin_unlock_irqrestore(&snd_dev->lock, flags);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snd_soc_set_runtime_hwparams(substream, &gb_plat_pcm_hardware);
|
||||
|
||||
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_rates);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
}
|
||||
|
||||
static int gb_pcm_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
substream->runtime->private_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static int gb_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops gb_pcm_ops = {
|
||||
.open = gb_pcm_open,
|
||||
.close = gb_pcm_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = gb_pcm_hw_params,
|
||||
.hw_free = gb_pcm_hw_free,
|
||||
.prepare = gb_pcm_prepare,
|
||||
.pointer = gb_pcm_pointer,
|
||||
};
|
||||
|
||||
static void gb_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
static int gb_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
|
||||
return snd_pcm_lib_preallocate_pages_for_all(
|
||||
pcm,
|
||||
SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
|
||||
}
|
||||
|
||||
static struct snd_soc_platform_driver gb_soc_platform = {
|
||||
.ops = &gb_pcm_ops,
|
||||
.pcm_new = gb_pcm_new,
|
||||
.pcm_free = gb_pcm_free,
|
||||
};
|
||||
|
||||
static int gb_soc_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
return snd_soc_register_platform(&pdev->dev, &gb_soc_platform);
|
||||
}
|
||||
|
||||
static int gb_soc_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver gb_audio_pcm_driver = {
|
||||
.driver = {
|
||||
.name = "gb-pcm-audio",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = gb_soc_platform_probe,
|
||||
.remove = gb_soc_platform_remove,
|
||||
};
|
@ -1,470 +0,0 @@
|
||||
/*
|
||||
* 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 <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 "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
|
||||
*/
|
||||
static DEFINE_SPINLOCK(gb_snd_list_lock);
|
||||
static LIST_HEAD(gb_snd_list);
|
||||
static int device_count;
|
||||
|
||||
static struct gb_snd *gb_find_snd(int bundle_id)
|
||||
{
|
||||
struct gb_snd *tmp, *ret = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gb_snd_list_lock, flags);
|
||||
list_for_each_entry(tmp, &gb_snd_list, list)
|
||||
if (tmp->gb_bundle_id == bundle_id) {
|
||||
ret = tmp;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&gb_snd_list_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct gb_snd *gb_get_snd(int bundle_id)
|
||||
{
|
||||
struct gb_snd *snd_dev;
|
||||
unsigned long flags;
|
||||
|
||||
snd_dev = gb_find_snd(bundle_id);
|
||||
if (snd_dev)
|
||||
return snd_dev;
|
||||
|
||||
snd_dev = kzalloc(sizeof(*snd_dev), GFP_KERNEL);
|
||||
if (!snd_dev)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&snd_dev->lock);
|
||||
snd_dev->device_count = device_count++;
|
||||
snd_dev->gb_bundle_id = bundle_id;
|
||||
spin_lock_irqsave(&gb_snd_list_lock, flags);
|
||||
list_add(&snd_dev->list, &gb_snd_list);
|
||||
spin_unlock_irqrestore(&gb_snd_list_lock, flags);
|
||||
return snd_dev;
|
||||
}
|
||||
|
||||
static void gb_free_snd(struct gb_snd *snd)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gb_snd_list_lock, flags);
|
||||
if (!snd->i2s_tx_connection &&
|
||||
!snd->mgmt_connection) {
|
||||
list_del(&snd->list);
|
||||
spin_unlock_irqrestore(&gb_snd_list_lock, flags);
|
||||
kfree(snd);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&gb_snd_list_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This is the ASoC simple card binds the platform codec,
|
||||
* cpu-dai and codec-dai togheter
|
||||
*/
|
||||
struct gb_card_info_object {
|
||||
struct asoc_simple_card_info card_info;
|
||||
char codec_name[255];
|
||||
char platform_name[255];
|
||||
char dai_name[255];
|
||||
};
|
||||
|
||||
|
||||
static struct asoc_simple_card_info *setup_card_info(int device_count)
|
||||
{
|
||||
struct gb_card_info_object *obj;
|
||||
|
||||
obj = kzalloc(sizeof(struct gb_card_info_object), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
|
||||
obj->card_info.name = "Greybus Audio Module";
|
||||
obj->card_info.card = "gb-card";
|
||||
obj->card_info.codec = obj->codec_name;
|
||||
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 = SND_SOC_DAIFMT_CBM_CFM;
|
||||
#endif
|
||||
#if USE_RT5645
|
||||
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";
|
||||
#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");
|
||||
obj->card_info.codec_dai.name = "dit-hifi";
|
||||
#endif
|
||||
sprintf(obj->platform_name, "gb-pcm-audio.%i", device_count);
|
||||
sprintf(obj->dai_name, "gb-dai-audio.%i", device_count);
|
||||
|
||||
return &obj->card_info;
|
||||
}
|
||||
|
||||
static void free_card_info(struct asoc_simple_card_info *ci)
|
||||
{
|
||||
struct gb_card_info_object *obj;
|
||||
|
||||
obj = container_of(ci, struct gb_card_info_object, card_info);
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* XXX this is sort of cruddy but I get warnings if
|
||||
* we don't have dev.release handler set.
|
||||
*/
|
||||
static void default_release(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* GB connection hooks
|
||||
*/
|
||||
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;
|
||||
struct i2c_adapter *i2c_adap;
|
||||
#endif
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
snd_dev = gb_get_snd(connection->bundle->id);
|
||||
if (!snd_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
codec = platform_device_register_simple("spdif-dit", -1, NULL, 0);
|
||||
if (!codec) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dai = platform_device_register_simple("gb-pcm-audio", snd_dev->device_count, NULL, 0);
|
||||
if (!dai) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
simple_card = setup_card_info(snd_dev->device_count);
|
||||
if (!simple_card) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&snd_dev->lock, flags);
|
||||
snd_dev->card.name = "asoc-simple-card";
|
||||
snd_dev->card.id = snd_dev->device_count;
|
||||
snd_dev->card.dev.release = default_release; /* XXX - suspicious */
|
||||
|
||||
snd_dev->cpu_dai.name = "gb-dai-audio";
|
||||
snd_dev->cpu_dai.id = snd_dev->device_count;
|
||||
snd_dev->cpu_dai.dev.release = default_release; /* XXX - suspicious */
|
||||
|
||||
|
||||
snd_dev->simple_card_info = simple_card;
|
||||
snd_dev->card.dev.platform_data = simple_card;
|
||||
|
||||
snd_dev->codec = codec;
|
||||
snd_dev->i2s_tx_connection = connection;
|
||||
snd_dev->cpu_dai.dev.platform_data = snd_dev;
|
||||
snd_dev->i2s_tx_connection->private = snd_dev;
|
||||
spin_unlock_irqrestore(&snd_dev->lock, flags);
|
||||
|
||||
ret = platform_device_register(&snd_dev->cpu_dai);
|
||||
if (ret) {
|
||||
pr_err("cpu_dai platform_device register failed\n");
|
||||
goto out_dai;
|
||||
}
|
||||
|
||||
ret = platform_device_register(&snd_dev->card);
|
||||
if (ret) {
|
||||
pr_err("card platform_device register failed\n");
|
||||
goto out_card;
|
||||
}
|
||||
|
||||
#if USE_RT5645
|
||||
rt5647_info.addr = RT5647_I2C_ADDR;
|
||||
strlcpy(rt5647_info.type, "rt5647", I2C_NAME_SIZE);
|
||||
|
||||
i2c_adap = i2c_get_adapter(RT5647_I2C_ADAPTER_NR);
|
||||
if (!i2c_adap) {
|
||||
pr_err("codec unavailable\n");
|
||||
ret = -ENODEV;
|
||||
goto out_get_ver;
|
||||
}
|
||||
|
||||
snd_dev->rt5647 = i2c_new_device(i2c_adap, &rt5647_info);
|
||||
if (!snd_dev->rt5647) {
|
||||
pr_err("can't create rt5647 i2c device\n");
|
||||
goto out_get_ver;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
#if USE_RT5645
|
||||
out_get_ver:
|
||||
platform_device_unregister(&snd_dev->card);
|
||||
#endif
|
||||
out_card:
|
||||
platform_device_unregister(&snd_dev->cpu_dai);
|
||||
out_dai:
|
||||
platform_device_unregister(codec);
|
||||
out:
|
||||
gb_free_snd(snd_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gb_i2s_transmitter_connection_exit(struct gb_connection *connection)
|
||||
{
|
||||
struct gb_snd *snd_dev;
|
||||
|
||||
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);
|
||||
|
||||
free_card_info(snd_dev->simple_card_info);
|
||||
snd_dev->i2s_tx_connection = NULL;
|
||||
gb_free_snd(snd_dev);
|
||||
}
|
||||
|
||||
static int gb_i2s_mgmt_connection_init(struct gb_connection *connection)
|
||||
{
|
||||
struct gb_snd *snd_dev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
snd_dev = gb_get_snd(connection->bundle->id);
|
||||
if (!snd_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&snd_dev->lock, flags);
|
||||
snd_dev->mgmt_connection = connection;
|
||||
connection->private = snd_dev;
|
||||
spin_unlock_irqrestore(&snd_dev->lock, flags);
|
||||
|
||||
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_free_i2s_configs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_i2s_configs:
|
||||
gb_i2s_mgmt_free_cfgs(snd_dev);
|
||||
err_free_snd_dev:
|
||||
gb_free_snd(snd_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection)
|
||||
{
|
||||
struct gb_snd *snd_dev = (struct gb_snd *)connection->private;
|
||||
|
||||
gb_i2s_mgmt_free_cfgs(snd_dev);
|
||||
|
||||
kfree(snd_dev->send_data_req_buf);
|
||||
snd_dev->send_data_req_buf = NULL;
|
||||
|
||||
snd_dev->mgmt_connection = NULL;
|
||||
gb_free_snd(snd_dev);
|
||||
}
|
||||
|
||||
static int gb_i2s_mgmt_report_event_recv(u8 type, struct gb_operation *op)
|
||||
{
|
||||
struct gb_connection *connection = op->connection;
|
||||
struct gb_i2s_mgmt_report_event_request *req = op->request->payload;
|
||||
char *event_name;
|
||||
|
||||
if (type != GB_I2S_MGMT_TYPE_REPORT_EVENT) {
|
||||
dev_err(&connection->bundle->dev, "Invalid request type: %d\n",
|
||||
type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (op->request->payload_size < sizeof(*req)) {
|
||||
dev_err(&connection->bundle->dev,
|
||||
"Short request received (%zu < %zu)\n",
|
||||
op->request->payload_size, sizeof(*req));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (req->event) {
|
||||
case GB_I2S_MGMT_EVENT_UNSPECIFIED:
|
||||
event_name = "UNSPECIFIED";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_HALT:
|
||||
/* XXX Should stop streaming now */
|
||||
event_name = "HALT";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_INTERNAL_ERROR:
|
||||
event_name = "INTERNAL_ERROR";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_PROTOCOL_ERROR:
|
||||
event_name = "PROTOCOL_ERROR";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_FAILURE:
|
||||
event_name = "FAILURE";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_OUT_OF_SEQUENCE:
|
||||
event_name = "OUT_OF_SEQUENCE";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_UNDERRUN:
|
||||
event_name = "UNDERRUN";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_OVERRUN:
|
||||
event_name = "OVERRUN";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_CLOCKING:
|
||||
event_name = "CLOCKING";
|
||||
break;
|
||||
case GB_I2S_MGMT_EVENT_DATA_LEN:
|
||||
event_name = "DATA_LEN";
|
||||
break;
|
||||
default:
|
||||
dev_warn(&connection->bundle->dev,
|
||||
"Unknown I2S Event received: %d\n", req->event);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_warn(&connection->bundle->dev, "I2S Event received: %d - '%s'\n",
|
||||
req->event, event_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gb_protocol gb_i2s_receiver_protocol = {
|
||||
.name = GB_AUDIO_DATA_DRIVER_NAME,
|
||||
.id = GREYBUS_PROTOCOL_I2S_RECEIVER,
|
||||
.major = 0,
|
||||
.minor = 1,
|
||||
.connection_init = gb_i2s_transmitter_connection_init,
|
||||
.connection_exit = gb_i2s_transmitter_connection_exit,
|
||||
.request_recv = NULL,
|
||||
};
|
||||
|
||||
static struct gb_protocol gb_i2s_mgmt_protocol = {
|
||||
.name = GB_AUDIO_MGMT_DRIVER_NAME,
|
||||
.id = GREYBUS_PROTOCOL_I2S_MGMT,
|
||||
.major = 0,
|
||||
.minor = 1,
|
||||
.connection_init = gb_i2s_mgmt_connection_init,
|
||||
.connection_exit = gb_i2s_mgmt_connection_exit,
|
||||
.request_recv = gb_i2s_mgmt_report_event_recv,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This is the basic hook get things initialized and registered w/ gb
|
||||
*/
|
||||
|
||||
int gb_audio_protocol_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = gb_protocol_register(&gb_i2s_mgmt_protocol);
|
||||
if (err) {
|
||||
pr_err("Can't register i2s mgmt protocol driver: %d\n", -err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = gb_protocol_register(&gb_i2s_receiver_protocol);
|
||||
if (err) {
|
||||
pr_err("Can't register Audio protocol driver: %d\n", -err);
|
||||
goto err_unregister_i2s_mgmt;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&gb_audio_plat_driver);
|
||||
if (err) {
|
||||
pr_err("Can't register platform driver: %d\n", -err);
|
||||
goto err_unregister_plat;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&gb_audio_pcm_driver);
|
||||
if (err) {
|
||||
pr_err("Can't register pcm driver: %d\n", -err);
|
||||
goto err_unregister_pcm;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_pcm:
|
||||
platform_driver_unregister(&gb_audio_plat_driver);
|
||||
err_unregister_plat:
|
||||
gb_protocol_deregister(&gb_i2s_receiver_protocol);
|
||||
err_unregister_i2s_mgmt:
|
||||
gb_protocol_deregister(&gb_i2s_mgmt_protocol);
|
||||
return err;
|
||||
}
|
||||
|
||||
void gb_audio_protocol_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&gb_audio_pcm_driver);
|
||||
platform_driver_unregister(&gb_audio_plat_driver);
|
||||
gb_protocol_deregister(&gb_i2s_receiver_protocol);
|
||||
gb_protocol_deregister(&gb_i2s_mgmt_protocol);
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#define GB_SAMPLE_RATE 48000
|
||||
#define GB_RATES SNDRV_PCM_RATE_48000
|
||||
#define GB_FMTS SNDRV_PCM_FMTBIT_S16_LE
|
||||
#define PREALLOC_BUFFER (32 * 1024)
|
||||
#define PREALLOC_BUFFER_MAX (32 * 1024)
|
||||
|
||||
/* assuming 1 ms samples @ 48KHz */
|
||||
#define CONFIG_SAMPLES_PER_MSG 48L
|
||||
#define CONFIG_PERIOD_NS 1000000 /* send msg every 1ms */
|
||||
|
||||
#define CONFIG_COUNT_MAX 20
|
||||
|
||||
/* Switch between dummy spdif and jetson rt5645 codec */
|
||||
#define USE_RT5645 0
|
||||
|
||||
#define SAMPLE_SIZE 4
|
||||
#define MAX_SEND_DATA_LEN (CONFIG_SAMPLES_PER_MSG * SAMPLE_SIZE)
|
||||
#define SEND_DATA_BUF_LEN (sizeof(struct gb_i2s_send_data_request) + \
|
||||
MAX_SEND_DATA_LEN)
|
||||
|
||||
|
||||
/*
|
||||
* This is the gb_snd structure which ties everything together
|
||||
* and fakes DMA interrupts via a timer.
|
||||
*/
|
||||
struct gb_snd {
|
||||
struct platform_device card;
|
||||
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;
|
||||
int device_count;
|
||||
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;
|
||||
int transfer_done;
|
||||
struct list_head list;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* GB I2S cmd functions
|
||||
*/
|
||||
int gb_i2s_mgmt_activate_cport(struct gb_connection *connection,
|
||||
uint16_t cport);
|
||||
int gb_i2s_mgmt_deactivate_cport(struct gb_connection *connection,
|
||||
uint16_t cport);
|
||||
int gb_i2s_mgmt_get_supported_configurations(
|
||||
struct gb_connection *connection,
|
||||
struct gb_i2s_mgmt_get_supported_configurations_response *get_cfg,
|
||||
size_t size);
|
||||
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_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);
|
||||
|
||||
|
||||
/*
|
||||
* GB PCM hooks
|
||||
*/
|
||||
void gb_pcm_hrtimer_start(struct gb_snd *snd_dev);
|
||||
void gb_pcm_hrtimer_stop(struct gb_snd *snd_dev);
|
||||
|
||||
|
||||
/*
|
||||
* Platform drivers
|
||||
*/
|
||||
extern struct platform_driver gb_audio_pcm_driver;
|
||||
extern struct platform_driver gb_audio_plat_driver;
|
||||
|
||||
|
||||
#endif /* __GB_AUDIO_H */
|
@ -53,15 +53,9 @@ static int __init gpbridge_init(void)
|
||||
pr_err("error initializing hid protocol\n");
|
||||
goto error_hid;
|
||||
}
|
||||
if (gb_audio_protocol_init()) {
|
||||
pr_err("error initializing audio protocols\n");
|
||||
goto error_audio;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_audio:
|
||||
gb_hid_protocol_exit();
|
||||
error_hid:
|
||||
gb_spi_protocol_exit();
|
||||
error_spi:
|
||||
@ -83,7 +77,6 @@ module_init(gpbridge_init);
|
||||
|
||||
static void __exit gpbridge_exit(void)
|
||||
{
|
||||
gb_audio_protocol_exit();
|
||||
gb_hid_protocol_exit();
|
||||
gb_spi_protocol_exit();
|
||||
gb_i2c_protocol_exit();
|
||||
|
@ -85,9 +85,6 @@ extern void gb_spi_protocol_exit(void);
|
||||
extern int gb_hid_protocol_init(void);
|
||||
extern void gb_hid_protocol_exit(void);
|
||||
|
||||
extern int gb_audio_protocol_init(void);
|
||||
extern void gb_audio_protocol_exit(void);
|
||||
|
||||
/* __protocol: Pointer to struct gb_protocol */
|
||||
#define gb_protocol_driver(__protocol) \
|
||||
static int __init protocol_init(void) \
|
||||
|
Loading…
Reference in New Issue
Block a user