Browse Source

dsp: support for AFE SPDIF input interface

Support two SPDIF input and two SPDIF output interfaces in AFE.
Support 61937 compressed capture.

Change-Id: Ie71434eb53be798567a6240e0f4bf171aee305b8
Signed-off-by: Ralf Herz <[email protected]>
Ralf Herz 6 years ago
parent
commit
cc29b9e839
8 changed files with 501 additions and 23 deletions
  1. 281 7
      dsp/q6afe.c
  2. 81 1
      dsp/q6asm.c
  3. 20 3
      dsp/q6audio-v2.c
  4. 17 0
      dsp/q6core.c
  5. 87 10
      include/dsp/apr_audio-v2.h
  6. 11 2
      include/dsp/q6afe-v2.h
  7. 3 0
      include/dsp/q6asm-v2.h
  8. 1 0
      include/dsp/q6core.h

+ 281 - 7
dsp/q6afe.c

@@ -103,6 +103,19 @@ struct afe_ctl {
 	void *rx_private_data;
 	uint32_t mmap_handle;
 
+	void (*pri_spdif_tx_cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv);
+	void (*sec_spdif_tx_cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv);
+	void *pri_spdif_tx_private_data;
+	void *sec_spdif_tx_private_data;
+	struct afe_port_mod_evt_rsp_hdr pri_spdif_evt_pl;
+	struct afe_event_fmt_update pri_spdif_fmt_event;
+	struct afe_port_mod_evt_rsp_hdr sec_spdif_evt_pl;
+	struct afe_event_fmt_update sec_spdif_fmt_event;
+	struct work_struct afe_pri_spdif_work;
+	struct work_struct afe_sec_spdif_work;
+
 	int	topology[AFE_MAX_PORTS];
 	struct cal_type_data *cal_data[MAX_AFE_CAL_TYPES];
 
@@ -353,6 +366,128 @@ static void afe_notify_dc_presence_work_fn(struct work_struct *work)
 		       __func__, event, ret);
 }
 
