ASoC: msm: HDMI PCM 6 channel support
HDMI 1.3 supports Multi channel PCM up to 8 channels with sample
size of 16/20/24 bits and sample rate of 32, 44.1, 48, 96, 176.4,
192K. This patch add supports for 6 channel PCM at 48K sample rate
with sample size of 16 bits.
Change-Id: Id09f1f9d7ef2e2444c8c1b661bfc5b3b4c1e66a6
Signed-off-by: Kiran Kandi <kkandi@codeaurora.org>
diff --git a/sound/soc/msm/qdsp6/q6adm.c b/sound/soc/msm/qdsp6/q6adm.c
index 177e1d8..2710fbb 100644
--- a/sound/soc/msm/qdsp6/q6adm.c
+++ b/sound/soc/msm/qdsp6/q6adm.c
@@ -103,7 +103,8 @@
}
switch (data->opcode) {
- case ADM_CMDRSP_COPP_OPEN: {
+ case ADM_CMDRSP_COPP_OPEN:
+ case ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN: {
struct adm_copp_open_respond *open = data->payload;
if (open->copp_id == INVALID_COPP_ID) {
pr_err("%s: invalid coppid rxed %d\n",
@@ -360,6 +361,133 @@
return ret;
}
+
+int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
+ int topology)
+{
+ struct adm_multi_ch_copp_open_command open;
+ int ret = 0;
+ int index;
+
+ pr_debug("%s: port %d path:%d rate:%d channel :%d\n", __func__,
+ port_id, path, rate, channel_mode);
+
+ port_id = afe_convert_virtual_to_portid(port_id);
+
+ if (afe_validate_port(port_id) < 0) {
+ pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
+ return -ENODEV;
+ }
+
+ index = afe_get_port_index(port_id);
+ pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
+
+ if (this_adm.apr == NULL) {
+ this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+ 0xFFFFFFFF, &this_adm);
+ if (this_adm.apr == NULL) {
+ pr_err("%s: Unable to register ADM\n", __func__);
+ ret = -ENODEV;
+ return ret;
+ }
+ rtac_set_adm_handle(this_adm.apr);
+ }
+
+ /* Create a COPP if port id are not enabled */
+ if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
+
+ open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+
+ open.hdr.pkt_size =
+ sizeof(struct adm_multi_ch_copp_open_command);
+ open.hdr.opcode = ADM_CMD_MULTI_CHANNEL_COPP_OPEN;
+ memset(open.dev_channel_mapping, 0, 8);
+
+ if (channel_mode == 1) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 6) {
+ open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
+ open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
+ open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+ open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
+ open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
+ open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
+ } else {
+ pr_err("%s invalid num_chan %d\n", __func__,
+ channel_mode);
+ return -EINVAL;
+ }
+
+
+ open.hdr.src_svc = APR_SVC_ADM;
+ open.hdr.src_domain = APR_DOMAIN_APPS;
+ open.hdr.src_port = port_id;
+ open.hdr.dest_svc = APR_SVC_ADM;
+ open.hdr.dest_domain = APR_DOMAIN_ADSP;
+ open.hdr.dest_port = port_id;
+ open.hdr.token = port_id;
+
+ open.mode = path;
+ open.endpoint_id1 = port_id;
+ open.endpoint_id2 = 0xFFFF;
+
+ /* convert path to acdb path */
+ if (path == ADM_PATH_PLAYBACK)
+ open.topology_id = get_adm_rx_topology();
+ else {
+ open.topology_id = get_adm_tx_topology();
+ if ((open.topology_id ==
+ VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
+ (open.topology_id ==
+ VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) ||
+ (open.topology_id ==
+ VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY))
+ rate = 16000;
+ }
+
+ if (open.topology_id == 0)
+ open.topology_id = topology;
+
+ open.channel_config = channel_mode & 0x00FF;
+ open.rate = rate;
+
+ pr_debug("%s: channel_config=%d port_id=%d rate=%d"
+ " topology_id=0x%X\n", __func__, open.channel_config,
+ open.endpoint_id1, open.rate,
+ open.topology_id);
+
+ atomic_set(&this_adm.copp_stat[index], 0);
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ if (ret < 0) {
+ pr_err("%s:ADM enable for port %d failed\n",
+ __func__, port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback with copp id */
+ ret = wait_event_timeout(this_adm.wait,
+ atomic_read(&this_adm.copp_stat[index]),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s ADM open failed for port %d\n", __func__,
+ port_id);
+ ret = -EINVAL;
+ goto fail_cmd;
+ }
+ }
+ atomic_inc(&this_adm.copp_cnt[index]);
+ return 0;
+
+fail_cmd:
+
+ return ret;
+}
+
int adm_matrix_map(int session_id, int path, int num_copps,
unsigned int *port_id, int copp_id)
{
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 302ef57..ef01fb3 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -77,6 +77,7 @@
if (data->opcode == APR_BASIC_RSP_RESULT) {
switch (payload[0]) {
case AFE_PORT_AUDIO_IF_CONFIG:
+ case AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG:
case AFE_PORT_CMD_STOP:
case AFE_PORT_CMD_START:
case AFE_PORT_CMD_LOOPBACK:
@@ -280,7 +281,7 @@
ret_size = SIZEOF_CFG_CMD(afe_port_mi2s_cfg);
break;
case HDMI_RX:
- ret_size = SIZEOF_CFG_CMD(afe_port_hdmi_cfg);
+ ret_size = SIZEOF_CFG_CMD(afe_port_hdmi_multi_ch_cfg);
break;
case SLIMBUS_0_RX:
case SLIMBUS_0_TX:
@@ -400,13 +401,25 @@
ret = -ENODEV;
return ret;
}
- config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+
+ if (port_id == HDMI_RX) {
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
- config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
- config.hdr.src_port = 0;
- config.hdr.dest_port = 0;
- config.hdr.token = 0;
- config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+ config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = 0;
+ config.hdr.opcode = AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG;
+ } else {
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ config.hdr.pkt_size = afe_sizeof_cfg_cmd(port_id);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = 0;
+ config.hdr.opcode = AFE_PORT_AUDIO_IF_CONFIG;
+ }
if (afe_validate_port(port_id) < 0) {
diff --git a/sound/soc/msm/qdsp6/q6asm.c b/sound/soc/msm/qdsp6/q6asm.c
index 62168d2..dc49f12 100644
--- a/sound/soc/msm/qdsp6/q6asm.c
+++ b/sound/soc/msm/qdsp6/q6asm.c
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -1193,6 +1193,9 @@
case FORMAT_LINEAR_PCM:
open.format = LINEAR_PCM;
break;
+ case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+ open.format = MULTI_CHANNEL_PCM;
+ break;
case FORMAT_MPEG4_AAC:
open.format = MPEG4_AAC;
break;
@@ -1761,6 +1764,66 @@
return -EINVAL;
}
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+ uint32_t rate, uint32_t channels)
+{
+ struct asm_stream_media_format_update fmt;
+ u8 *channel_mapping;
+ int rc = 0;
+
+ pr_debug("%s:session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+ channels);
+
+ q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+
+ fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+
+ fmt.format = MULTI_CHANNEL_PCM;
+ fmt.cfg_size = sizeof(struct asm_multi_channel_pcm_fmt_blk);
+ fmt.write_cfg.multi_ch_pcm_cfg.num_channels = channels;
+ fmt.write_cfg.multi_ch_pcm_cfg.bits_per_sample = 16;
+ fmt.write_cfg.multi_ch_pcm_cfg.sample_rate = rate;
+ fmt.write_cfg.multi_ch_pcm_cfg.is_signed = 1;
+ fmt.write_cfg.multi_ch_pcm_cfg.is_interleaved = 1;
+ channel_mapping =
+ fmt.write_cfg.multi_ch_pcm_cfg.channel_mapping;
+
+ memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (channels == 1) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ } else if (channels == 2) {
+ channel_mapping[0] = PCM_CHANNEL_FL;
+ channel_mapping[1] = PCM_CHANNEL_FR;
+ } else if (channels == 6) {
+ channel_mapping[0] = PCM_CHANNEL_FC;
+ channel_mapping[1] = PCM_CHANNEL_FL;
+ channel_mapping[2] = PCM_CHANNEL_FR;
+ channel_mapping[3] = PCM_CHANNEL_LB;
+ channel_mapping[4] = PCM_CHANNEL_RB;
+ channel_mapping[5] = PCM_CHANNEL_LFE;
+ } else {
+ pr_err("%s: ERROR.unsupported num_ch = %u\n", __func__,
+ channels);
+ return -EINVAL;
+ }
+
+ rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+ if (rc < 0) {
+ pr_err("%s:Comamnd open failed\n", __func__);
+ goto fail_cmd;
+ }
+ rc = wait_event_timeout(ac->cmd_wait,
+ (atomic_read(&ac->cmd_state) == 0), 5*HZ);
+ if (!rc) {
+ pr_err("%s:timeout. waited for FORMAT_UPDATE\n", __func__);
+ goto fail_cmd;
+ }
+ return 0;
+fail_cmd:
+ return -EINVAL;
+}
+
int q6asm_media_format_block_aac(struct audio_client *ac,
struct asm_aac_cfg *cfg)
{