xref: /openbmc/linux/sound/soc/qcom/lpass-hdmi.c (revision 738f6ba1)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2020 The Linux Foundation. All rights reserved.
4  *
5  * lpass-hdmi.c -- ALSA SoC HDMI-CPU DAI driver for QTi LPASS HDMI
6  */
7 
8 
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <sound/pcm_params.h>
12 #include <linux/regmap.h>
13 #include <sound/soc.h>
14 #include <sound/soc-dai.h>
15 #include <dt-bindings/sound/sc7180-lpass.h>
16 #include "lpass-lpaif-reg.h"
17 #include "lpass.h"
18 
19 static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream,
20 		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
21 {
22 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
23 	snd_pcm_format_t format = params_format(params);
24 	unsigned int rate = params_rate(params);
25 	unsigned int channels = params_channels(params);
26 	unsigned int ret;
27 	int bitwidth;
28 	unsigned int word_length;
29 	unsigned int ch_sts_buf0;
30 	unsigned int ch_sts_buf1;
31 	unsigned int data_format;
32 	unsigned int sampling_freq;
33 	unsigned int ch = 0;
34 	struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
35 	struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
36 
37 	bitwidth = snd_pcm_format_width(format);
38 	if (bitwidth < 0) {
39 		dev_err(dai->dev, "%s invalid bit width given : %d\n",
40 					__func__, bitwidth);
41 		return bitwidth;
42 	}
43 
44 	switch (bitwidth) {
45 	case 16:
46 		word_length = LPASS_DP_AUDIO_BITWIDTH16;
47 		break;
48 	case 24:
49 		word_length = LPASS_DP_AUDIO_BITWIDTH24;
50 		break;
51 	default:
52 		dev_err(dai->dev, "%s invalid bit width given : %d\n",
53 					__func__, bitwidth);
54 		return -EINVAL;
55 	}
56 
57 	switch (rate) {
58 	case 32000:
59 		sampling_freq = LPASS_SAMPLING_FREQ32;
60 		break;
61 	case 44100:
62 		sampling_freq = LPASS_SAMPLING_FREQ44;
63 		break;
64 	case 48000:
65 		sampling_freq = LPASS_SAMPLING_FREQ48;
66 		break;
67 	default:
68 		dev_err(dai->dev, "%s invalid bit width given : %d\n",
69 					__func__, bitwidth);
70 		return -EINVAL;
71 	}
72 	data_format = LPASS_DATA_FORMAT_LINEAR;
73 	ch_sts_buf0 = (((data_format << LPASS_DATA_FORMAT_SHIFT) & LPASS_DATA_FORMAT_MASK)
74 				| ((sampling_freq << LPASS_FREQ_BIT_SHIFT) & LPASS_FREQ_BIT_MASK));
75 	ch_sts_buf1 = (word_length) & LPASS_WORDLENGTH_MASK;
76 
77 	ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_RESET);
78 	if (ret)
79 		return ret;
80 
81 	ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_CLEAR);
82 	if (ret)
83 		return ret;
84 
85 	ret = regmap_field_write(drvdata->hdmitx_legacy_en, LPASS_HDMITX_LEGACY_DISABLE);
86 	if (ret)
87 		return ret;
88 
89 	ret = regmap_field_write(drvdata->hdmitx_parity_calc_en, HDMITX_PARITY_CALC_EN);
90 	if (ret)
91 		return ret;
92 
93 	ret = regmap_field_write(drvdata->vbit_ctl->replace_vbit, REPLACE_VBIT);
94 	if (ret)
95 		return ret;
96 
97 	ret = regmap_field_write(drvdata->vbit_ctl->vbit_stream, LINEAR_PCM_DATA);
98 	if (ret)
99 		return ret;
100 
101 	ret = regmap_field_write(drvdata->hdmitx_ch_msb[0], ch_sts_buf1);
102 	if (ret)
103 		return ret;
104 
105 	ret = regmap_field_write(drvdata->hdmitx_ch_lsb[0], ch_sts_buf0);
106 	if (ret)
107 		return ret;
108 
109 	ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_chs, HW_MODE);
110 	if (ret)
111 		return ret;
112 
113 	ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_chs_sel, SW_MODE);
114 	if (ret)
115 		return ret;
116 
117 	ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_usr, HW_MODE);
118 	if (ret)
119 		return ret;
120 
121 	ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_usr_sel, SW_MODE);
122 	if (ret)
123 		return ret;
124 
125 	ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE);
126 	if (ret)
127 		return ret;
128 
129 	ret = regmap_field_write(meta_ctl->as_sdp_cc, channels - 1);
130 	if (ret)
131 		return ret;
132 
133 	ret = regmap_field_write(meta_ctl->as_sdp_ct, LPASS_META_DEFAULT_VAL);
134 	if (ret)
135 		return ret;
136 
137 	ret = regmap_field_write(meta_ctl->aif_db4, LPASS_META_DEFAULT_VAL);
138 	if (ret)
139 		return ret;
140 
141 	ret = regmap_field_write(meta_ctl->frequency, sampling_freq);
142 	if (ret)
143 		return ret;
144 
145 	ret = regmap_field_write(meta_ctl->mst_index, LPASS_META_DEFAULT_VAL);
146 	if (ret)
147 		return ret;
148 
149 	ret = regmap_field_write(meta_ctl->dptx_index, LPASS_META_DEFAULT_VAL);
150 	if (ret)
151 		return ret;
152 
153 	ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE);
154 	if (ret)
155 		return ret;
156 
157 	ret = regmap_field_write(sstream_ctl->dma_sel, ch);
158 	if (ret)
159 		return ret;
160 
161 	ret = regmap_field_write(sstream_ctl->auto_bbit_en, LPASS_SSTREAM_DEFAULT_ENABLE);
162 	if (ret)
163 		return ret;
164 
165 	ret = regmap_field_write(sstream_ctl->layout, LPASS_SSTREAM_DEFAULT_DISABLE);
166 	if (ret)
167 		return ret;
168 
169 	ret = regmap_field_write(sstream_ctl->layout_sp, LPASS_LAYOUT_SP_DEFAULT);
170 	if (ret)
171 		return ret;
172 
173 	ret = regmap_field_write(sstream_ctl->dp_audio, LPASS_SSTREAM_DEFAULT_ENABLE);
174 	if (ret)
175 		return ret;
176 
177 	ret = regmap_field_write(sstream_ctl->set_sp_on_en, LPASS_SSTREAM_DEFAULT_ENABLE);
178 	if (ret)
179 		return ret;
180 
181 	ret = regmap_field_write(sstream_ctl->dp_sp_b_hw_en, LPASS_SSTREAM_DEFAULT_ENABLE);
182 	if (ret)
183 		return ret;
184 
185 	ret = regmap_field_write(sstream_ctl->dp_staffing_en, LPASS_SSTREAM_DEFAULT_ENABLE);
186 	if (ret)
187 		return ret;
188 
189 	return ret;
190 }
191 
192 static int lpass_hdmi_daiops_prepare(struct snd_pcm_substream *substream,
193 		struct snd_soc_dai *dai)
194 {
195 	int ret;
196 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
197 
198 	ret = regmap_field_write(drvdata->sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE);
199 	if (ret)
200 		return ret;
201 
202 	ret = regmap_field_write(drvdata->meta_ctl->mute, LPASS_MUTE_DISABLE);
203 	if (ret)
204 		return ret;
205 
206 	return ret;
207 }
208 
209 static int lpass_hdmi_daiops_trigger(struct snd_pcm_substream *substream,
210 		int cmd, struct snd_soc_dai *dai)
211 {
212 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
213 	struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl;
214 	struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl;
215 	int ret = -EINVAL;
216 
217 	switch (cmd) {
218 	case SNDRV_PCM_TRIGGER_START:
219 	case SNDRV_PCM_TRIGGER_RESUME:
220 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
221 		ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE);
222 		if (ret)
223 			return ret;
224 
225 		ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_DISABLE);
226 		if (ret)
227 			return ret;
228 
229 		break;
230 	case SNDRV_PCM_TRIGGER_STOP:
231 	case SNDRV_PCM_TRIGGER_SUSPEND:
232 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
233 		ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE);
234 		if (ret)
235 			return ret;
236 
237 		ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE);
238 		if (ret)
239 			return ret;
240 
241 		ret = regmap_field_write(sstream_ctl->dp_audio, 0);
242 		if (ret)
243 			return ret;
244 
245 		break;
246 	}
247 	return ret;
248 }
249 
250 const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = {
251 	.hw_params	= lpass_hdmi_daiops_hw_params,
252 	.prepare	= lpass_hdmi_daiops_prepare,
253 	.trigger	= lpass_hdmi_daiops_trigger,
254 };
255 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops);
256 
257 MODULE_DESCRIPTION("QTi LPASS HDMI Driver");
258 MODULE_LICENSE("GPL v2");
259