Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ddc88eff authored by Mark Greer's avatar Mark Greer Committed by Greg Kroah-Hartman
Browse files

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: default avatarMark Greer <mgreer@animalcreek.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent d6e139bc
Loading
Loading
Loading
Loading
+1 −5
Original line number Diff line number Diff line
@@ -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
+0 −112
Original line number Diff line number Diff line
/*
 * 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,
};
+0 −188
Original line number Diff line number Diff line
/*
 * 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;
}
+0 −308
Original line number Diff line number Diff line
/*
 * 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,
};

drivers/staging/greybus/audio.c

deleted100644 → 0
+0 −470

File deleted.

Preview size limit exceeded, changes collapsed.

Loading