+
+static const char *const afe_event_port_text[] = {
+	"PORT=Primary",
+	"PORT=Secondary",
+};
+
+static const char * const afe_event_state_text[] = {
+	"STATE=Inactive",
+	"STATE=Active",
+	"STATE=EOS",
+};
+
+static const char *const afe_event_rate_text[] = {
+	"RATE=32000",
+	"RATE=44100",
+	"RATE=48000",
+	"RATE=88200",
+	"RATE=96000",
+	"RATE=176400",
+	"RATE=192000",
+};
+
+static const char *const afe_event_format_text[] = {
+	"FORMAT=LPCM",
+	"FORMAT=Compr",
+};
+
+static void afe_notify_spdif_fmt_update_common(void *payload)
+{
+	int ret = 0;
+	char *env[6];
+	struct afe_port_mod_evt_rsp_hdr *evt_pl;
+	struct afe_event_fmt_update *fmt_event;
+
+	evt_pl = (struct afe_port_mod_evt_rsp_hdr *)payload;
+	fmt_event = (struct afe_event_fmt_update *)
+			(payload + sizeof(struct afe_port_mod_evt_rsp_hdr));
+
+	env[0] = "SPDIF_FMT_UPDATE=TRUE";
+	if (evt_pl->port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX)
+		env[1] = (char *)afe_event_port_text[0];
+	else
+		env[1] = (char *)afe_event_port_text[1];
+
+	switch (fmt_event->status) {
+	case AFE_PORT_STATUS_AUDIO_ACTIVE:
+		env[2] = (char *)afe_event_state_text[1];
+		break;
+	case AFE_PORT_STATUS_AUDIO_EOS:
+		env[2] = (char *)afe_event_state_text[2];
+		break;
+	default:
+		env[2] = (char *)afe_event_state_text[0];
+	}
+
+	switch (fmt_event->sample_rate) {
+	case 32000:
+		env[3] = (char *)afe_event_rate_text[0];
+		break;
+	case 44100:
+		env[3] = (char *)afe_event_rate_text[1];
+		break;
+	case 48000:
+		env[3] = (char *)afe_event_rate_text[2];
+		break;
+	case 88200:
+		env[3] = (char *)afe_event_rate_text[3];
+		break;
+	case 96000:
+		env[3] = (char *)afe_event_rate_text[4];
+		break;
+	case 176400:
+		env[3] = (char *)afe_event_rate_text[5];
+		break;
+	case 192000:
+		env[3] = (char *)afe_event_rate_text[6];
+		break;
+	default:
+		env[3] = (char *)afe_event_rate_text[2];
+	}
+
+	if (fmt_event->data_format == AFE_NON_LINEAR_DATA)
+		env[4] = (char *)afe_event_format_text[1];
+	else
+		env[4] = (char *)afe_event_format_text[0];
+
+	env[5] = NULL;
+
+	ret = q6core_send_uevent_env(this_afe.uevent_data, env);
+	if (ret)
+		pr_err("%s: Send UEvent %s failed: %d\n", __func__,
+			env[0], ret);
+}
+
+static void afe_notify_pri_spdif_fmt_update_work_fn(struct work_struct *work)
+{
+	afe_notify_spdif_fmt_update_common(&this_afe.pri_spdif_evt_pl);
+}
+
+static void afe_notify_sec_spdif_fmt_update_work_fn(struct work_struct *work)
+{
+	afe_notify_spdif_fmt_update_common(&this_afe.sec_spdif_evt_pl);
+}
+
+static void afe_notify_spdif_fmt_update(void *payload)
+{
+	struct afe_port_mod_evt_rsp_hdr *evt_pl;
+
+	evt_pl = (struct afe_port_mod_evt_rsp_hdr *)payload;
+	if (evt_pl->port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) {
+		memcpy(&this_afe.pri_spdif_evt_pl, payload,
+			sizeof(struct afe_port_mod_evt_rsp_hdr) +
+			sizeof(struct afe_event_fmt_update));
+		schedule_work(&this_afe.afe_pri_spdif_work);
+	} else {
+		memcpy(&this_afe.sec_spdif_evt_pl, payload,
+			sizeof(struct afe_port_mod_evt_rsp_hdr) +
+			sizeof(struct afe_event_fmt_update));
+		schedule_work(&this_afe.afe_sec_spdif_work);
+	}
+}
+
 static int32_t afe_callback(struct apr_client_data *data, void *priv)
 {
 	if (!data) {
@@ -549,6 +684,20 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv)
 					flag_dc_presence[1] == 1) {
 					afe_notify_dc_presence();
 				}
+			} else if (evt_pl->port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) {
+				if (this_afe.pri_spdif_tx_cb) {
+					this_afe.pri_spdif_tx_cb(data->opcode,
+						data->token, data->payload,
+						this_afe.pri_spdif_tx_private_data);
+				}
+				afe_notify_spdif_fmt_update(data->payload);
+			} else if (evt_pl->port_id == AFE_PORT_ID_SECONDARY_SPDIF_TX) {
+				if (this_afe.sec_spdif_tx_cb) {
+					this_afe.sec_spdif_tx_cb(data->opcode,
+						data->token, data->payload,
+						this_afe.sec_spdif_tx_private_data);
+				}
+				afe_notify_spdif_fmt_update(data->payload);
 			} else {
 				pr_debug("%s: mod ID = 0x%x event_id = 0x%x\n",
 						__func__, evt_pl->module_id,
@@ -639,6 +788,13 @@ int afe_sizeof_cfg_cmd(u16 port_id)
 		ret_size =
 		SIZEOF_CFG_CMD(afe_param_id_hdmi_multi_chan_audio_cfg);
 		break;
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX:
+		ret_size =
+		SIZEOF_CFG_CMD(afe_param_id_spdif_cfg_v2);
+		break;
 	case SLIMBUS_0_RX:
 	case SLIMBUS_0_TX:
 	case SLIMBUS_1_RX:
@@ -2793,7 +2949,7 @@ int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port,
 					       param_hdr, (u8 *) spdif_port);
 	if (ret) {
 		pr_err("%s: AFE enable for port 0x%x failed ret = %d\n",
-				__func__, port_id, ret);
+			__func__, port_id, ret);
 		goto fail_cmd;
 	}
 
