greybus: gb-audio: Set I2S Configuration according to ASOC requests

Currently, the audio driver unconditionally sets the I2S
configuration to have a sample rate of 48KHz, two channels,
16 bits per channel, in little endian order.  Make this
more flexible by setting the I2S configuration according to
the arguments passed to the PCM 'hw_params' callback.

To accomplish this, query for the supported I2S configurations
at Greybus protocol init time and save them in the 'snd_dev'
structure.  When the 'hw_params' callback is called, compare its
arguments to the table of supported configurations.  If there is
a match, set the I2S connection accordingly.

Signed-off-by: Mark A. Greer <mgreer@animalcreek.com>
Acked-by: John Stultz <john.stultz@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Mark A. Greer 2015-05-21 15:57:03 -07:00 committed by Greg Kroah-Hartman
parent 48229e592f
commit 6b34099ec3
4 changed files with 80 additions and 34 deletions

View File

@ -89,21 +89,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,19 +107,48 @@ 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) &&
@ -142,12 +162,11 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection)
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;
@ -157,19 +176,17 @@ int gb_i2s_mgmt_setup(struct gb_connection *connection)
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);
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;
return ret;
}
ret = gb_i2s_mgmt_set_samples_per_message(connection,
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);
free_get_cfg:
kfree(get_cfg);
return ret;
}

View File

@ -220,6 +220,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));
}

View File

@ -286,17 +286,23 @@ 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;
}
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_snd_dev;
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;
@ -306,6 +312,8 @@ 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;

View File

@ -45,6 +45,8 @@ struct gb_snd {
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;
@ -79,7 +81,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);