mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-15 07:04:44 +00:00
bcd8215b49
This macro should be DATA not MGMT. Signed-off-by: Phong Tran <tranmanphong@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
216 lines
5.9 KiB
C
216 lines
5.9 KiB
C
/*
|
|
* 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_DATA_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;
|
|
}
|