diff options
Diffstat (limited to 'sound/soc/qcom/lpass-cpu.c')
-rw-r--r-- | sound/soc/qcom/lpass-cpu.c | 196 |
1 files changed, 150 insertions, 46 deletions
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index c6d26395f13b..e00a4af29c13 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -19,6 +19,16 @@ #include "lpass-lpaif-reg.h" #include "lpass.h" +#define LPASS_CPU_MAX_MI2S_LINES 4 +#define LPASS_CPU_I2S_SD0_MASK BIT(0) +#define LPASS_CPU_I2S_SD1_MASK BIT(1) +#define LPASS_CPU_I2S_SD2_MASK BIT(2) +#define LPASS_CPU_I2S_SD3_MASK BIT(3) +#define LPASS_CPU_I2S_SD0_1_MASK GENMASK(1, 0) +#define LPASS_CPU_I2S_SD2_3_MASK GENMASK(3, 2) +#define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0) +#define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0) + static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { @@ -72,6 +82,7 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); + unsigned int mode; unsigned int regval; int bitwidth, ret; @@ -99,60 +110,84 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - switch (channels) { - case 1: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_MONO; - break; - case 2: - regval |= LPAIF_I2SCTL_SPKMODE_SD0; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 4: - regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; - break; - case 6: - regval |= LPAIF_I2SCTL_SPKMODE_6CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mode = drvdata->mi2s_playback_sd_mode[dai->driver->id]; + else + mode = drvdata->mi2s_capture_sd_mode[dai->driver->id]; + + if (!mode) { + dev_err(dai->dev, "no line is assigned\n"); + return -EINVAL; + } + + switch (channels) { + case 1: + case 2: + switch (mode) { + case LPAIF_I2SCTL_MODE_QUAD01: + case LPAIF_I2SCTL_MODE_6CH: + case LPAIF_I2SCTL_MODE_8CH: + mode = LPAIF_I2SCTL_MODE_SD0; break; - case 8: - regval |= LPAIF_I2SCTL_SPKMODE_8CH; - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + case LPAIF_I2SCTL_MODE_QUAD23: + mode = LPAIF_I2SCTL_MODE_SD2; break; - default: - dev_err(dai->dev, "invalid channels given: %u\n", - channels); + } + + break; + case 4: + if (mode < LPAIF_I2SCTL_MODE_QUAD01) { + dev_err(dai->dev, "cannot configure 4 channels with mode %d\n", + mode); return -EINVAL; } - } else { - switch (channels) { - case 1: - regval |= LPAIF_I2SCTL_MICMODE_SD0; - regval |= LPAIF_I2SCTL_MICMONO_MONO; - break; - case 2: - regval |= LPAIF_I2SCTL_MICMODE_SD0; - regval |= LPAIF_I2SCTL_MICMONO_STEREO; - break; - case 4: - regval |= LPAIF_I2SCTL_MICMODE_QUAD01; - regval |= LPAIF_I2SCTL_MICMONO_STEREO; - break; - case 6: - regval |= LPAIF_I2SCTL_MICMODE_6CH; - regval |= LPAIF_I2SCTL_MICMONO_STEREO; + + switch (mode) { + case LPAIF_I2SCTL_MODE_6CH: + case LPAIF_I2SCTL_MODE_8CH: + mode = LPAIF_I2SCTL_MODE_QUAD01; break; - case 8: - regval |= LPAIF_I2SCTL_MICMODE_8CH; - regval |= LPAIF_I2SCTL_MICMONO_STEREO; + } + break; + case 6: + if (mode < LPAIF_I2SCTL_MODE_6CH) { + dev_err(dai->dev, "cannot configure 6 channels with mode %d\n", + mode); + return -EINVAL; + } + + switch (mode) { + case LPAIF_I2SCTL_MODE_8CH: + mode = LPAIF_I2SCTL_MODE_6CH; break; - default: - dev_err(dai->dev, "invalid channels given: %u\n", - channels); + } + break; + case 8: + if (mode < LPAIF_I2SCTL_MODE_8CH) { + dev_err(dai->dev, "cannot configure 8 channels with mode %d\n", + mode); return -EINVAL; } + break; + default: + dev_err(dai->dev, "invalid channels given: %u\n", channels); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regval |= LPAIF_I2SCTL_SPKMODE(mode); + + if (channels >= 2) + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + else + regval |= LPAIF_I2SCTL_SPKMONO_MONO; + } else { + regval |= LPAIF_I2SCTL_MICMODE(mode); + + if (channels >= 2) + regval |= LPAIF_I2SCTL_MICMONO_STEREO; + else + regval |= LPAIF_I2SCTL_MICMONO_MONO; } ret = regmap_write(drvdata->lpaif_map, @@ -413,6 +448,73 @@ static struct regmap_config lpass_cpu_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, + struct device_node *node, + const char *name) +{ + unsigned int lines[LPASS_CPU_MAX_MI2S_LINES]; + unsigned int sd_line_mask = 0; + int num_lines, i; + + num_lines = of_property_read_variable_u32_array(node, name, lines, 0, + LPASS_CPU_MAX_MI2S_LINES); + if (num_lines < 0) + return LPAIF_I2SCTL_MODE_NONE; + + for (i = 0; i < num_lines; i++) + sd_line_mask |= BIT(lines[i]); + + switch (sd_line_mask) { + case LPASS_CPU_I2S_SD0_MASK: + return LPAIF_I2SCTL_MODE_SD0; + case LPASS_CPU_I2S_SD1_MASK: + return LPAIF_I2SCTL_MODE_SD1; + case LPASS_CPU_I2S_SD2_MASK: + return LPAIF_I2SCTL_MODE_SD2; + case LPASS_CPU_I2S_SD3_MASK: + return LPAIF_I2SCTL_MODE_SD3; + case LPASS_CPU_I2S_SD0_1_MASK: + return LPAIF_I2SCTL_MODE_QUAD01; + case LPASS_CPU_I2S_SD2_3_MASK: + return LPAIF_I2SCTL_MODE_QUAD23; + case LPASS_CPU_I2S_SD0_1_2_MASK: + return LPAIF_I2SCTL_MODE_6CH; + case LPASS_CPU_I2S_SD0_1_2_3_MASK: + return LPAIF_I2SCTL_MODE_8CH; + default: + dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask); + return LPAIF_I2SCTL_MODE_NONE; + } +} + +static void of_lpass_cpu_parse_dai_data(struct device *dev, + struct lpass_data *data) +{ + struct device_node *node; + int ret, id; + + /* Allow all channels by default for backwards compatibility */ + for (id = 0; id < data->variant->num_dai; id++) { + data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; + data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; + } + + for_each_child_of_node(dev->of_node, node) { + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= data->variant->num_dai) { + dev_err(dev, "valid dai id not found: %d\n", ret); + continue; + } + + data->mi2s_playback_sd_mode[id] = + of_lpass_cpu_parse_sd_lines(dev, node, + "qcom,playback-sd-lines"); + data->mi2s_capture_sd_mode[id] = + of_lpass_cpu_parse_sd_lines(dev, node, + "qcom,capture-sd-lines"); + } +} + int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) { struct lpass_data *drvdata; @@ -441,6 +543,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) drvdata->variant = (struct lpass_variant *)match->data; variant = drvdata->variant; + of_lpass_cpu_parse_dai_data(dev, drvdata); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); drvdata->lpaif = devm_ioremap_resource(dev, res); |