blob: 9ca3164b13323674eb814d6e4fa5a1a0b1969b0a [file] [log] [blame]
/*
* 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"
#define GB_I2S_MGMT_VERSION_MAJOR 0x00
#define GB_I2S_MGMT_VERSION_MINOR 0x01
#define GB_I2S_DATA_VERSION_MAJOR 0x00
#define GB_I2S_MGMT_VERSION_MINOR 0x01
/***********************************
* GB I2S helper functions
***********************************/
int gb_i2s_mgmt_get_version(struct gb_connection *connection)
{
struct gb_protocol_version_response response;
memset(&response, 0, sizeof(response));
return gb_protocol_get_version(connection,
GB_I2S_MGMT_TYPE_PROTOCOL_VERSION,
NULL, 0, &response,
GB_I2S_MGMT_VERSION_MAJOR);
}
int gb_i2s_data_get_version(struct gb_connection *connection)
{
struct gb_protocol_version_response response;
memset(&response, 0, sizeof(response));
return gb_protocol_get_version(connection,
GB_I2S_DATA_TYPE_PROTOCOL_VERSION,
NULL, 0, &response,
GB_I2S_DATA_VERSION_MAJOR);
}
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;
}