blob: 5349598114222c77c8abd0b973b033f2ff1ca243 [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Xiaojun Sang53cd13a2018-06-29 15:14:37 +08002/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
Asish Bhattacharya366f7502017-07-25 15:15:56 +05303 */
4#include <linux/platform_device.h>
5#include <linux/slab.h>
6#include <linux/module.h>
7#include <linux/of_device.h>
8#include <linux/err.h>
9#include <sound/core.h>
10#include <sound/pcm.h>
11#include <sound/soc.h>
12#include <linux/msm_ext_display.h>
13
Meng Wang15c825d2018-09-06 10:49:18 +080014#define DRV_NAME "HDMI_codec"
15
Asish Bhattacharya366f7502017-07-25 15:15:56 +053016#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000
17#define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT)
18#define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT)
19#define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE)
20
Karthikeyan Manifc1e9722018-05-03 18:38:56 -070021#define SOC_EXT_DISP_AUDIO_TYPE(index) \
22 static SOC_ENUM_SINGLE_DECL(ext_disp_audio_type##index, SND_SOC_NOPM, \
23 index, ext_disp_audio_type_text)
24#define SOC_EXT_DISP_AUDIO_ACK_STATE(index) \
25 static SOC_ENUM_SINGLE_DECL(ext_disp_audio_ack_state##index, \
26 SND_SOC_NOPM, index, ext_disp_audio_ack_text)
27
28#define SWITCH_DP_CODEC(codec_info, codec_data, dai_id) \
29 codec_info.type = EXT_DISPLAY_TYPE_DP; \
30 codec_info.ctrl_id = codec_data->ctl[dai_id]; \
31 codec_info.stream_id = codec_data->stream[dai_id]; \
32 msm_ext_disp_select_audio_codec(codec_data->ext_disp_core_pdev, \
33 &codec_info)
34
35enum {
36 DP_STREAM0 = 0,
37 DP_STREAM1,
38 DP_STREAM_MAX,
39};
40
41enum {
42 DP_DAI1 = 0,
43 DP_DAI2,
44 HDMI_DAI,
45 DP_DAI_MAX,
46};
47
Asish Bhattacharya366f7502017-07-25 15:15:56 +053048static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
49static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect",
50 "Ack_Enable"};
51
Karthikeyan Manifc1e9722018-05-03 18:38:56 -070052SOC_EXT_DISP_AUDIO_TYPE(0);
53SOC_EXT_DISP_AUDIO_ACK_STATE(0);
54SOC_EXT_DISP_AUDIO_TYPE(1);
55SOC_EXT_DISP_AUDIO_ACK_STATE(1);
Asish Bhattacharya366f7502017-07-25 15:15:56 +053056
57struct msm_ext_disp_audio_codec_rx_data {
58 struct platform_device *ext_disp_core_pdev;
59 struct msm_ext_disp_audio_codec_ops ext_disp_ops;
60 int cable_status;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -070061 struct mutex dp_ops_lock;
62 int stream[DP_DAI_MAX];
63 int ctl[DP_DAI_MAX];
Asish Bhattacharya366f7502017-07-25 15:15:56 +053064};
65
66static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
67 struct snd_ctl_elem_info *uinfo)
68{
Meng Wang15c825d2018-09-06 10:49:18 +080069 struct snd_soc_component *component =
70 snd_soc_kcontrol_component(kcontrol);
Asish Bhattacharya366f7502017-07-25 15:15:56 +053071 struct msm_ext_disp_audio_codec_rx_data *codec_data;
72 struct msm_ext_disp_audio_edid_blk edid_blk;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -070073 int rc = 0;
74 struct msm_ext_disp_codec_id codec_info;
75 int dai_id = kcontrol->private_value;
Asish Bhattacharya366f7502017-07-25 15:15:56 +053076
Meng Wang15c825d2018-09-06 10:49:18 +080077 codec_data = snd_soc_component_get_drvdata(component);
Asish Bhattacharya366f7502017-07-25 15:15:56 +053078
79 if (!codec_data) {
Meng Wang15c825d2018-09-06 10:49:18 +080080 dev_err(component->dev, "%s: codec_data is NULL\n", __func__);
Asish Bhattacharya366f7502017-07-25 15:15:56 +053081 return -EINVAL;
82 }
83
84 if (!codec_data->ext_disp_ops.get_audio_edid_blk) {
Meng Wang15c825d2018-09-06 10:49:18 +080085 dev_dbg(component->dev, "%s: get_audio_edid_blk() is NULL\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +053086 __func__);
87 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
88 uinfo->count = 0;
89 return 0;
90 }
91
Meng Wang15c825d2018-09-06 10:49:18 +080092 dev_dbg(component->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -070093 codec_data->ctl[dai_id], codec_data->stream[dai_id]);
94
95 mutex_lock(&codec_data->dp_ops_lock);
96 SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
Asish Bhattacharya366f7502017-07-25 15:15:56 +053097 rc = codec_data->ext_disp_ops.get_audio_edid_blk(
98 codec_data->ext_disp_core_pdev, &edid_blk);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -070099 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530100 if (rc >= 0) {
101 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
102 uinfo->count = edid_blk.audio_data_blk_size +
103 edid_blk.spk_alloc_data_blk_size;
104 }
105
Meng Wang15c825d2018-09-06 10:49:18 +0800106 dev_dbg(component->dev, "%s: count: %d\n", __func__, uinfo->count);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530107
108 return rc;
109}
110
111static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
112 struct snd_ctl_elem_value *ucontrol) {
Meng Wang15c825d2018-09-06 10:49:18 +0800113 struct snd_soc_component *component =
114 snd_soc_kcontrol_component(kcontrol);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530115 struct msm_ext_disp_audio_codec_rx_data *codec_data;
116 struct msm_ext_disp_audio_edid_blk edid_blk;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700117 struct msm_ext_disp_codec_id codec_info;
118 int rc = 0;
119 int dai_id = kcontrol->private_value;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530120
Meng Wang15c825d2018-09-06 10:49:18 +0800121 codec_data = snd_soc_component_get_drvdata(component);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530122 if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) {
Meng Wang15c825d2018-09-06 10:49:18 +0800123 dev_err(component->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530124 __func__);
125 return -EINVAL;
126 }
127
Meng Wang15c825d2018-09-06 10:49:18 +0800128 dev_dbg(component->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700129 codec_data->ctl[dai_id], codec_data->stream[dai_id]);
130
131 mutex_lock(&codec_data->dp_ops_lock);
132 SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530133 rc = codec_data->ext_disp_ops.get_audio_edid_blk(
134 codec_data->ext_disp_core_pdev, &edid_blk);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700135 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530136 if (rc >= 0) {
137 if (sizeof(ucontrol->value.bytes.data) <
138 (edid_blk.audio_data_blk_size +
139 edid_blk.spk_alloc_data_blk_size)) {
Meng Wang15c825d2018-09-06 10:49:18 +0800140 dev_err(component->dev,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530141 "%s: Not enough memory to copy EDID data\n",
142 __func__);
143 return -ENOMEM;
144 }
145
146 memcpy(ucontrol->value.bytes.data,
147 edid_blk.audio_data_blk,
148 edid_blk.audio_data_blk_size);
149 memcpy((ucontrol->value.bytes.data +
150 edid_blk.audio_data_blk_size),
151 edid_blk.spk_alloc_data_blk,
152 edid_blk.spk_alloc_data_blk_size);
153
Meng Wang15c825d2018-09-06 10:49:18 +0800154 dev_dbg(component->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530155 __func__, edid_blk.audio_data_blk_size,
156 edid_blk.spk_alloc_data_blk_size);
157 }
158
159 return rc;
160}
161
162static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
163 struct snd_ctl_elem_value *ucontrol)
164{
Meng Wang15c825d2018-09-06 10:49:18 +0800165 struct snd_soc_component *component =
166 snd_soc_kcontrol_component(kcontrol);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530167 struct msm_ext_disp_audio_codec_rx_data *codec_data;
168 enum msm_ext_disp_cable_state cable_state;
169 enum msm_ext_disp_type disp_type;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700170 struct msm_ext_disp_codec_id codec_info;
171 int rc = 0;
172 int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530173
Meng Wang15c825d2018-09-06 10:49:18 +0800174 codec_data = snd_soc_component_get_drvdata(component);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530175 if (!codec_data ||
176 !codec_data->ext_disp_ops.get_audio_edid_blk ||
177 !codec_data->ext_disp_ops.get_intf_id) {
Meng Wang15c825d2018-09-06 10:49:18 +0800178 dev_err(component->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530179 __func__);
180 return -EINVAL;
181 }
182
Meng Wang15c825d2018-09-06 10:49:18 +0800183 dev_dbg(component->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700184 codec_data->ctl[dai_id], codec_data->stream[dai_id]);
185
186 mutex_lock(&codec_data->dp_ops_lock);
187 SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530188 cable_state = codec_data->ext_disp_ops.cable_status(
189 codec_data->ext_disp_core_pdev, 1);
190 if (cable_state < 0) {
Meng Wang15c825d2018-09-06 10:49:18 +0800191 dev_err(component->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530192 __func__, cable_state);
193 rc = cable_state;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700194 goto cable_err;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530195 }
196
197 codec_data->cable_status = cable_state;
198 if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) {
Meng Wang15c825d2018-09-06 10:49:18 +0800199 dev_err(component->dev, "%s: Display cable disconnected\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530200 __func__);
201 ucontrol->value.integer.value[0] = 0;
202 rc = 0;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700203 goto cable_err;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530204 }
205
206 disp_type = codec_data->ext_disp_ops.get_intf_id(
207 codec_data->ext_disp_core_pdev);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700208 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530209 if (disp_type >= 0) {
210 switch (disp_type) {
211 case EXT_DISPLAY_TYPE_DP:
212 ucontrol->value.integer.value[0] = 2;
213 rc = 0;
214 break;
215 case EXT_DISPLAY_TYPE_HDMI:
216 ucontrol->value.integer.value[0] = 1;
217 rc = 0;
218 break;
219 default:
220 rc = -EINVAL;
Meng Wang15c825d2018-09-06 10:49:18 +0800221 dev_err(component->dev, "%s: Invalid disp_type:%d\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530222 __func__, disp_type);
223 goto done;
224 }
Meng Wang15c825d2018-09-06 10:49:18 +0800225 dev_dbg(component->dev, "%s: Display type: %d\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530226 __func__, disp_type);
227 } else {
Meng Wang15c825d2018-09-06 10:49:18 +0800228 dev_err(component->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530229 __func__, disp_type);
230 rc = disp_type;
231 }
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700232 return rc;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530233
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700234cable_err:
235 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530236done:
237 return rc;
238}
239
240static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol,
241 struct snd_ctl_elem_value *ucontrol)
242{
Meng Wang15c825d2018-09-06 10:49:18 +0800243 struct snd_soc_component *component =
244 snd_soc_kcontrol_component(kcontrol);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530245 struct msm_ext_disp_audio_codec_rx_data *codec_data;
246 u32 ack_state = 0;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700247 struct msm_ext_disp_codec_id codec_info;
248 int rc = 0;
249 int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530250
Meng Wang15c825d2018-09-06 10:49:18 +0800251 codec_data = snd_soc_component_get_drvdata(component);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530252 if (!codec_data ||
253 !codec_data->ext_disp_ops.acknowledge) {
Meng Wang15c825d2018-09-06 10:49:18 +0800254 dev_err(component->dev,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530255 "%s: codec_data or ops acknowledge() is NULL\n",
256 __func__);
257 rc = -EINVAL;
258 goto done;
259 }
260
Meng Wang15c825d2018-09-06 10:49:18 +0800261 dev_dbg(component->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700262 codec_data->ctl[dai_id], codec_data->stream[dai_id]);
263
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530264 switch (ucontrol->value.enumerated.item[0]) {
265 case 0:
266 ack_state = AUD_EXT_DISP_ACK_DISCONNECT;
267 break;
268 case 1:
269 ack_state = AUD_EXT_DISP_ACK_CONNECT;
270 break;
271 case 2:
272 ack_state = AUD_EXT_DISP_ACK_ENABLE;
273 break;
274 default:
275 rc = -EINVAL;
Meng Wang15c825d2018-09-06 10:49:18 +0800276 dev_err(component->dev,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530277 "%s: invalid value %d for mixer ctl\n",
278 __func__, ucontrol->value.enumerated.item[0]);
279 goto done;
280 }
Meng Wang15c825d2018-09-06 10:49:18 +0800281 dev_dbg(component->dev, "%s: control %d, ack set value 0x%x\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530282 __func__, ucontrol->value.enumerated.item[0], ack_state);
283
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700284 mutex_lock(&codec_data->dp_ops_lock);
285 SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530286 rc = codec_data->ext_disp_ops.acknowledge(
287 codec_data->ext_disp_core_pdev, ack_state);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700288 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530289 if (rc < 0) {
Meng Wang15c825d2018-09-06 10:49:18 +0800290 dev_err(component->dev, "%s: error from acknowledge(), err:%d\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530291 __func__, rc);
292 }
293
294done:
295 return rc;
296}
297
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700298static int msm_ext_disp_audio_device_set(struct snd_kcontrol *kcontrol,
299 struct snd_ctl_elem_value *ucontrol)
300{
Meng Wang15c825d2018-09-06 10:49:18 +0800301 struct snd_soc_component *component =
302 snd_soc_kcontrol_component(kcontrol);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700303 struct msm_ext_disp_audio_codec_rx_data *codec_data;
304 int rc = 0;
305 int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l;
306
Meng Wang15c825d2018-09-06 10:49:18 +0800307 codec_data = snd_soc_component_get_drvdata(component);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700308 if (!codec_data) {
Meng Wang15c825d2018-09-06 10:49:18 +0800309 dev_err(component->dev,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700310 "%s: codec_data or ops acknowledge() is NULL\n",
311 __func__);
312 rc = -EINVAL;
313 goto done;
314 }
315
316 mutex_lock(&codec_data->dp_ops_lock);
317 codec_data->ctl[dai_id] = ucontrol->value.enumerated.item[0];
318 codec_data->stream[dai_id] = ucontrol->value.enumerated.item[1];
319 mutex_unlock(&codec_data->dp_ops_lock);
320
321done:
322 return rc;
323}
324
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530325static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
326 {
327 .access = SNDRV_CTL_ELEM_ACCESS_READ |
328 SNDRV_CTL_ELEM_ACCESS_VOLATILE,
329 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
330 .name = "HDMI EDID",
331 .info = msm_ext_disp_edid_ctl_info,
332 .get = msm_ext_disp_edid_get,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700333 .private_value = HDMI_DAI,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530334 },
335 {
336 .access = SNDRV_CTL_ELEM_ACCESS_READ |
337 SNDRV_CTL_ELEM_ACCESS_VOLATILE,
338 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
339 .name = "Display Port EDID",
340 .info = msm_ext_disp_edid_ctl_info,
341 .get = msm_ext_disp_edid_get,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700342 .private_value = DP_DAI1,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530343 },
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700344 {
345 .access = SNDRV_CTL_ELEM_ACCESS_READ |
346 SNDRV_CTL_ELEM_ACCESS_VOLATILE,
347 .iface = SNDRV_CTL_ELEM_IFACE_PCM,
348 .name = "Display Port1 EDID",
349 .info = msm_ext_disp_edid_ctl_info,
350 .get = msm_ext_disp_edid_get,
351 .private_value = DP_DAI2,
352 },
353 SOC_ENUM_EXT("External Display Type",
354 ext_disp_audio_type0,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530355 msm_ext_disp_audio_type_get, NULL),
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700356 SOC_ENUM_EXT("External Display1 Type",
357 ext_disp_audio_type1,
358 msm_ext_disp_audio_type_get, NULL),
359 SOC_ENUM_EXT("External Display Audio Ack",
360 ext_disp_audio_ack_state0,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530361 NULL, msm_ext_disp_audio_ack_set),
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700362 SOC_ENUM_EXT("External Display1 Audio Ack",
363 ext_disp_audio_ack_state1,
364 NULL, msm_ext_disp_audio_ack_set),
365
366 SOC_SINGLE_EXT("External Display Audio Device",
367 SND_SOC_NOPM, DP_DAI1, DP_STREAM_MAX, 0,
368 NULL, msm_ext_disp_audio_device_set),
369 SOC_SINGLE_EXT("External Display1 Audio Device",
370 SND_SOC_NOPM, DP_DAI2, DP_STREAM_MAX, 0,
371 NULL, msm_ext_disp_audio_device_set),
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530372};
373
374static int msm_ext_disp_audio_codec_rx_dai_startup(
375 struct snd_pcm_substream *substream,
376 struct snd_soc_dai *dai)
377{
378 int ret = 0;
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700379 struct msm_ext_disp_codec_id codec_info;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530380 struct msm_ext_disp_audio_codec_rx_data *codec_data =
Meng Wang15c825d2018-09-06 10:49:18 +0800381 dev_get_drvdata(dai->component->dev);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530382
383 if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
384 dev_err(dai->dev, "%s() codec_data or cable_status is null\n",
385 __func__);
386 return -EINVAL;
387 }
388
Meng Wang15c825d2018-09-06 10:49:18 +0800389 dev_dbg(dai->component->dev, "%s: DP ctl id %d Stream id %d\n",
390 __func__,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700391 codec_data->ctl[dai->id], codec_data->stream[dai->id]);
392
393 mutex_lock(&codec_data->dp_ops_lock);
394 SWITCH_DP_CODEC(codec_info, codec_data, dai->id);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530395 codec_data->cable_status =
396 codec_data->ext_disp_ops.cable_status(
397 codec_data->ext_disp_core_pdev, 1);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700398 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530399 if (codec_data->cable_status < 0) {
400 dev_err(dai->dev,
401 "%s() ext disp core is not ready (ret val = %d)\n",
402 __func__, codec_data->cable_status);
403 ret = codec_data->cable_status;
404 } else if (!codec_data->cable_status) {
405 dev_err(dai->dev,
406 "%s() ext disp cable is not connected (ret val = %d)\n",
407 __func__, codec_data->cable_status);
408 ret = -ENODEV;
409 }
410
411 return ret;
412}
413
414static int msm_ext_disp_audio_codec_rx_dai_hw_params(
415 struct snd_pcm_substream *substream,
416 struct snd_pcm_hw_params *params,
417 struct snd_soc_dai *dai)
418{
419 u32 channel_allocation = 0;
420 u32 level_shift = 0; /* 0dB */
421 bool down_mix = 0;
422 u32 num_channels = params_channels(params);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700423 struct msm_ext_disp_codec_id codec_info;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530424 int rc = 0;
425 struct msm_ext_disp_audio_setup_params audio_setup_params = {0};
426
427 struct msm_ext_disp_audio_codec_rx_data *codec_data =
Meng Wang15c825d2018-09-06 10:49:18 +0800428 dev_get_drvdata(dai->component->dev);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530429
430 if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) {
431 dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n",
432 __func__);
433 return -EINVAL;
434 }
435
Meng Wang15c825d2018-09-06 10:49:18 +0800436 dev_dbg(dai->component->dev, "%s: DP ctl id %d Stream id %d\n",
437 __func__,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700438 codec_data->ctl[dai->id], codec_data->stream[dai->id]);
439
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530440 if (codec_data->cable_status < 0) {
441 dev_err_ratelimited(dai->dev,
442 "%s() ext disp core is not ready (ret val = %d)\n",
443 __func__, codec_data->cable_status);
444 return codec_data->cable_status;
445 } else if (!codec_data->cable_status) {
446 dev_err_ratelimited(dai->dev,
447 "%s() ext disp cable is not connected (ret val = %d)\n",
448 __func__, codec_data->cable_status);
449 return -ENODEV;
450 }
451
452 /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
453 switch (num_channels) {
454 case 2:
455 channel_allocation = 0;
456 break;
457 case 3:
458 channel_allocation = 0x02;/*default to FL/FR/FC*/
459 audio_setup_params.sample_present = 0x3;
460 break;
461 case 4:
462 channel_allocation = 0x06;/*default to FL/FR/FC/RC*/
463 audio_setup_params.sample_present = 0x7;
464 break;
465 case 5:
466 channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/
467 audio_setup_params.sample_present = 0x7;
468 break;
469 case 6:
470 channel_allocation = 0x0B;
471 audio_setup_params.sample_present = 0x7;
472 break;
473 case 7:
474 channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/
475 audio_setup_params.sample_present = 0xf;
476 break;
477 case 8:
478 channel_allocation = 0x13;
479 audio_setup_params.sample_present = 0xf;
480 break;
481 default:
482 dev_err(dai->dev, "invalid Channels = %u\n", num_channels);
483 return -EINVAL;
484 }
485
486 dev_dbg(dai->dev,
487 "%s() num_ch %u samplerate %u channel_allocation = %u\n",
488 __func__, num_channels, params_rate(params),
489 channel_allocation);
490
491 audio_setup_params.sample_rate_hz = params_rate(params);
492 audio_setup_params.num_of_channels = num_channels;
493 audio_setup_params.channel_allocation = channel_allocation;
494 audio_setup_params.level_shift = level_shift;
495 audio_setup_params.down_mix = down_mix;
496
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700497 mutex_lock(&codec_data->dp_ops_lock);
498 SWITCH_DP_CODEC(codec_info, codec_data, dai->id);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530499 rc = codec_data->ext_disp_ops.audio_info_setup(
500 codec_data->ext_disp_core_pdev, &audio_setup_params);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700501 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530502 if (rc < 0) {
503 dev_err_ratelimited(dai->dev,
504 "%s() ext disp core is not ready, rc: %d\n",
505 __func__, rc);
506 }
507
508 return rc;
509}
510
511static void msm_ext_disp_audio_codec_rx_dai_shutdown(
512 struct snd_pcm_substream *substream,
513 struct snd_soc_dai *dai)
514{
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700515 int rc = 0;
516 struct msm_ext_disp_codec_id codec_info;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530517
518 struct msm_ext_disp_audio_codec_rx_data *codec_data =
Meng Wang15c825d2018-09-06 10:49:18 +0800519 dev_get_drvdata(dai->component->dev);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530520
521 if (!codec_data || !codec_data->ext_disp_ops.teardown_done ||
522 !codec_data->ext_disp_ops.cable_status) {
523 dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n",
524 __func__);
525 return;
526 }
527
Meng Wang15c825d2018-09-06 10:49:18 +0800528 dev_dbg(dai->component->dev, "%s: DP ctl id %d Stream id %d\n",
529 __func__,
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700530 codec_data->ctl[dai->id], codec_data->stream[dai->id]);
531
532 mutex_lock(&codec_data->dp_ops_lock);
533 SWITCH_DP_CODEC(codec_info, codec_data, dai->id);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530534 rc = codec_data->ext_disp_ops.cable_status(
535 codec_data->ext_disp_core_pdev, 0);
536 if (rc < 0) {
537 dev_err(dai->dev,
538 "%s: ext disp core had problems releasing audio flag\n",
539 __func__);
540 }
541
542 codec_data->ext_disp_ops.teardown_done(
543 codec_data->ext_disp_core_pdev);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700544 mutex_unlock(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530545}
546
Meng Wang15c825d2018-09-06 10:49:18 +0800547static int msm_ext_disp_audio_codec_rx_probe(
548 struct snd_soc_component *component)
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530549{
550 struct msm_ext_disp_audio_codec_rx_data *codec_data;
551 struct device_node *of_node_parent = NULL;
552
553 codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data),
554 GFP_KERNEL);
555
556 if (!codec_data) {
Meng Wang15c825d2018-09-06 10:49:18 +0800557 dev_err(component->dev, "%s(): fail to allocate dai data\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530558 __func__);
559 return -ENOMEM;
560 }
561
Meng Wang15c825d2018-09-06 10:49:18 +0800562 of_node_parent = of_get_parent(component->dev->of_node);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530563 if (!of_node_parent) {
Meng Wang15c825d2018-09-06 10:49:18 +0800564 dev_err(component->dev, "%s(): Parent device tree node not found\n",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530565 __func__);
566 kfree(codec_data);
567 return -ENODEV;
568 }
569
570 codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent);
571 if (!codec_data->ext_disp_core_pdev) {
Meng Wang15c825d2018-09-06 10:49:18 +0800572 dev_err(component->dev, "%s(): can't get parent pdev\n",
573 __func__);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530574 kfree(codec_data);
575 return -ENODEV;
576 }
577
578 if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev,
579 &codec_data->ext_disp_ops)) {
Meng Wang15c825d2018-09-06 10:49:18 +0800580 dev_err(component->dev, "%s(): can't register with ext disp core",
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530581 __func__);
582 kfree(codec_data);
583 return -ENODEV;
584 }
585
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700586 mutex_init(&codec_data->dp_ops_lock);
Meng Wang15c825d2018-09-06 10:49:18 +0800587 dev_set_drvdata(component->dev, codec_data);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530588
Meng Wang15c825d2018-09-06 10:49:18 +0800589 dev_dbg(component->dev, "%s(): registered %s with ext disp core\n",
590 __func__, component->name);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530591
592 return 0;
593}
594
Meng Wang15c825d2018-09-06 10:49:18 +0800595static void msm_ext_disp_audio_codec_rx_remove(
596 struct snd_soc_component *component)
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530597{
598 struct msm_ext_disp_audio_codec_rx_data *codec_data;
599
Meng Wang15c825d2018-09-06 10:49:18 +0800600 codec_data = dev_get_drvdata(component->dev);
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700601 mutex_destroy(&codec_data->dp_ops_lock);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530602 kfree(codec_data);
603
Meng Wang15c825d2018-09-06 10:49:18 +0800604 return;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530605}
606
607static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = {
608 .startup = msm_ext_disp_audio_codec_rx_dai_startup,
609 .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params,
610 .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown
611};
612
613static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = {
614 {
615 .name = "msm_hdmi_audio_codec_rx_dai",
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700616 .id = HDMI_DAI,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530617 .playback = {
618 .stream_name = "HDMI Playback",
619 .channels_min = 1,
620 .channels_max = 8,
621 .rate_min = 48000,
622 .rate_max = 48000,
623 .rates = MSM_EXT_DISP_PCM_RATES,
624 .formats = SNDRV_PCM_FMTBIT_S16_LE,
625 },
626 .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
627 },
628 {
629 .name = "msm_dp_audio_codec_rx_dai",
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700630 .id = DP_DAI1,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530631 .playback = {
632 .stream_name = "Display Port Playback",
633 .channels_min = 1,
634 .channels_max = 8,
635 .rate_min = 48000,
636 .rate_max = 192000,
637 .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700638 SNDRV_PCM_RATE_192000,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530639 .formats = SNDRV_PCM_FMTBIT_S16_LE |
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700640 SNDRV_PCM_FMTBIT_S24_LE |
641 SNDRV_PCM_FMTBIT_S24_3LE,
642 },
643 .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
644 },
645 {
646 .name = "msm_dp_audio_codec_rx1_dai",
647 .id = DP_DAI2,
648 .playback = {
649 .stream_name = "Display Port1 Playback",
650 .channels_min = 1,
651 .channels_max = 8,
652 .rate_min = 48000,
653 .rate_max = 192000,
654 .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
655 SNDRV_PCM_RATE_192000,
656 .formats = SNDRV_PCM_FMTBIT_S16_LE |
657 SNDRV_PCM_FMTBIT_S24_LE |
658 SNDRV_PCM_FMTBIT_S24_3LE,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530659 },
660 .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
661 },
662};
663
Meng Wang15c825d2018-09-06 10:49:18 +0800664static const struct snd_soc_component_driver msm_ext_disp_codec_rx_driver = {
665 .name = DRV_NAME,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530666 .probe = msm_ext_disp_audio_codec_rx_probe,
667 .remove = msm_ext_disp_audio_codec_rx_remove,
Meng Wang15c825d2018-09-06 10:49:18 +0800668 .controls = msm_ext_disp_codec_rx_controls,
669 .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls),
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530670};
671
672static int msm_ext_disp_audio_codec_rx_plat_probe(
673 struct platform_device *pdev)
674{
675 dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__,
676 dev_name(&pdev->dev));
677
Meng Wang15c825d2018-09-06 10:49:18 +0800678 return snd_soc_register_component(&pdev->dev,
679 &msm_ext_disp_codec_rx_driver,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530680 msm_ext_disp_audio_codec_rx_dais,
681 ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais));
682}
683
684static int msm_ext_disp_audio_codec_rx_plat_remove(
685 struct platform_device *pdev)
686{
Meng Wang15c825d2018-09-06 10:49:18 +0800687 snd_soc_unregister_component(&pdev->dev);
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530688 return 0;
689}
690static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = {
691 { .compatible = "qcom,msm-ext-disp-audio-codec-rx", },
692 {}
693};
694MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match);
695
696static struct platform_driver msm_ext_disp_audio_codec_rx_driver = {
697 .driver = {
698 .name = "msm-ext-disp-audio-codec-rx",
699 .owner = THIS_MODULE,
700 .of_match_table = msm_ext_disp_audio_codec_rx_dt_match,
Xiaojun Sang53cd13a2018-06-29 15:14:37 +0800701 .suppress_bind_attrs = true,
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530702 },
703 .probe = msm_ext_disp_audio_codec_rx_plat_probe,
704 .remove = msm_ext_disp_audio_codec_rx_plat_remove,
705};
706
707static int __init msm_ext_disp_audio_codec_rx_init(void)
708{
Karthikeyan Manifc1e9722018-05-03 18:38:56 -0700709 int rc = 0;
Asish Bhattacharya366f7502017-07-25 15:15:56 +0530710
711 rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver);
712 if (rc) {
713 pr_err("%s: failed to register ext disp codec driver err:%d\n",
714 __func__, rc);
715 }
716
717 return rc;
718}
719module_init(msm_ext_disp_audio_codec_rx_init);
720
721static void __exit msm_ext_disp_audio_codec_rx_exit(void)
722{
723 platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver);
724}
725module_exit(msm_ext_disp_audio_codec_rx_exit);
726
727MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver");
728MODULE_LICENSE("GPL v2");