Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29e07131 authored by Ralf Herz's avatar Ralf Herz
Browse files

asoc: Add support for META MI2S ports



QCS405 can group data lines of several MI2S interfaces.
Add backend dai links for new META MI2S ports.

Change-Id: I9fc5ff06b61ffc2e085c5f05a75111b63ceeeb68
Signed-off-by: default avatarRalf Herz <rherz@codeaurora.org>
parent 43c0abd0
Loading
Loading
Loading
Loading
+726 −0
Original line number Diff line number Diff line
@@ -261,6 +261,17 @@ struct msm_dai_q6_mi2s_dai_data {
	struct msm_dai_q6_mi2s_dai_config rx_dai;
};

struct msm_dai_q6_meta_mi2s_dai_data {
	DECLARE_BITMAP(status_mask, STATUS_MAX);
	u16 num_member_ports;
	u16 member_port_id[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
	u16 channel_mode[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
	u32 rate;
	u32 channels;
	u32 bitwidth;
	union afe_port_config port_config;
};

struct msm_dai_q6_cdc_dma_dai_data {
	DECLARE_BITMAP(status_mask, STATUS_MAX);
	DECLARE_BITMAP(hwfree_status, STATUS_MAX);
@@ -6096,6 +6107,42 @@ static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr,
	return -EINVAL;
}

static u16 msm_dai_q6_mi2s_get_num_channels(u16 config)
{
	switch (config) {
	case AFE_PORT_I2S_SD0:
	case AFE_PORT_I2S_SD1:
	case AFE_PORT_I2S_SD2:
	case AFE_PORT_I2S_SD3:
	case AFE_PORT_I2S_SD4:
	case AFE_PORT_I2S_SD5:
	case AFE_PORT_I2S_SD6:
	case AFE_PORT_I2S_SD7:
		return 2;
	case AFE_PORT_I2S_QUAD01:
	case AFE_PORT_I2S_QUAD23:
	case AFE_PORT_I2S_QUAD45:
	case AFE_PORT_I2S_QUAD67:
		return 4;
	case AFE_PORT_I2S_6CHS:
		return 6;
	case AFE_PORT_I2S_8CHS:
	case AFE_PORT_I2S_8CHS_2:
		return 8;
	case AFE_PORT_I2S_10CHS:
		return 10;
	case AFE_PORT_I2S_12CHS:
		return 12;
	case AFE_PORT_I2S_14CHS:
		return 14;
	case AFE_PORT_I2S_16CHS:
		return 16;
	default:
		pr_err("%s: invalid config\n", __func__);
		return 0;
	}
}

static int msm_dai_q6_mi2s_platform_data_validation(
	struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
{
@@ -6261,6 +6308,657 @@ static int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev)
	return 0;
}

static int msm_dai_q6_dai_meta_mi2s_probe(struct snd_soc_dai *dai)
{
	struct msm_meta_mi2s_pdata *meta_mi2s_pdata =
			(struct msm_meta_mi2s_pdata *) dai->dev->platform_data;
	int rc = 0;

	dai->id = meta_mi2s_pdata->intf_id;
	rc = msm_dai_q6_dai_add_route(dai);
	return rc;
}

static int msm_dai_q6_dai_meta_mi2s_remove(struct snd_soc_dai *dai)
{
	return 0;
}

static int msm_dai_q6_meta_mi2s_startup(struct snd_pcm_substream *substream,
				   struct snd_soc_dai *dai)
{
	return 0;
}

static int msm_meta_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id)
{
	int ret = 0;

	switch (stream) {
	case SNDRV_PCM_STREAM_PLAYBACK:
		switch (mi2s_id) {
		case MSM_PRIM_META_MI2S:
			*port_id = AFE_PORT_ID_PRIMARY_META_MI2S_RX;
			break;
		case MSM_SEC_META_MI2S:
			*port_id = AFE_PORT_ID_SECONDARY_META_MI2S_RX;
			break;
		default:
			pr_err("%s: playback err id 0x%x\n",
				__func__, mi2s_id);
			ret = -1;
			break;
		}
		break;

	case SNDRV_PCM_STREAM_CAPTURE:
		switch (mi2s_id) {
		default:
			pr_err("%s: capture err id 0x%x\n", __func__, mi2s_id);
			ret = -1;
			break;
		}
		break;

	default:
		pr_err("%s: default err %d\n", __func__, stream);
		ret = -1;
		break;
	}
	pr_debug("%s: port_id = 0x%x\n", __func__, *port_id);
	return ret;
}

static int msm_dai_q6_meta_mi2s_prepare(struct snd_pcm_substream *substream,
		struct snd_soc_dai *dai)
{
	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
		dev_get_drvdata(dai->dev);
	u16 port_id = 0;
	int rc = 0;