@@ -2806,10 +2962,13 @@ int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port,
 		goto fail_cmd;
 	}
 
-	ret = afe_send_spdif_ch_status_cfg(&spdif_port->ch_status, port_id);
-	if (ret < 0) {
-		pr_err("%s: afe send failed %d\n", __func__, ret);
-		goto fail_cmd;
+	if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) {
+		ret = afe_send_spdif_ch_status_cfg(&spdif_port->ch_status,
+						   port_id);
+		if (ret < 0) {
+			pr_err("%s: afe send failed %d\n", __func__, ret);
+			goto fail_cmd;
+		}
 	}
 
 	return afe_send_cmd_port_start(port_id);
@@ -2819,6 +2978,105 @@ fail_cmd:
 }
 EXPORT_SYMBOL(afe_spdif_port_start);
 
+/**
+ * afe_spdif_reg_event_cfg -
+ *         register for event from AFE spdif port
+ *
+ * @port_id: Port ID to register event
+ * @reg_flag: register or unregister
+ * @cb: callback function to invoke for events from module
+ * @private_data: private data to sent back in callback fn
+ *
+ * Returns 0 on success or error on failure
+ */
+int afe_spdif_reg_event_cfg(u16 port_id, u16 reg_flag,
+		void (*cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv),
+		void *private_data)
+{
+	struct afe_port_cmd_event_cfg *config;
+	struct afe_port_cmd_mod_evt_cfg_payload pl;
+	int index;
+	int ret;
+	int num_events = 1;
+	int cmd_size = sizeof(struct afe_port_cmd_event_cfg) +
+		(num_events * sizeof(struct afe_port_cmd_mod_evt_cfg_payload));
+
+	config = kzalloc(cmd_size, GFP_KERNEL);
+	if (!config)
+		return -ENOMEM;
+
+	if (port_id == AFE_PORT_ID_PRIMARY_SPDIF_TX) {
+		this_afe.pri_spdif_tx_cb = cb;
+		this_afe.pri_spdif_tx_private_data = private_data;
+	} else if (port_id == AFE_PORT_ID_SECONDARY_SPDIF_TX) {
+		this_afe.sec_spdif_tx_cb = cb;
+		this_afe.sec_spdif_tx_private_data = private_data;
+	} else {
+		pr_err("%s: wrong port id 0x%x\n", __func__, port_id);
+		ret = -EINVAL;
+		goto fail_idx;
+	}
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0) {
+		pr_err("%s: Invalid index number: %d\n", __func__, index);
+		ret = -EINVAL;
+		goto fail_idx;
+	}
+
+	memset(&pl, 0, sizeof(pl));
+	pl.module_id = AFE_MODULE_CUSTOM_EVENTS;
+	pl.event_id = AFE_PORT_FMT_UPDATE_EVENT;
+	pl.reg_flag = reg_flag;
+
+	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 = cmd_size;
+	config->hdr.src_port = 1;
+	config->hdr.dest_port = 1;
+	config->hdr.token = index;
+
+	config->hdr.opcode = AFE_PORT_CMD_MOD_EVENT_CFG;
+	config->port_id = q6audio_get_port_id(port_id);
+	config->num_events = num_events;
+	config->version = 1;
+	memcpy(config->payload, &pl, sizeof(pl));
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) config);
+	if (ret < 0) {
+		pr_err("%s: port = 0x%x failed %d\n",
+			__func__, port_id, ret);
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+		(atomic_read(&this_afe.state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_idx;
+	}
+	ret = 0;
+fail_cmd:
+	pr_debug("%s: config.opcode 0x%x status %d\n",
+		__func__, config->hdr.opcode, ret);
+
+fail_idx:
+	kfree(config);
+	return ret;
+}
+EXPORT_SYMBOL(afe_spdif_reg_event_cfg);
+
 int afe_send_slot_mapping_cfg(
 	struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg,
 	u16 port_id)
@@ -3880,7 +4138,10 @@ int afe_get_port_index(u16 port_id)
 	case MI2S_TX: return IDX_MI2S_TX;
 	case HDMI_RX: return IDX_HDMI_RX;
 	case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
-	case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX: return IDX_PRIMARY_SPDIF_RX;
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX: return IDX_PRIMARY_SPDIF_TX;
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX: return IDX_SECONDARY_SPDIF_RX;
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX: return IDX_SECONDARY_SPDIF_TX;
 	case RSVD_2: return IDX_RSVD_2;
 	case RSVD_3: return IDX_RSVD_3;
 	case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
@@ -4259,6 +4520,12 @@ int afe_open(u16 port_id,
 	case DISPLAY_PORT_RX:
 		cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
 		break;
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX:
+		cfg_type = AFE_PARAM_ID_SPDIF_CONFIG;
+		break;
 	case SLIMBUS_0_RX:
 	case SLIMBUS_0_TX:
 	case SLIMBUS_1_RX:
@@ -6100,7 +6367,10 @@ int afe_validate_port(u16 port_id)
 	case MI2S_TX:
 	case HDMI_RX:
 	case DISPLAY_PORT_RX:
-	case AFE_PORT_ID_SPDIF_RX:
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX:
 	case RSVD_2:
 	case RSVD_3:
 	case DIGI_MIC_TX:
@@ -7695,6 +7965,10 @@ int __init afe_init(void)
 	q6core_init_uevent_data(this_afe.uevent_data, "q6afe_uevent");
 
 	INIT_WORK(&this_afe.afe_dc_work, afe_notify_dc_presence_work_fn);
+	INIT_WORK(&this_afe.afe_pri_spdif_work,
+		  afe_notify_pri_spdif_fmt_update_work_fn);
+	INIT_WORK(&this_afe.afe_sec_spdif_work,
+		  afe_notify_sec_spdif_fmt_update_work_fn);
 
 	return 0;
 }

+ 81 - 1
dsp/q6asm.c

@@ -1953,6 +1953,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
 		case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
 		case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
 		case ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS:
+		case ASM_STREAM_CMD_OPEN_READ_COMPRESSED:
 		case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
 			pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n",
 				__func__, ac->session,
@@ -2166,7 +2167,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
 
 		if (ac->io_mode & SYNC_IO_MODE) {
 			if (port->buf == NULL) {
-				pr_err("%s: Unexpected Write Done\n", __func__);
+				pr_err("%s: Unexpected Read Done\n", __func__);
 				spin_unlock_irqrestore(
 					&(session[session_id].session_lock),
 					flags);
@@ -2833,6 +2834,85 @@ int q6asm_set_soft_volume_module_instance_ids(int instance,
 }
 EXPORT_SYMBOL(q6asm_set_soft_volume_module_instance_ids);
 
+/**
+ * q6asm_open_read_compressed -
+ *       command to open ASM in compressed read mode
+ *
+ * @ac: Audio client handle
+ * @format: capture format for ASM
+ * @passthrough_flag: flag to indicate passthrough option
+ *
+ * Returns 0 on success or error on failure
+ */
+int q6asm_open_read_compressed(struct audio_client *ac, uint32_t format,
+			       uint32_t passthrough_flag)
+{
+	int rc = 0;
+	struct asm_stream_cmd_open_read_compressed open;
+
+	if (ac == NULL) {
+		pr_err("%s: ac[%pK] NULL\n",  __func__, ac);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (ac->apr == NULL) {
+		pr_err("%s: APR handle[%pK] NULL\n", __func__,  ac->apr);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	pr_debug("%s: session[%d] wr_format[0x%x]\n", __func__, ac->session,
+		format);
+
+	q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_COMPRESSED;
+	atomic_set(&ac->cmd_state, -1);
+
+	/*
+	 * Below flag indicates whether DSP shall keep IEC61937 packing or
+	 * unpack to raw compressed format
+	 */
+	if (format == FORMAT_IEC61937) {
+		open.mode_flags = 0x1;
+		pr_debug("%s: Flag 1 IEC61937 output\n", __func__);
+	} else {
+		open.mode_flags = 0;
+		open.frames_per_buf = 1;
+		pr_debug("%s: Flag 0 RAW_COMPR output\n", __func__);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n",
+			__func__, open.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for OPEN_READ_COMPR rc[%d]\n",
+			__func__, rc);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+			atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	return 0;
+
+fail_cmd:
+	return rc;
+}
+EXPORT_SYMBOL(q6asm_open_read_compressed);
+
 static int __q6asm_open_read(struct audio_client *ac,
 			     uint32_t format, uint16_t bits_per_sample,
 			     uint32_t pcm_format_block_ver,

+ 20 - 3
dsp/q6audio-v2.c

@@ -50,7 +50,10 @@ int q6audio_get_port_index(u16 port_id)
 	case MI2S_TX: return IDX_MI2S_TX;
 	case HDMI_RX: return IDX_HDMI_RX;
 	case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
-	case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX: return IDX_PRIMARY_SPDIF_RX;
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX: return IDX_PRIMARY_SPDIF_TX;
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX: return IDX_SECONDARY_SPDIF_RX;
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX: return IDX_SECONDARY_SPDIF_TX;
 	case RSVD_2: return IDX_RSVD_2;
 	case RSVD_3: return IDX_RSVD_3;
 	case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
@@ -351,7 +354,14 @@ int q6audio_get_port_id(u16 port_id)
 	case HDMI_RX: return AFE_PORT_ID_MULTICHAN_HDMI_RX;
 	case DISPLAY_PORT_RX:
 			return AFE_PORT_ID_HDMI_OVER_DP_RX;
-	case AFE_PORT_ID_SPDIF_RX: return AFE_PORT_ID_SPDIF_RX;
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
+			return AFE_PORT_ID_PRIMARY_SPDIF_RX;
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX:
+			return AFE_PORT_ID_PRIMARY_SPDIF_TX;
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
+			return AFE_PORT_ID_SECONDARY_SPDIF_RX;
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX:
+			return AFE_PORT_ID_SECONDARY_SPDIF_TX;
 	case RSVD_2: return IDX_RSVD_2;
 	case RSVD_3: return IDX_RSVD_3;
 	case DIGI_MIC_TX: return AFE_PORT_ID_DIGITAL_MIC_TX;
@@ -777,6 +787,10 @@ int q6audio_is_digital_pcm_interface(u16 port_id)
 	case AFE_PORT_ID_WSA_CODEC_DMA_TX_2:
 	case AFE_PORT_ID_VA_CODEC_DMA_TX_0:
 	case AFE_PORT_ID_VA_CODEC_DMA_TX_1:
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX:
 		break;
 	default:
 		ret = -EINVAL;
@@ -854,7 +868,10 @@ int q6audio_validate_port(u16 port_id)
 	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
 	case AFE_PORT_ID_SECONDARY_MI2S_RX:
 	case AFE_PORT_ID_SECONDARY_MI2S_TX:
-	case AFE_PORT_ID_SPDIF_RX:
+	case AFE_PORT_ID_PRIMARY_SPDIF_RX:
+	case AFE_PORT_ID_PRIMARY_SPDIF_TX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_RX:
+	case AFE_PORT_ID_SECONDARY_SPDIF_TX:
 	case AFE_PORT_ID_TERTIARY_MI2S_RX:
 	case AFE_PORT_ID_TERTIARY_MI2S_TX:
 	case AFE_PORT_ID_QUINARY_MI2S_RX:

+ 17 - 0
dsp/q6core.c

@@ -189,6 +189,23 @@ int q6core_send_uevent(struct audio_uevent_data *uevent_data, char *event)
 }
 EXPORT_SYMBOL(q6core_send_uevent);
 
+/**
+ * q6core_send_uevent_env - send uevent with list of keys to userspace.
+ *
+ * @uevent_data: uevent data.
+ * @event: array of event keys to send.
+ *
+ * Returns 0 on success or error otherwise.
+ */
+int q6core_send_uevent_env(struct audio_uevent_data *uevent_data, char *env[])
+{
+	if (!env || !uevent_data)
+		return -EINVAL;
+
+	return kobject_uevent_env(&uevent_data->kobj, KOBJ_CHANGE, env);
+}
+EXPORT_SYMBOL(q6core_send_uevent_env);
+
 static int parse_fwk_version_info(uint32_t *payload)
 {
 	size_t ver_size;

+ 87 - 10
include/dsp/apr_audio-v2.h

@@ -1236,7 +1236,11 @@ struct adm_cmd_connect_afe_port_v5 {
 #define AFE_PORT_ID_QUINARY_PCM_RX       0x103C
 #define AFE_PORT_ID_QUINARY_PCM_TX       0x103D
 
-#define AFE_PORT_ID_SPDIF_RX                0x5000
+#define AFE_PORT_ID_PRIMARY_SPDIF_RX        0x5000
+#define AFE_PORT_ID_PRIMARY_SPDIF_TX        0x5001
+#define AFE_PORT_ID_SECONDARY_SPDIF_RX      0x5002
+#define AFE_PORT_ID_SECONDARY_SPDIF_TX      0x5003
+
 #define  AFE_PORT_ID_RT_PROXY_PORT_001_RX   0x2000
 #define  AFE_PORT_ID_RT_PROXY_PORT_001_TX   0x2001
 #define AFE_PORT_ID_INTERNAL_BT_SCO_RX      0x3000
@@ -2270,6 +2274,7 @@ struct afe_param_id_i2s_cfg {
  * This param id is used to configure PCM interface
  */
 
+#define AFE_API_VERSION_SPDIF_CONFIG_V2 0x2
 #define AFE_API_VERSION_SPDIF_CONFIG 0x1
 #define AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG 0x1
 #define AFE_API_VERSION_SPDIF_CLK_CONFIG 0x1
@@ -2283,10 +2288,20 @@ struct afe_param_id_i2s_cfg {
 #define AFE_PORT_CLK_ROOT_LPAPLL 0x3
 #define AFE_PORT_CLK_ROOT_LPAQ6PLL   0x4
 
-struct afe_param_id_spdif_cfg {
+#define AFE_MODULE_CUSTOM_EVENTS            0x00010251
+
+#define AFE_PORT_FMT_UPDATE_EVENT           0x0001010E
+
+#define AFE_API_VERSION_EVENT_FMT_UPDATE    0x1
+#define AFE_PORT_STATUS_NO_SIGNAL           0
+#define AFE_PORT_STATUS_AUDIO_ACTIVE        1
+#define AFE_PORT_STATUS_AUDIO_EOS           2
+
+struct afe_param_id_spdif_cfg_v2 {
 /* Minor version used for tracking the version of the SPDIF
  * configuration interface.
- * Supported values: #AFE_API_VERSION_SPDIF_CONFIG
+ * Supported values: #AFE_API_VERSION_SPDIF_CONFIG,
+ *                   #AFE_API_VERSION_SPDIF_CONFIG_V2
  */
 	u32	spdif_cfg_minor_version;
 
@@ -2318,6 +2333,8 @@ struct afe_param_id_spdif_cfg {
 	u16	bit_width;
 /* This field must be set to zero. */
 	u16	reserved;
+/* Input select for spdif input, must be set to 0 for spdif output. */
+	u32	src_sel;
 } __packed;
 
 struct afe_param_id_spdif_ch_status_cfg {
@@ -2344,6 +2361,7 @@ struct afe_param_id_spdif_ch_status_cfg {
  */
 } __packed;
 
+/* deprecated */
 struct afe_param_id_spdif_clk_cfg {
 	u32 clk_cfg_minor_version;
 /* Minor version used for tracking the version of SPDIF
@@ -2367,8 +2385,43 @@ struct afe_param_id_spdif_clk_cfg {
  */
 } __packed;
 
+struct afe_event_fmt_update {
+	/* Tracks the configuration of this event. */
+	u32 minor_version;
+
+	/* Detected port status.
+	 * Supported values:
+	 * - #AFE_PORT_STATUS_NO_SIGNAL
+	 * - #AFE_PORT_STATUS_AUDIO_ACTIVE
+	 * - #AFE_PORT_STATUS_AUDIO_EOS
+	 */
+	u32 status;
+
+	/* Sampling rate of the port.
+	 * Supported values:
+	 * - #AFE_PORT_SAMPLE_RATE_32K
+	 * - #AFE_PORT_SAMPLE_RATE_44_1K
+	 * - #AFE_PORT_SAMPLE_RATE_48K
+	 * - #AFE_PORT_SAMPLE_RATE_88_2K
+	 * - #AFE_PORT_SAMPLE_RATE_96K
+	 * - #AFE_PORT_SAMPLE_RATE_176_4K
+	 * - #AFE_PORT_SAMPLE_RATE_192K
+	 */
+	u32 sample_rate;
+
+	/* Data format of the port.
+	 * Supported values:
+	 * - #AFE_LINEAR_PCM_DATA
+	 * - #AFE_NON_LINEAR_DATA
+	 */
+	u16 data_format;
+
+	/* First 6 bytes of channel status bits */
+	u8 channel_status[6];
+} __packed;
+
 struct afe_spdif_port_config {
-	struct afe_param_id_spdif_cfg            cfg;
+	struct afe_param_id_spdif_cfg_v2         cfg;
 	struct afe_param_id_spdif_ch_status_cfg  ch_status;
 } __packed;
 
@@ -4093,7 +4146,7 @@ union afe_port_config {
 	struct afe_param_id_internal_bt_fm_cfg    int_bt_fm;
 	struct afe_param_id_pseudo_port_cfg       pseudo_port;
 	struct afe_param_id_device_hw_delay_cfg   hw_delay;
-	struct afe_param_id_spdif_cfg             spdif;
+	struct afe_param_id_spdif_cfg_v2          spdif;
 	struct afe_param_id_set_topology_cfg      topology;
 	struct afe_param_id_tdm_cfg               tdm;
 	struct afe_param_id_usb_audio_cfg         usb_audio;
@@ -4793,7 +4846,7 @@ struct asm_generic_compressed_fmt_blk_t {
 
 /* Command to send sample rate & channels for IEC61937 (compressed) or IEC60958
  * (pcm) streams. Both audio standards use the same format and are used for
- * HDMI or SPDIF.
+ * HDMI or SPDIF output.
  */
 #define ASM_DATA_CMD_IEC_60958_MEDIA_FMT        0x0001321E
 
@@ -7669,11 +7722,23 @@ struct asm_data_cmd_remove_silence {
 
 #define ASM_STREAM_CMD_OPEN_READ_COMPRESSED                        0x00010D95
 
+/* Bitmask for the IEC 61937 to 61937 pass-through capture. */
+#define ASM_BIT_MASK_IEC_61937_PASS_THROUGH_FLAG        (0x00000001UL)
+
+/* Shift value for the IEC 61937 to 61937 pass-through capture. */
+#define ASM_SHIFT_IEC_61937_PASS_THROUGH_FLAG           0
+
 struct asm_stream_cmd_open_read_compressed {
 	struct apr_hdr hdr;
 	u32                    mode_flags;
 /* Mode flags that indicate whether meta information per encoded
- * frame is to be provided.
+ * frame is to be provided and packaging.
+ * Supported values for bit 0: (IEC 61937 pass-through mode)
+ * - 0 -- Unpack the IEC 61937 format stream to RAW compressed format
+ * - 1 -- Pass-through transfer of the IEC 61937 format stream
+ * - Use #ASM_BIT_MASK_IEC_61937_PASS_THROUGH_FLAG to set the bitmask
+ *   and #ASM_SHIFT_IEC_61937_PASS_THROUGH_FLAG to set the shift value
+ *   for this bit.
  * Supported values for bit 4:
  * - 0 -- Return data buffer contains all encoded frames only; it does
  *      not contain frame metadata.
@@ -7687,7 +7752,9 @@ struct asm_stream_cmd_open_read_compressed {
 	u32                    frames_per_buf;
 /* Indicates the number of frames that need to be returned per
  * read buffer
- * Supported values: should be greater than 0
+ * Supported values: should be greater than 0 for IEC to RAW compressed
+ *                   unpack mode.
+ *                   Value is don't care for IEC 61937 pass-through mode.
  */
 
 } __packed;
@@ -10321,8 +10388,18 @@ enum afe_lpass_clk_mode {
 /* Clock ID for AHB HDMI input */
 #define Q6AFE_LPASS_CLK_ID_AHB_HDMI_INPUT                         0x400
 
-/* Clock ID for SPDIF core */
-#define Q6AFE_LPASS_CLK_ID_SPDIF_CORE                             0x500
+/* Clock ID for the primary SPDIF output core. */
+#define AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE              0x500
+/* Clock ID for the secondary SPDIF output core. */
+#define AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_CORE              0x501
+/* Clock ID for the primary SPDIF input core. */
+#define AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_INPUT_CORE               0x502
+/* Clock ID for the secondary SPDIF input core. */
+#define AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_INPUT_CORE               0x503
+/* Clock ID for the secondary SPDIF output NPL clk. */
+#define AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_NPL               0x504
+/* Clock ID for the primary SPDIF output NPL clk. */
+#define AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_NPL               0x505
 
 
 /* Clock attribute for invalid use (reserved for internal usage) */

+ 11 - 2
include/dsp/q6afe-v2.h

@@ -107,7 +107,7 @@ enum {
 	/* IDX 45->49 */
 	IDX_SLIMBUS_6_RX,
 	IDX_SLIMBUS_6_TX,
-	IDX_SPDIF_RX,
+	IDX_PRIMARY_SPDIF_RX,
 	IDX_GLOBAL_CFG,
 	IDX_AUDIO_PORT_ID_I2S_RX,
 	/* IDX 50->53 */
@@ -229,7 +229,7 @@ enum {
 	IDX_AFE_PORT_ID_QUINARY_TDM_TX_6,
 	IDX_AFE_PORT_ID_QUINARY_TDM_RX_7,
 	IDX_AFE_PORT_ID_QUINARY_TDM_TX_7,
-	/* IDX 161 to 166 */
+	/* IDX 161 to 167 */
 	IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_0,
 	IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_0,
 	IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_1,
@@ -237,6 +237,10 @@ enum {
 	IDX_AFE_PORT_ID_WSA_CODEC_DMA_TX_2,
 	IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_0,
 	IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_1,
+	/* IDX 168 to 170 */
+	IDX_SECONDARY_SPDIF_RX,
+	IDX_PRIMARY_SPDIF_TX,
+	IDX_SECONDARY_SPDIF_TX,
 	AFE_MAX_PORTS
 };
 
@@ -389,6 +393,11 @@ int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg
 int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port,
 		u32 rate);
 
+int afe_spdif_reg_event_cfg(u16 port_id, u16 reg_flag,
+		void (*cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv),
+		void *private_data);
+
 int afe_turn_onoff_hw_mad(u16 mad_type, u16 mad_enable);
 int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type);
 enum afe_mad_type afe_port_get_mad_type(u16 port_id);

+ 3 - 0
include/dsp/q6asm-v2.h

@@ -323,6 +323,9 @@ int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format,
 			       uint16_t bits_per_sample, int32_t stream_id,
 			       bool is_gapless_mode);
 
+int q6asm_open_read_compressed(struct audio_client *ac, uint32_t format,
+				uint32_t passthrough_flag);
+
 int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format,
 				uint32_t passthrough_flag);
 

+ 1 - 0
include/dsp/q6core.h

@@ -35,6 +35,7 @@ struct audio_uevent_data {
 int q6core_init_uevent_data(struct audio_uevent_data *uevent_data, char *name);
 void q6core_destroy_uevent_data(struct audio_uevent_data *uevent_data);
 int q6core_send_uevent(struct audio_uevent_data *uevent_data, char *name);
+int q6core_send_uevent_env(struct audio_uevent_data *uevent_data, char *env[]);
 int q6core_get_avcs_api_version_per_service(uint32_t service_id);
 
 #define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919