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

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

parents d52b35f6 30c6d9d7
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
+10 −1
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>
@@ -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"

/*
+62 −53
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 "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;
	}

	snd_dev->i2s_configs = get_cfg;

	return 0;

err_free_get_cfg:
	kfree(get_cfg);
	return ret;
}

	/* 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) &&
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;
}

+47 −3
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>
@@ -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));
}
+56 −15
Original line number Diff line number Diff line
/*
 * 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 @@ static int gb_i2s_mgmt_connection_init(struct gb_connection *connection)
static void gb_i2s_mgmt_connection_exit(struct gb_connection *connection)
{
	struct gb_snd *snd_dev = (struct gb_snd *)connection->private;
	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;
Loading