	if (msm_meta_mi2s_get_port_id(dai->id, substream->stream,
		&port_id) != 0) {
		dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
			__func__, port_id);
		return -EINVAL;
	}

	dev_dbg(dai->dev, "%s: dai id %d, afe port id = 0x%x\n"
		"dai_data->channels = %u sample_rate = %u\n", __func__,
		dai->id, port_id, dai_data->channels, dai_data->rate);

	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
		/* PORT START should be set if prepare called
		 * in active state.
		 */
		rc = afe_port_start(port_id, &dai_data->port_config,
				    dai_data->rate);
		if (rc < 0)
			dev_err(dai->dev, "fail to open AFE port 0x%x\n",
				dai->id);
		else
			set_bit(STATUS_PORT_STARTED,
				dai_data->status_mask);
	}

	return rc;
}

static int msm_dai_q6_meta_mi2s_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *dai)
{
	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
		dev_get_drvdata(dai->dev);
	struct afe_param_id_meta_i2s_cfg *port_cfg =
		&dai_data->port_config.meta_i2s;
	int idx = 0;
	u16 port_channels = 0;
	u16 channels_left = 0;

	dai_data->channels = params_channels(params);
	channels_left = dai_data->channels;

	/* map requested channels to channels that member ports provide */
	for (idx = 0; idx < dai_data->num_member_ports; idx++) {
		port_channels = msm_dai_q6_mi2s_get_num_channels(
			dai_data->channel_mode[idx]);

		if (channels_left >= port_channels) {
			port_cfg->member_port_id[idx] =
				dai_data->member_port_id[idx];
			port_cfg->member_port_channel_mode[idx] =
				dai_data->channel_mode[idx];
			channels_left -= port_channels;
		} else {
			switch (channels_left) {
			case 15:
			case 16:
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_16CHS:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_16CHS;
					break;
				default:
					goto error_invalid_data;
				};
				break;
			case 13:
			case 14:
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_14CHS:
				case AFE_PORT_I2S_16CHS:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_14CHS;
					break;
				default:
					goto error_invalid_data;
				};
				break;
			case 11:
			case 12:
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_12CHS:
				case AFE_PORT_I2S_14CHS:
				case AFE_PORT_I2S_16CHS:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_12CHS;
					break;
				default:
					goto error_invalid_data;
				};
				break;
			case 9:
			case 10:
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_10CHS:
				case AFE_PORT_I2S_12CHS:
				case AFE_PORT_I2S_14CHS:
				case AFE_PORT_I2S_16CHS:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_10CHS;
					break;
				default:
					goto error_invalid_data;
				};
				break;
			case 8:
			case 7:
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_8CHS:
				case AFE_PORT_I2S_10CHS:
				case AFE_PORT_I2S_12CHS:
				case AFE_PORT_I2S_14CHS:
				case AFE_PORT_I2S_16CHS:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_8CHS;
					break;
				case AFE_PORT_I2S_8CHS_2:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_8CHS_2;
					break;
				default:
					goto error_invalid_data;
				};
				break;
			case 6:
			case 5:
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_6CHS:
				case AFE_PORT_I2S_8CHS:
				case AFE_PORT_I2S_10CHS:
				case AFE_PORT_I2S_12CHS:
				case AFE_PORT_I2S_14CHS:
				case AFE_PORT_I2S_16CHS:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_6CHS;
					break;
				default:
					goto error_invalid_data;
				};
				break;
			case 4:
			case 3:
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_SD0:
				case AFE_PORT_I2S_SD1:
				case AFE_PORT_I2S_SD2:
				case AFE_PORT_I2S_SD3:
				case AFE_PORT_I2S_SD4:
				case AFE_PORT_I2S_SD5:
				case AFE_PORT_I2S_SD6:
				case AFE_PORT_I2S_SD7:
					goto error_invalid_data;
				case AFE_PORT_I2S_QUAD01:
				case AFE_PORT_I2S_QUAD23:
				case AFE_PORT_I2S_QUAD45:
				case AFE_PORT_I2S_QUAD67:
					port_cfg->member_port_channel_mode[idx]
						= dai_data->channel_mode[idx];
					break;
				case AFE_PORT_I2S_8CHS_2:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_QUAD45;
					break;
				default:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_QUAD01;
				};
				break;
			case 2:
			case 1:
				if (dai_data->channel_mode[idx] <
					AFE_PORT_I2S_SD0)
					goto error_invalid_data;
				switch (dai_data->channel_mode[idx]) {
				case AFE_PORT_I2S_SD0:
				case AFE_PORT_I2S_SD1:
				case AFE_PORT_I2S_SD2:
				case AFE_PORT_I2S_SD3:
				case AFE_PORT_I2S_SD4:
				case AFE_PORT_I2S_SD5:
				case AFE_PORT_I2S_SD6:
				case AFE_PORT_I2S_SD7:
					port_cfg->member_port_channel_mode[idx]
						= dai_data->channel_mode[idx];
					break;
				case AFE_PORT_I2S_QUAD01:
				case AFE_PORT_I2S_6CHS:
				case AFE_PORT_I2S_8CHS:
				case AFE_PORT_I2S_10CHS:
				case AFE_PORT_I2S_12CHS:
				case AFE_PORT_I2S_14CHS:
				case AFE_PORT_I2S_16CHS:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_SD0;
					break;
				case AFE_PORT_I2S_QUAD23:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_SD2;
					break;
				case AFE_PORT_I2S_QUAD45:
				case AFE_PORT_I2S_8CHS_2:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_SD4;
					break;
				case AFE_PORT_I2S_QUAD67:
					port_cfg->member_port_channel_mode[idx]
						= AFE_PORT_I2S_SD6;
					break;
				}
				break;
			case 0:
				port_cfg->member_port_channel_mode[idx] = 0;
			}
			if (port_cfg->member_port_channel_mode[idx] == 0) {
				port_cfg->member_port_id[idx] =
					AFE_PORT_ID_INVALID;
			} else {
				port_cfg->member_port_id[idx] =
					dai_data->member_port_id[idx];
				channels_left -=
					msm_dai_q6_mi2s_get_num_channels(
					port_cfg->member_port_channel_mode[idx]);
			}
		}
	}

	if (channels_left > 0) {
		pr_err("%s: too many channels %d\n",
			__func__, dai_data->channels);
		return -EINVAL;
	}

	dai_data->rate = params_rate(params);
	port_cfg->sample_rate = dai_data->rate;

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
	case SNDRV_PCM_FORMAT_SPECIAL:
		port_cfg->bit_width = 16;
		dai_data->bitwidth = 16;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
	case SNDRV_PCM_FORMAT_S24_3LE:
		port_cfg->bit_width = 24;
		dai_data->bitwidth = 24;
		break;
	default:
		pr_err("%s: format %d\n",
			__func__, params_format(params));
		return -EINVAL;
	}

	port_cfg->minor_version = AFE_API_VERSION_META_I2S_CONFIG;
	port_cfg->data_format = AFE_LINEAR_PCM_DATA;

	dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n"
		"bit_width = %hu ws_src = 0x%x sample_rate = %u\n"
		"member_ports 0x%x 0x%x 0x%x 0x%x\n"
		"sd_lines 0x%x 0x%x 0x%x 0x%x\n",
		__func__, dai->id, dai_data->channels,
		port_cfg->bit_width, port_cfg->ws_src, port_cfg->sample_rate,
		port_cfg->member_port_id[0],
		port_cfg->member_port_id[1],
		port_cfg->member_port_id[2],
		port_cfg->member_port_id[3],
		port_cfg->member_port_channel_mode[0],
		port_cfg->member_port_channel_mode[1],
		port_cfg->member_port_channel_mode[2],
		port_cfg->member_port_channel_mode[3]);
	return 0;

