| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | // Copyright (c) 2022, Linaro Limited |
| 3 | |
| 4 | #include <dt-bindings/sound/qcom,q6afe.h> |
| 5 | #include <linux/module.h> |
| 6 | #include <linux/platform_device.h> |
| 7 | #include <sound/soc.h> |
| 8 | #include <sound/soc-dapm.h> |
| 9 | #include <sound/pcm.h> |
| 10 | #include <sound/pcm_params.h> |
| 11 | #include <linux/soundwire/sdw.h> |
| 12 | #include <sound/jack.h> |
| 13 | #include <linux/input-event-codes.h> |
| 14 | #include "qdsp6/q6afe.h" |
| 15 | #include "common.h" |
| 16 | #include "sdw.h" |
| 17 | |
| 18 | struct sc8280xp_snd_data { |
| 19 | bool stream_prepared[AFE_PORT_MAX]; |
| 20 | struct snd_soc_card *card; |
| 21 | struct snd_soc_jack jack; |
| 22 | struct snd_soc_jack dp_jack[8]; |
| 23 | bool jack_setup; |
| 24 | }; |
| 25 | |
| 26 | static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) |
| 27 | { |
| 28 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
| 29 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
| 30 | struct snd_soc_card *card = rtd->card; |
| 31 | struct snd_soc_jack *dp_jack = NULL; |
| 32 | int dp_pcm_id = 0; |
| 33 | |
| 34 | switch (cpu_dai->id) { |
| 35 | case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: |
| 36 | case QUINARY_MI2S_RX...QUINARY_MI2S_TX: |
| 37 | snd_soc_dai_set_fmt(dai: cpu_dai, SND_SOC_DAIFMT_BP_FP); |
| 38 | break; |
| 39 | case WSA_CODEC_DMA_RX_0: |
| 40 | case WSA_CODEC_DMA_RX_1: |
| 41 | /* |
| 42 | * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume |
| 43 | * to reduce the risk of speaker damage until we have active |
| 44 | * speaker protection in place. |
| 45 | */ |
| 46 | snd_soc_limit_volume(card, name: "WSA_RX0 Digital Volume" , max: 81); |
| 47 | snd_soc_limit_volume(card, name: "WSA_RX1 Digital Volume" , max: 81); |
| 48 | snd_soc_limit_volume(card, name: "SpkrLeft PA Volume" , max: 17); |
| 49 | snd_soc_limit_volume(card, name: "SpkrRight PA Volume" , max: 17); |
| 50 | break; |
| 51 | case DISPLAY_PORT_RX_0: |
| 52 | /* DISPLAY_PORT dai ids are not contiguous */ |
| 53 | dp_pcm_id = 0; |
| 54 | dp_jack = &data->dp_jack[dp_pcm_id]; |
| 55 | break; |
| 56 | case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7: |
| 57 | dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1; |
| 58 | dp_jack = &data->dp_jack[dp_pcm_id]; |
| 59 | break; |
| 60 | default: |
| 61 | break; |
| 62 | } |
| 63 | |
| 64 | if (dp_jack) |
| 65 | return qcom_snd_dp_jack_setup(rtd, dp_jack, id: dp_pcm_id); |
| 66 | |
| 67 | return qcom_snd_wcd_jack_setup(rtd, jack: &data->jack, jack_setup: &data->jack_setup); |
| 68 | } |
| 69 | |
| 70 | static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, |
| 71 | struct snd_pcm_hw_params *params) |
| 72 | { |
| 73 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
| 74 | struct snd_interval *rate = hw_param_interval(params, |
| 75 | SNDRV_PCM_HW_PARAM_RATE); |
| 76 | struct snd_interval *channels = hw_param_interval(params, |
| 77 | SNDRV_PCM_HW_PARAM_CHANNELS); |
| 78 | struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); |
| 79 | |
| 80 | rate->min = rate->max = 48000; |
| 81 | snd_mask_set_format(mask: fmt, SNDRV_PCM_FORMAT_S16_LE); |
| 82 | channels->min = 2; |
| 83 | channels->max = 2; |
| 84 | switch (cpu_dai->id) { |
| 85 | case TX_CODEC_DMA_TX_0: |
| 86 | case TX_CODEC_DMA_TX_1: |
| 87 | case TX_CODEC_DMA_TX_2: |
| 88 | case TX_CODEC_DMA_TX_3: |
| 89 | channels->min = 1; |
| 90 | break; |
| 91 | default: |
| 92 | break; |
| 93 | } |
| 94 | |
| 95 | |
| 96 | return 0; |
| 97 | } |
| 98 | |
| 99 | static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) |
| 100 | { |
| 101 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
| 102 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
| 103 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
| 104 | |
| 105 | return qcom_snd_sdw_prepare(substream, stream_prepared: &data->stream_prepared[cpu_dai->id]); |
| 106 | } |
| 107 | |
| 108 | static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) |
| 109 | { |
| 110 | struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); |
| 111 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
| 112 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
| 113 | |
| 114 | return qcom_snd_sdw_hw_free(substream, stream_prepared: &data->stream_prepared[cpu_dai->id]); |
| 115 | } |
| 116 | |
| 117 | static const struct snd_soc_ops sc8280xp_be_ops = { |
| 118 | .startup = qcom_snd_sdw_startup, |
| 119 | .shutdown = qcom_snd_sdw_shutdown, |
| 120 | .hw_free = sc8280xp_snd_hw_free, |
| 121 | .prepare = sc8280xp_snd_prepare, |
| 122 | }; |
| 123 | |
| 124 | static void sc8280xp_add_be_ops(struct snd_soc_card *card) |
| 125 | { |
| 126 | struct snd_soc_dai_link *link; |
| 127 | int i; |
| 128 | |
| 129 | for_each_card_prelinks(card, i, link) { |
| 130 | if (link->no_pcm == 1) { |
| 131 | link->init = sc8280xp_snd_init; |
| 132 | link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup; |
| 133 | link->ops = &sc8280xp_be_ops; |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | static int sc8280xp_platform_probe(struct platform_device *pdev) |
| 139 | { |
| 140 | struct snd_soc_card *card; |
| 141 | struct sc8280xp_snd_data *data; |
| 142 | struct device *dev = &pdev->dev; |
| 143 | int ret; |
| 144 | |
| 145 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
| 146 | if (!card) |
| 147 | return -ENOMEM; |
| 148 | card->owner = THIS_MODULE; |
| 149 | /* Allocate the private data */ |
| 150 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
| 151 | if (!data) |
| 152 | return -ENOMEM; |
| 153 | |
| 154 | card->dev = dev; |
| 155 | dev_set_drvdata(dev, data: card); |
| 156 | snd_soc_card_set_drvdata(card, data); |
| 157 | ret = qcom_snd_parse_of(card); |
| 158 | if (ret) |
| 159 | return ret; |
| 160 | |
| 161 | card->driver_name = of_device_get_match_data(dev); |
| 162 | sc8280xp_add_be_ops(card); |
| 163 | return devm_snd_soc_register_card(dev, card); |
| 164 | } |
| 165 | |
| 166 | static const struct of_device_id snd_sc8280xp_dt_match[] = { |
| 167 | {.compatible = "qcom,kaanapali-sndcard" , "kaanapali" }, |
| 168 | {.compatible = "qcom,qcm6490-idp-sndcard" , "qcm6490" }, |
| 169 | {.compatible = "qcom,qcs615-sndcard" , "qcs615" }, |
| 170 | {.compatible = "qcom,qcs6490-rb3gen2-sndcard" , "qcs6490" }, |
| 171 | {.compatible = "qcom,qcs8275-sndcard" , "qcs8300" }, |
| 172 | {.compatible = "qcom,qcs9075-sndcard" , "sa8775p" }, |
| 173 | {.compatible = "qcom,qcs9100-sndcard" , "sa8775p" }, |
| 174 | {.compatible = "qcom,sc8280xp-sndcard" , "sc8280xp" }, |
| 175 | {.compatible = "qcom,sm8450-sndcard" , "sm8450" }, |
| 176 | {.compatible = "qcom,sm8550-sndcard" , "sm8550" }, |
| 177 | {.compatible = "qcom,sm8650-sndcard" , "sm8650" }, |
| 178 | {.compatible = "qcom,sm8750-sndcard" , "sm8750" }, |
| 179 | {} |
| 180 | }; |
| 181 | |
| 182 | MODULE_DEVICE_TABLE(of, snd_sc8280xp_dt_match); |
| 183 | |
| 184 | static struct platform_driver snd_sc8280xp_driver = { |
| 185 | .probe = sc8280xp_platform_probe, |
| 186 | .driver = { |
| 187 | .name = "snd-sc8280xp" , |
| 188 | .of_match_table = snd_sc8280xp_dt_match, |
| 189 | }, |
| 190 | }; |
| 191 | module_platform_driver(snd_sc8280xp_driver); |
| 192 | MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org" ); |
| 193 | MODULE_DESCRIPTION("SC8280XP ASoC Machine Driver" ); |
| 194 | MODULE_LICENSE("GPL" ); |
| 195 | |