error_invalid_data:
	pr_err("%s: error when assigning member port %d channels (channels_left %d)\n",
		__func__, idx, channels_left);
	return -EINVAL;
}

static int msm_dai_q6_meta_mi2s_set_fmt(struct snd_soc_dai *dai,
	unsigned int fmt)
{
	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
		dev_get_drvdata(dai->dev);

	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
		dev_err(dai->dev, "%s: err chg meta i2s mode while dai running",
			__func__);
		return -EPERM;
	}

	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
	case SND_SOC_DAIFMT_CBS_CFS:
		dai_data->port_config.meta_i2s.ws_src = 1;
		break;
	case SND_SOC_DAIFMT_CBM_CFM:
		dai_data->port_config.meta_i2s.ws_src = 0;
		break;
	default:
		pr_err("%s: fmt %d\n",
			__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
		return -EINVAL;
	}

	return 0;
}

static void msm_dai_q6_meta_mi2s_shutdown(struct snd_pcm_substream *substream,
				     struct snd_soc_dai *dai)
{
	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
		dev_get_drvdata(dai->dev);
	u16 port_id = 0;
	int rc = 0;

	if (msm_meta_mi2s_get_port_id(dai->id, substream->stream,
				 &port_id) != 0) {
		dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
			__func__, port_id);
	}

	dev_dbg(dai->dev, "%s: closing afe port id = 0x%x\n",
			__func__, port_id);

	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
		rc = afe_close(port_id);
		if (rc < 0)
			dev_err(dai->dev, "fail to close AFE port\n");
		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
	}
}

static struct snd_soc_dai_ops msm_dai_q6_meta_mi2s_ops = {
	.startup	= msm_dai_q6_meta_mi2s_startup,
	.prepare	= msm_dai_q6_meta_mi2s_prepare,
	.hw_params	= msm_dai_q6_meta_mi2s_hw_params,
	.set_fmt	= msm_dai_q6_meta_mi2s_set_fmt,
	.shutdown	= msm_dai_q6_meta_mi2s_shutdown,
};

/* Channel min and max are initialized base on platform data */
static struct snd_soc_dai_driver msm_dai_q6_meta_mi2s_dai[] = {
	{
		.playback = {
			.stream_name = "Primary META MI2S Playback",
			.aif_name = "PRI_META_MI2S_RX",
			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
				SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
				SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
				SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
				SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
				SNDRV_PCM_RATE_384000,
			.formats = SNDRV_PCM_FMTBIT_S16_LE |
				SNDRV_PCM_FMTBIT_S24_LE |
				SNDRV_PCM_FMTBIT_S24_3LE,
			.rate_min =     8000,
			.rate_max =     384000,
		},
		.ops = &msm_dai_q6_meta_mi2s_ops,
		.name = "Primary META MI2S",
		.id = AFE_PORT_ID_PRIMARY_META_MI2S_RX,
		.probe = msm_dai_q6_dai_meta_mi2s_probe,
		.remove = msm_dai_q6_dai_meta_mi2s_remove,
	},
	{
		.playback = {
			.stream_name = "Secondary META MI2S Playback",
			.aif_name = "SEC_META_MI2S_RX",
			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
				 SNDRV_PCM_RATE_192000,
			.formats = SNDRV_PCM_FMTBIT_S16_LE,
			.rate_min =     8000,
			.rate_max =     192000,
		},
		.ops = &msm_dai_q6_meta_mi2s_ops,
		.name = "Secondary META MI2S",
		.id = AFE_PORT_ID_SECONDARY_META_MI2S_RX,
		.probe = msm_dai_q6_dai_meta_mi2s_probe,
		.remove = msm_dai_q6_dai_meta_mi2s_remove,
	},
};

static int msm_dai_q6_meta_mi2s_platform_data_validation(
	struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
{
	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
		dev_get_drvdata(&pdev->dev);
	struct msm_meta_mi2s_pdata *meta_mi2s_pdata =
		(struct msm_meta_mi2s_pdata *) pdev->dev.platform_data;
	int rc = 0;
	int idx = 0;
	u16 channel_mode = 0;
	unsigned int ch_cnt = 0;
	unsigned int ch_cnt_sum = 0;
	struct afe_param_id_meta_i2s_cfg *port_cfg =
		&dai_data->port_config.meta_i2s;

	if (meta_mi2s_pdata == NULL) {
		pr_err("%s: meta_mi2s_pdata NULL", __func__);
		return -EINVAL;
	}

	dai_data->num_member_ports = meta_mi2s_pdata->num_member_ports;
	for (idx = 0; idx < meta_mi2s_pdata->num_member_ports; idx++) {
		rc = msm_dai_q6_mi2s_get_lineconfig(
			meta_mi2s_pdata->sd_lines[idx],
			&channel_mode,
			&ch_cnt);
		if (rc < 0) {
			dev_err(&pdev->dev, "invalid META MI2S RX sd line config\n");
			goto rtn;
		}
		if (ch_cnt) {
			msm_mi2s_get_port_id(meta_mi2s_pdata->member_port[idx],
				SNDRV_PCM_STREAM_PLAYBACK,
				&dai_data->member_port_id[idx]);
			dai_data->channel_mode[idx] = channel_mode;
			port_cfg->member_port_id[idx] =
				dai_data->member_port_id[idx];
			port_cfg->member_port_channel_mode[idx] = channel_mode;
		}
		ch_cnt_sum += ch_cnt;
	}

	if (ch_cnt_sum) {
		dai_driver->playback.channels_min = 1;
		dai_driver->playback.channels_max = ch_cnt_sum << 1;
	} else {
		dai_driver->playback.channels_min = 0;
		dai_driver->playback.channels_max = 0;
	}

	dev_dbg(&pdev->dev, "%s: sdline 0x%x 0x%x 0x%x 0x%x\n", __func__,
		dai_data->channel_mode[0], dai_data->channel_mode[1],
		dai_data->channel_mode[2], dai_data->channel_mode[3]);
	dev_dbg(&pdev->dev, "%s: playback ch_max %d\n",
		__func__, dai_driver->playback.channels_max);
rtn:
	return rc;
}

static const struct snd_soc_component_driver msm_q6_meta_mi2s_dai_component = {
	.name		= "msm-dai-q6-meta-mi2s",
};

static int msm_dai_q6_meta_mi2s_dev_probe(struct platform_device *pdev)
{
	struct msm_dai_q6_meta_mi2s_dai_data *dai_data;
	const char *q6_meta_mi2s_dev_id = "qcom,msm-dai-q6-meta-mi2s-dev-id";
	u32 dev_id = 0;
	u32 meta_mi2s_intf = 0;
	struct msm_meta_mi2s_pdata *meta_mi2s_pdata;
	int rc;

	rc = of_property_read_u32(pdev->dev.of_node, q6_meta_mi2s_dev_id,
				  &dev_id);
	if (rc) {
		dev_err(&pdev->dev,
			"%s: missing %s in dt node\n", __func__,
			q6_meta_mi2s_dev_id);
		goto rtn;
	}

	dev_dbg(&pdev->dev, "dev name %s dev id 0x%x\n", dev_name(&pdev->dev),
		dev_id);

	switch (dev_id) {
	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
		meta_mi2s_intf = 0;
		break;
	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
		meta_mi2s_intf = 1;
		break;
	default:
		dev_err(&pdev->dev,
			"%s: Invalid META MI2S ID 0x%x from Device Tree\n",
			__func__, dev_id);
		rc = -ENXIO;
		goto rtn;
	}

	pdev->id = dev_id;

	meta_mi2s_pdata = kzalloc(sizeof(struct msm_meta_mi2s_pdata),
		GFP_KERNEL);
	if (!meta_mi2s_pdata) {
		rc = -ENOMEM;
		goto rtn;
	}

	rc = of_property_read_u32(pdev->dev.of_node,
		"qcom,msm-mi2s-num-members",
		&meta_mi2s_pdata->num_member_ports);
	if (rc) {
		dev_err(&pdev->dev, "%s: invalid num from DT file %s\n",
			__func__, "qcom,msm-mi2s-num-members");
		goto free_pdata;
	}

	if (meta_mi2s_pdata->num_member_ports >
		MAX_NUM_I2S_META_PORT_MEMBER_PORTS) {
		dev_err(&pdev->dev, "%s: num-members %d too large from DT file\n",
			__func__, meta_mi2s_pdata->num_member_ports);
		goto free_pdata;
	}

	rc = of_property_read_u32_array(pdev->dev.of_node,
		"qcom,msm-mi2s-member-id",
		meta_mi2s_pdata->member_port,
		meta_mi2s_pdata->num_member_ports);
	if (rc) {
		dev_err(&pdev->dev, "%s: member-id from DT file %s\n",
			__func__, "qcom,msm-mi2s-member-id");
		goto free_pdata;
	}

	rc = of_property_read_u32_array(pdev->dev.of_node,
		"qcom,msm-mi2s-rx-lines",
		meta_mi2s_pdata->sd_lines,
		meta_mi2s_pdata->num_member_ports);
	if (rc) {
		dev_err(&pdev->dev, "%s: Rx line from DT file %s\n",
			__func__, "qcom,msm-mi2s-rx-lines");
		goto free_pdata;
	}

	dev_dbg(&pdev->dev, "dev name %s num-members=%d\n",
		dev_name(&pdev->dev), meta_mi2s_pdata->num_member_ports);
	dev_dbg(&pdev->dev, "member array (%d, %d, %d, %d)\n",
		meta_mi2s_pdata->member_port[0],
		meta_mi2s_pdata->member_port[1],
		meta_mi2s_pdata->member_port[2],
		meta_mi2s_pdata->member_port[3]);
	dev_dbg(&pdev->dev, "sd-lines array (0x%x, 0x%x, 0x%x, 0x%x)\n",
		meta_mi2s_pdata->sd_lines[0],
		meta_mi2s_pdata->sd_lines[1],
		meta_mi2s_pdata->sd_lines[2],
		meta_mi2s_pdata->sd_lines[3]);

	meta_mi2s_pdata->intf_id = meta_mi2s_intf;

	dai_data = kzalloc(sizeof(struct msm_dai_q6_meta_mi2s_dai_data),
			   GFP_KERNEL);
	if (!dai_data) {
		rc = -ENOMEM;
		goto free_pdata;
	} else
		dev_set_drvdata(&pdev->dev, dai_data);

	pdev->dev.platform_data = meta_mi2s_pdata;

	rc = msm_dai_q6_meta_mi2s_platform_data_validation(pdev,
		&msm_dai_q6_meta_mi2s_dai[meta_mi2s_intf]);
	if (rc < 0)
		goto free_dai_data;

	rc = snd_soc_register_component(&pdev->dev,
		&msm_q6_meta_mi2s_dai_component,
		&msm_dai_q6_meta_mi2s_dai[meta_mi2s_intf], 1);
	if (rc < 0)
		goto err_register;
	return 0;

err_register:
	dev_err(&pdev->dev, "fail to %s\n", __func__);
free_dai_data:
	kfree(dai_data);
free_pdata:
	kfree(meta_mi2s_pdata);
rtn:
	return rc;
}

static int msm_dai_q6_meta_mi2s_dev_remove(struct platform_device *pdev)
{
	snd_soc_unregister_component(&pdev->dev);
	return 0;
}

static const struct snd_soc_component_driver msm_dai_q6_component = {
	.name		= "msm-dai-q6-dev",
};
@@ -6627,6 +7325,24 @@ static struct platform_driver msm_dai_q6_mi2s_driver = {
	},
};

static const struct of_device_id msm_dai_q6_meta_mi2s_dev_dt_match[] = {
	{ .compatible = "qcom,msm-dai-q6-meta-mi2s", },
	{ }
};

MODULE_DEVICE_TABLE(of, msm_dai_q6_meta_mi2s_dev_dt_match);

static struct platform_driver msm_dai_q6_meta_mi2s_driver = {
	.probe  = msm_dai_q6_meta_mi2s_dev_probe,
	.remove  = msm_dai_q6_meta_mi2s_dev_remove,
	.driver = {
		.name = "msm-dai-q6-meta-mi2s",
		.owner = THIS_MODULE,
		.of_match_table = msm_dai_q6_meta_mi2s_dev_dt_match,
		.suppress_bind_attrs = true,
	},
};

static int msm_dai_q6_spdif_dev_probe(struct platform_device *pdev)
{
	int rc, id;
@@ -12175,6 +12891,13 @@ int __init msm_dai_q6_init(void)
		goto dai_q6_mi2s_drv_fail;
	}

	rc = platform_driver_register(&msm_dai_q6_meta_mi2s_driver);
	if (rc) {
		pr_err("%s: fail to register dai META MI2S dev drv\n",
			__func__);
		goto dai_q6_meta_mi2s_drv_fail;
	}

	rc = platform_driver_register(&msm_dai_mi2s_q6);
	if (rc) {
		pr_err("%s: fail to register dai MI2S\n", __func__);
@@ -12224,6 +12947,8 @@ int __init msm_dai_q6_init(void)
dai_spdif_q6_fail:
	platform_driver_unregister(&msm_dai_mi2s_q6);
dai_mi2s_q6_fail:
	platform_driver_unregister(&msm_dai_q6_meta_mi2s_driver);
dai_q6_meta_mi2s_drv_fail:
	platform_driver_unregister(&msm_dai_q6_mi2s_driver);
dai_q6_mi2s_drv_fail:
	platform_driver_unregister(&msm_dai_q6_dev);
@@ -12243,6 +12968,7 @@ void msm_dai_q6_exit(void)
	platform_driver_unregister(&msm_dai_q6_tdm_driver);
	platform_driver_unregister(&msm_dai_q6_spdif_driver);
	platform_driver_unregister(&msm_dai_mi2s_q6);
	platform_driver_unregister(&msm_dai_q6_meta_mi2s_driver);
	platform_driver_unregister(&msm_dai_q6_mi2s_driver);
	platform_driver_unregister(&msm_dai_q6_dev);
	platform_driver_unregister(&msm_dai_q6);
+12 −0
Original line number Diff line number Diff line
@@ -38,6 +38,11 @@
#define MSM_DISPLAY_PORT	0
#define MSM_DISPLAY_PORT1	1

#define MSM_PRIM_META_MI2S 0
#define MSM_SEC_META_MI2S  1
#define MSM_META_MI2S_MIN  MSM_PRIM_META_MI2S
#define MSM_META_MI2S_MAX  MSM_SEC_META_MI2S

struct msm_dai_auxpcm_config {
	u16 mode;
	u16 sync;
@@ -60,6 +65,13 @@ struct msm_mi2s_pdata {
	u16 intf_id;
};

struct msm_meta_mi2s_pdata {
	u32 num_member_ports;
	u32 member_port[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
	u32 sd_lines[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
	u16 intf_id;
};

struct msm_i2s_data {
	u32 capability; /* RX or TX */
	u16 sd_lines;
+237 −1

File changed.

Preview size limit exceeded, changes collapsed.

+5 −0
Original line number Diff line number Diff line
@@ -81,6 +81,9 @@
#define LPASS_BE_SENARY_MI2S_TX "SENARY_MI2S_TX"
#define LPASS_BE_SENARY_MI2S_RX "SENARY_MI2S_RX"

#define LPASS_BE_PRI_META_MI2S_RX "PRI_META_MI2S_RX"
#define LPASS_BE_SEC_META_MI2S_RX "SEC_META_MI2S_RX"

#define LPASS_BE_PRI_TDM_RX_0 "PRI_TDM_RX_0"
#define LPASS_BE_PRI_TDM_TX_0 "PRI_TDM_TX_0"
#define LPASS_BE_PRI_TDM_RX_1 "PRI_TDM_RX_1"
@@ -494,6 +497,8 @@ enum {
	MSM_BACKEND_DAI_SLIMBUS_9_RX,
	MSM_BACKEND_DAI_SLIMBUS_9_TX,
	MSM_BACKEND_DAI_AFE_LOOPBACK_TX,
	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
	MSM_BACKEND_DAI_MAX,
};

+6 −4
Original line number Diff line number Diff line
@@ -1484,14 +1484,16 @@ static const struct snd_kcontrol_new sec_auxpcm_lb_vol_mixer_controls[] = {
};

static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
	SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM, 0, 47,
	0, PCM_FORMAT_MAX_NUM_CHANNEL_V8, msm_qti_pp_get_channel_map_mixer,
	SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM,
	0, PCM_MAX_CHMAP_ID, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8,
	msm_qti_pp_get_channel_map_mixer,
	msm_qti_pp_put_channel_map_mixer),
};

static const struct snd_kcontrol_new multi_ch_channel_map_capture_controls[] = {
	SOC_SINGLE_MULTI_EXT("Capture Device Channel Map", SND_SOC_NOPM, 0, 47,
	0, PCM_FORMAT_MAX_NUM_CHANNEL_V8, msm_qti_pp_get_channel_map_capture,
	SOC_SINGLE_MULTI_EXT("Capture Device Channel Map", SND_SOC_NOPM,
	0, PCM_MAX_CHMAP_ID, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8,
	msm_qti_pp_get_channel_map_capture,
	msm_qti_pp_put_channel_map_capture),
};