msm_hdmi_codec_rx.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #include <linux/platform_device.h>
  13. #include <linux/slab.h>
  14. #include <linux/module.h>
  15. #include <linux/of_device.h>
  16. #include <linux/err.h>
  17. #include <sound/core.h>
  18. #include <sound/pcm.h>
  19. #include <sound/soc.h>
  20. #include <linux/msm_ext_display.h>
  21. #define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000
  22. #define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT)
  23. #define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT)
  24. #define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE)
  25. #define SOC_EXT_DISP_AUDIO_TYPE(index) \
  26. static SOC_ENUM_SINGLE_DECL(ext_disp_audio_type##index, SND_SOC_NOPM, \
  27. index, ext_disp_audio_type_text)
  28. #define SOC_EXT_DISP_AUDIO_ACK_STATE(index) \
  29. static SOC_ENUM_SINGLE_DECL(ext_disp_audio_ack_state##index, \
  30. SND_SOC_NOPM, index, ext_disp_audio_ack_text)
  31. #define SWITCH_DP_CODEC(codec_info, codec_data, dai_id) \
  32. codec_info.type = EXT_DISPLAY_TYPE_DP; \
  33. codec_info.ctrl_id = codec_data->ctl[dai_id]; \
  34. codec_info.stream_id = codec_data->stream[dai_id]; \
  35. msm_ext_disp_select_audio_codec(codec_data->ext_disp_core_pdev, \
  36. &codec_info)
  37. enum {
  38. DP_STREAM0 = 0,
  39. DP_STREAM1,
  40. DP_STREAM_MAX,
  41. };
  42. enum {
  43. DP_DAI1 = 0,
  44. DP_DAI2,
  45. HDMI_DAI,
  46. DP_DAI_MAX,
  47. };
  48. static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
  49. static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect",
  50. "Ack_Enable"};
  51. SOC_EXT_DISP_AUDIO_TYPE(0);
  52. SOC_EXT_DISP_AUDIO_ACK_STATE(0);
  53. SOC_EXT_DISP_AUDIO_TYPE(1);
  54. SOC_EXT_DISP_AUDIO_ACK_STATE(1);
  55. struct msm_ext_disp_audio_codec_rx_data {
  56. struct platform_device *ext_disp_core_pdev;
  57. struct msm_ext_disp_audio_codec_ops ext_disp_ops;
  58. int cable_status;
  59. struct mutex dp_ops_lock;
  60. int stream[DP_DAI_MAX];
  61. int ctl[DP_DAI_MAX];
  62. };
  63. static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
  64. struct snd_ctl_elem_info *uinfo)
  65. {
  66. struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
  67. struct msm_ext_disp_audio_codec_rx_data *codec_data;
  68. struct msm_ext_disp_audio_edid_blk edid_blk;
  69. int rc = 0;
  70. struct msm_ext_disp_codec_id codec_info;
  71. int dai_id = kcontrol->private_value;
  72. codec_data = snd_soc_codec_get_drvdata(codec);
  73. if (!codec_data) {
  74. dev_err(codec->dev, "%s: codec_data is NULL\n", __func__);
  75. return -EINVAL;
  76. }
  77. if (!codec_data->ext_disp_ops.get_audio_edid_blk) {
  78. dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n",
  79. __func__);
  80. uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
  81. uinfo->count = 0;
  82. return 0;
  83. }
  84. dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
  85. codec_data->ctl[dai_id], codec_data->stream[dai_id]);
  86. mutex_lock(&codec_data->dp_ops_lock);
  87. SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
  88. rc = codec_data->ext_disp_ops.get_audio_edid_blk(
  89. codec_data->ext_disp_core_pdev, &edid_blk);
  90. mutex_unlock(&codec_data->dp_ops_lock);
  91. if (rc >= 0) {
  92. uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
  93. uinfo->count = edid_blk.audio_data_blk_size +
  94. edid_blk.spk_alloc_data_blk_size;
  95. }
  96. dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count);
  97. return rc;
  98. }
  99. static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
  100. struct snd_ctl_elem_value *ucontrol) {
  101. struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
  102. struct msm_ext_disp_audio_codec_rx_data *codec_data;
  103. struct msm_ext_disp_audio_edid_blk edid_blk;
  104. struct msm_ext_disp_codec_id codec_info;
  105. int rc = 0;
  106. int dai_id = kcontrol->private_value;
  107. codec_data = snd_soc_codec_get_drvdata(codec);
  108. if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) {
  109. dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n",
  110. __func__);
  111. return -EINVAL;
  112. }
  113. dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
  114. codec_data->ctl[dai_id], codec_data->stream[dai_id]);
  115. mutex_lock(&codec_data->dp_ops_lock);
  116. SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
  117. rc = codec_data->ext_disp_ops.get_audio_edid_blk(
  118. codec_data->ext_disp_core_pdev, &edid_blk);
  119. mutex_unlock(&codec_data->dp_ops_lock);
  120. if (rc >= 0) {
  121. if (sizeof(ucontrol->value.bytes.data) <
  122. (edid_blk.audio_data_blk_size +
  123. edid_blk.spk_alloc_data_blk_size)) {
  124. dev_err(codec->dev,
  125. "%s: Not enough memory to copy EDID data\n",
  126. __func__);
  127. return -ENOMEM;
  128. }
  129. memcpy(ucontrol->value.bytes.data,
  130. edid_blk.audio_data_blk,
  131. edid_blk.audio_data_blk_size);
  132. memcpy((ucontrol->value.bytes.data +
  133. edid_blk.audio_data_blk_size),
  134. edid_blk.spk_alloc_data_blk,
  135. edid_blk.spk_alloc_data_blk_size);
  136. dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n",
  137. __func__, edid_blk.audio_data_blk_size,
  138. edid_blk.spk_alloc_data_blk_size);
  139. }
  140. return rc;
  141. }
  142. static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
  143. struct snd_ctl_elem_value *ucontrol)
  144. {
  145. struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
  146. struct msm_ext_disp_audio_codec_rx_data *codec_data;
  147. enum msm_ext_disp_cable_state cable_state;
  148. enum msm_ext_disp_type disp_type;
  149. struct msm_ext_disp_codec_id codec_info;
  150. int rc = 0;
  151. int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l;
  152. codec_data = snd_soc_codec_get_drvdata(codec);
  153. if (!codec_data ||
  154. !codec_data->ext_disp_ops.get_audio_edid_blk ||
  155. !codec_data->ext_disp_ops.get_intf_id) {
  156. dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n",
  157. __func__);
  158. return -EINVAL;
  159. }
  160. dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
  161. codec_data->ctl[dai_id], codec_data->stream[dai_id]);
  162. mutex_lock(&codec_data->dp_ops_lock);
  163. SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
  164. cable_state = codec_data->ext_disp_ops.cable_status(
  165. codec_data->ext_disp_core_pdev, 1);
  166. if (cable_state < 0) {
  167. dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n",
  168. __func__, cable_state);
  169. rc = cable_state;
  170. goto cable_err;
  171. }
  172. codec_data->cable_status = cable_state;
  173. if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) {
  174. dev_err(codec->dev, "%s: Display cable disconnected\n",
  175. __func__);
  176. ucontrol->value.integer.value[0] = 0;
  177. rc = 0;
  178. goto cable_err;
  179. }
  180. disp_type = codec_data->ext_disp_ops.get_intf_id(
  181. codec_data->ext_disp_core_pdev);
  182. mutex_unlock(&codec_data->dp_ops_lock);
  183. if (disp_type >= 0) {
  184. switch (disp_type) {
  185. case EXT_DISPLAY_TYPE_DP:
  186. ucontrol->value.integer.value[0] = 2;
  187. rc = 0;
  188. break;
  189. case EXT_DISPLAY_TYPE_HDMI:
  190. ucontrol->value.integer.value[0] = 1;
  191. rc = 0;
  192. break;
  193. default:
  194. rc = -EINVAL;
  195. dev_err(codec->dev, "%s: Invalid disp_type:%d\n",
  196. __func__, disp_type);
  197. goto done;
  198. }
  199. dev_dbg(codec->dev, "%s: Display type: %d\n",
  200. __func__, disp_type);
  201. } else {
  202. dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n",
  203. __func__, disp_type);
  204. rc = disp_type;
  205. }
  206. return rc;
  207. cable_err:
  208. mutex_unlock(&codec_data->dp_ops_lock);
  209. done:
  210. return rc;
  211. }
  212. static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol,
  213. struct snd_ctl_elem_value *ucontrol)
  214. {
  215. struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
  216. struct msm_ext_disp_audio_codec_rx_data *codec_data;
  217. u32 ack_state = 0;
  218. struct msm_ext_disp_codec_id codec_info;
  219. int rc = 0;
  220. int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l;
  221. codec_data = snd_soc_codec_get_drvdata(codec);
  222. if (!codec_data ||
  223. !codec_data->ext_disp_ops.acknowledge) {
  224. dev_err(codec->dev,
  225. "%s: codec_data or ops acknowledge() is NULL\n",
  226. __func__);
  227. rc = -EINVAL;
  228. goto done;
  229. }
  230. dev_dbg(codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
  231. codec_data->ctl[dai_id], codec_data->stream[dai_id]);
  232. switch (ucontrol->value.enumerated.item[0]) {
  233. case 0:
  234. ack_state = AUD_EXT_DISP_ACK_DISCONNECT;
  235. break;
  236. case 1:
  237. ack_state = AUD_EXT_DISP_ACK_CONNECT;
  238. break;
  239. case 2:
  240. ack_state = AUD_EXT_DISP_ACK_ENABLE;
  241. break;
  242. default:
  243. rc = -EINVAL;
  244. dev_err(codec->dev,
  245. "%s: invalid value %d for mixer ctl\n",
  246. __func__, ucontrol->value.enumerated.item[0]);
  247. goto done;
  248. }
  249. dev_dbg(codec->dev, "%s: control %d, ack set value 0x%x\n",
  250. __func__, ucontrol->value.enumerated.item[0], ack_state);
  251. mutex_lock(&codec_data->dp_ops_lock);
  252. SWITCH_DP_CODEC(codec_info, codec_data, dai_id);
  253. rc = codec_data->ext_disp_ops.acknowledge(
  254. codec_data->ext_disp_core_pdev, ack_state);
  255. mutex_unlock(&codec_data->dp_ops_lock);
  256. if (rc < 0) {
  257. dev_err(codec->dev, "%s: error from acknowledge(), err:%d\n",
  258. __func__, rc);
  259. }
  260. done:
  261. return rc;
  262. }
  263. static int msm_ext_disp_audio_device_set(struct snd_kcontrol *kcontrol,
  264. struct snd_ctl_elem_value *ucontrol)
  265. {
  266. struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
  267. struct msm_ext_disp_audio_codec_rx_data *codec_data;
  268. int rc = 0;
  269. int dai_id = ((struct soc_enum *) kcontrol->private_value)->shift_l;
  270. codec_data = snd_soc_codec_get_drvdata(codec);
  271. if (!codec_data) {
  272. dev_err(codec->dev,
  273. "%s: codec_data or ops acknowledge() is NULL\n",
  274. __func__);
  275. rc = -EINVAL;
  276. goto done;
  277. }
  278. mutex_lock(&codec_data->dp_ops_lock);
  279. codec_data->ctl[dai_id] = ucontrol->value.enumerated.item[0];
  280. codec_data->stream[dai_id] = ucontrol->value.enumerated.item[1];
  281. mutex_unlock(&codec_data->dp_ops_lock);
  282. done:
  283. return rc;
  284. }
  285. static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
  286. {
  287. .access = SNDRV_CTL_ELEM_ACCESS_READ |
  288. SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  289. .iface = SNDRV_CTL_ELEM_IFACE_PCM,
  290. .name = "HDMI EDID",
  291. .info = msm_ext_disp_edid_ctl_info,
  292. .get = msm_ext_disp_edid_get,
  293. .private_value = HDMI_DAI,
  294. },
  295. {
  296. .access = SNDRV_CTL_ELEM_ACCESS_READ |
  297. SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  298. .iface = SNDRV_CTL_ELEM_IFACE_PCM,
  299. .name = "Display Port EDID",
  300. .info = msm_ext_disp_edid_ctl_info,
  301. .get = msm_ext_disp_edid_get,
  302. .private_value = DP_DAI1,
  303. },
  304. {
  305. .access = SNDRV_CTL_ELEM_ACCESS_READ |
  306. SNDRV_CTL_ELEM_ACCESS_VOLATILE,
  307. .iface = SNDRV_CTL_ELEM_IFACE_PCM,
  308. .name = "Display Port1 EDID",
  309. .info = msm_ext_disp_edid_ctl_info,
  310. .get = msm_ext_disp_edid_get,
  311. .private_value = DP_DAI2,
  312. },
  313. SOC_ENUM_EXT("External Display Type",
  314. ext_disp_audio_type0,
  315. msm_ext_disp_audio_type_get, NULL),
  316. SOC_ENUM_EXT("External Display1 Type",
  317. ext_disp_audio_type1,
  318. msm_ext_disp_audio_type_get, NULL),
  319. SOC_ENUM_EXT("External Display Audio Ack",
  320. ext_disp_audio_ack_state0,
  321. NULL, msm_ext_disp_audio_ack_set),
  322. SOC_ENUM_EXT("External Display1 Audio Ack",
  323. ext_disp_audio_ack_state1,
  324. NULL, msm_ext_disp_audio_ack_set),
  325. SOC_SINGLE_EXT("External Display Audio Device",
  326. SND_SOC_NOPM, DP_DAI1, DP_STREAM_MAX, 0,
  327. NULL, msm_ext_disp_audio_device_set),
  328. SOC_SINGLE_EXT("External Display1 Audio Device",
  329. SND_SOC_NOPM, DP_DAI2, DP_STREAM_MAX, 0,
  330. NULL, msm_ext_disp_audio_device_set),
  331. };
  332. static int msm_ext_disp_audio_codec_rx_dai_startup(
  333. struct snd_pcm_substream *substream,
  334. struct snd_soc_dai *dai)
  335. {
  336. int ret = 0;
  337. struct msm_ext_disp_codec_id codec_info;
  338. struct msm_ext_disp_audio_codec_rx_data *codec_data =
  339. dev_get_drvdata(dai->codec->dev);
  340. if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
  341. dev_err(dai->dev, "%s() codec_data or cable_status is null\n",
  342. __func__);
  343. return -EINVAL;
  344. }
  345. dev_dbg(dai->codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
  346. codec_data->ctl[dai->id], codec_data->stream[dai->id]);
  347. mutex_lock(&codec_data->dp_ops_lock);
  348. SWITCH_DP_CODEC(codec_info, codec_data, dai->id);
  349. codec_data->cable_status =
  350. codec_data->ext_disp_ops.cable_status(
  351. codec_data->ext_disp_core_pdev, 1);
  352. mutex_unlock(&codec_data->dp_ops_lock);
  353. if (codec_data->cable_status < 0) {
  354. dev_err(dai->dev,
  355. "%s() ext disp core is not ready (ret val = %d)\n",
  356. __func__, codec_data->cable_status);
  357. ret = codec_data->cable_status;
  358. } else if (!codec_data->cable_status) {
  359. dev_err(dai->dev,
  360. "%s() ext disp cable is not connected (ret val = %d)\n",
  361. __func__, codec_data->cable_status);
  362. ret = -ENODEV;
  363. }
  364. return ret;
  365. }
  366. static int msm_ext_disp_audio_codec_rx_dai_hw_params(
  367. struct snd_pcm_substream *substream,
  368. struct snd_pcm_hw_params *params,
  369. struct snd_soc_dai *dai)
  370. {
  371. u32 channel_allocation = 0;
  372. u32 level_shift = 0; /* 0dB */
  373. bool down_mix = 0;
  374. u32 num_channels = params_channels(params);
  375. struct msm_ext_disp_codec_id codec_info;
  376. int rc = 0;
  377. struct msm_ext_disp_audio_setup_params audio_setup_params = {0};
  378. struct msm_ext_disp_audio_codec_rx_data *codec_data =
  379. dev_get_drvdata(dai->codec->dev);
  380. if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) {
  381. dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n",
  382. __func__);
  383. return -EINVAL;
  384. }
  385. dev_dbg(dai->codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
  386. codec_data->ctl[dai->id], codec_data->stream[dai->id]);
  387. if (codec_data->cable_status < 0) {
  388. dev_err_ratelimited(dai->dev,
  389. "%s() ext disp core is not ready (ret val = %d)\n",
  390. __func__, codec_data->cable_status);
  391. return codec_data->cable_status;
  392. } else if (!codec_data->cable_status) {
  393. dev_err_ratelimited(dai->dev,
  394. "%s() ext disp cable is not connected (ret val = %d)\n",
  395. __func__, codec_data->cable_status);
  396. return -ENODEV;
  397. }
  398. /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
  399. switch (num_channels) {
  400. case 2:
  401. channel_allocation = 0;
  402. break;
  403. case 3:
  404. channel_allocation = 0x02;/*default to FL/FR/FC*/
  405. audio_setup_params.sample_present = 0x3;
  406. break;
  407. case 4:
  408. channel_allocation = 0x06;/*default to FL/FR/FC/RC*/
  409. audio_setup_params.sample_present = 0x7;
  410. break;
  411. case 5:
  412. channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/
  413. audio_setup_params.sample_present = 0x7;
  414. break;
  415. case 6:
  416. channel_allocation = 0x0B;
  417. audio_setup_params.sample_present = 0x7;
  418. break;
  419. case 7:
  420. channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/
  421. audio_setup_params.sample_present = 0xf;
  422. break;
  423. case 8:
  424. channel_allocation = 0x13;
  425. audio_setup_params.sample_present = 0xf;
  426. break;
  427. default:
  428. dev_err(dai->dev, "invalid Channels = %u\n", num_channels);
  429. return -EINVAL;
  430. }
  431. dev_dbg(dai->dev,
  432. "%s() num_ch %u samplerate %u channel_allocation = %u\n",
  433. __func__, num_channels, params_rate(params),
  434. channel_allocation);
  435. audio_setup_params.sample_rate_hz = params_rate(params);
  436. audio_setup_params.num_of_channels = num_channels;
  437. audio_setup_params.channel_allocation = channel_allocation;
  438. audio_setup_params.level_shift = level_shift;
  439. audio_setup_params.down_mix = down_mix;
  440. mutex_lock(&codec_data->dp_ops_lock);
  441. SWITCH_DP_CODEC(codec_info, codec_data, dai->id);
  442. rc = codec_data->ext_disp_ops.audio_info_setup(
  443. codec_data->ext_disp_core_pdev, &audio_setup_params);
  444. mutex_unlock(&codec_data->dp_ops_lock);
  445. if (rc < 0) {
  446. dev_err_ratelimited(dai->dev,
  447. "%s() ext disp core is not ready, rc: %d\n",
  448. __func__, rc);
  449. }
  450. return rc;
  451. }
  452. static void msm_ext_disp_audio_codec_rx_dai_shutdown(
  453. struct snd_pcm_substream *substream,
  454. struct snd_soc_dai *dai)
  455. {
  456. int rc = 0;
  457. struct msm_ext_disp_codec_id codec_info;
  458. struct msm_ext_disp_audio_codec_rx_data *codec_data =
  459. dev_get_drvdata(dai->codec->dev);
  460. if (!codec_data || !codec_data->ext_disp_ops.teardown_done ||
  461. !codec_data->ext_disp_ops.cable_status) {
  462. dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n",
  463. __func__);
  464. return;
  465. }
  466. dev_dbg(dai->codec->dev, "%s: DP ctl id %d Stream id %d\n", __func__,
  467. codec_data->ctl[dai->id], codec_data->stream[dai->id]);
  468. mutex_lock(&codec_data->dp_ops_lock);
  469. SWITCH_DP_CODEC(codec_info, codec_data, dai->id);
  470. rc = codec_data->ext_disp_ops.cable_status(
  471. codec_data->ext_disp_core_pdev, 0);
  472. if (rc < 0) {
  473. dev_err(dai->dev,
  474. "%s: ext disp core had problems releasing audio flag\n",
  475. __func__);
  476. }
  477. codec_data->ext_disp_ops.teardown_done(
  478. codec_data->ext_disp_core_pdev);
  479. mutex_unlock(&codec_data->dp_ops_lock);
  480. }
  481. static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec)
  482. {
  483. struct msm_ext_disp_audio_codec_rx_data *codec_data;
  484. struct device_node *of_node_parent = NULL;
  485. codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data),
  486. GFP_KERNEL);
  487. if (!codec_data) {
  488. dev_err(codec->dev, "%s(): fail to allocate dai data\n",
  489. __func__);
  490. return -ENOMEM;
  491. }
  492. of_node_parent = of_get_parent(codec->dev->of_node);
  493. if (!of_node_parent) {
  494. dev_err(codec->dev, "%s(): Parent device tree node not found\n",
  495. __func__);
  496. kfree(codec_data);
  497. return -ENODEV;
  498. }
  499. codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent);
  500. if (!codec_data->ext_disp_core_pdev) {
  501. dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__);
  502. kfree(codec_data);
  503. return -ENODEV;
  504. }
  505. if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev,
  506. &codec_data->ext_disp_ops)) {
  507. dev_err(codec->dev, "%s(): can't register with ext disp core",
  508. __func__);
  509. kfree(codec_data);
  510. return -ENODEV;
  511. }
  512. mutex_init(&codec_data->dp_ops_lock);
  513. dev_set_drvdata(codec->dev, codec_data);
  514. dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n",
  515. __func__, codec->component.name);
  516. return 0;
  517. }
  518. static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec)
  519. {
  520. struct msm_ext_disp_audio_codec_rx_data *codec_data;
  521. codec_data = dev_get_drvdata(codec->dev);
  522. mutex_destroy(&codec_data->dp_ops_lock);
  523. kfree(codec_data);
  524. return 0;
  525. }
  526. static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = {
  527. .startup = msm_ext_disp_audio_codec_rx_dai_startup,
  528. .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params,
  529. .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown
  530. };
  531. static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = {
  532. {
  533. .name = "msm_hdmi_audio_codec_rx_dai",
  534. .id = HDMI_DAI,
  535. .playback = {
  536. .stream_name = "HDMI Playback",
  537. .channels_min = 1,
  538. .channels_max = 8,
  539. .rate_min = 48000,
  540. .rate_max = 48000,
  541. .rates = MSM_EXT_DISP_PCM_RATES,
  542. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  543. },
  544. .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
  545. },
  546. {
  547. .name = "msm_dp_audio_codec_rx_dai",
  548. .id = DP_DAI1,
  549. .playback = {
  550. .stream_name = "Display Port Playback",
  551. .channels_min = 1,
  552. .channels_max = 8,
  553. .rate_min = 48000,
  554. .rate_max = 192000,
  555. .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
  556. SNDRV_PCM_RATE_192000,
  557. .formats = SNDRV_PCM_FMTBIT_S16_LE |
  558. SNDRV_PCM_FMTBIT_S24_LE |
  559. SNDRV_PCM_FMTBIT_S24_3LE,
  560. },
  561. .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
  562. },
  563. {
  564. .name = "msm_dp_audio_codec_rx1_dai",
  565. .id = DP_DAI2,
  566. .playback = {
  567. .stream_name = "Display Port1 Playback",
  568. .channels_min = 1,
  569. .channels_max = 8,
  570. .rate_min = 48000,
  571. .rate_max = 192000,
  572. .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
  573. SNDRV_PCM_RATE_192000,
  574. .formats = SNDRV_PCM_FMTBIT_S16_LE |
  575. SNDRV_PCM_FMTBIT_S24_LE |
  576. SNDRV_PCM_FMTBIT_S24_3LE,
  577. },
  578. .ops = &msm_ext_disp_audio_codec_rx_dai_ops,
  579. },
  580. };
  581. static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = {
  582. .probe = msm_ext_disp_audio_codec_rx_probe,
  583. .remove = msm_ext_disp_audio_codec_rx_remove,
  584. .component_driver = {
  585. .controls = msm_ext_disp_codec_rx_controls,
  586. .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls),
  587. },
  588. };
  589. static int msm_ext_disp_audio_codec_rx_plat_probe(
  590. struct platform_device *pdev)
  591. {
  592. dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__,
  593. dev_name(&pdev->dev));
  594. return snd_soc_register_codec(&pdev->dev,
  595. &msm_ext_disp_audio_codec_rx_soc_driver,
  596. msm_ext_disp_audio_codec_rx_dais,
  597. ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais));
  598. }
  599. static int msm_ext_disp_audio_codec_rx_plat_remove(
  600. struct platform_device *pdev)
  601. {
  602. snd_soc_unregister_codec(&pdev->dev);
  603. return 0;
  604. }
  605. static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = {
  606. { .compatible = "qcom,msm-ext-disp-audio-codec-rx", },
  607. {}
  608. };
  609. MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match);
  610. static struct platform_driver msm_ext_disp_audio_codec_rx_driver = {
  611. .driver = {
  612. .name = "msm-ext-disp-audio-codec-rx",
  613. .owner = THIS_MODULE,
  614. .of_match_table = msm_ext_disp_audio_codec_rx_dt_match,
  615. },
  616. .probe = msm_ext_disp_audio_codec_rx_plat_probe,
  617. .remove = msm_ext_disp_audio_codec_rx_plat_remove,
  618. };
  619. static int __init msm_ext_disp_audio_codec_rx_init(void)
  620. {
  621. int rc = 0;
  622. rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver);
  623. if (rc) {
  624. pr_err("%s: failed to register ext disp codec driver err:%d\n",
  625. __func__, rc);
  626. }
  627. return rc;
  628. }
  629. module_init(msm_ext_disp_audio_codec_rx_init);
  630. static void __exit msm_ext_disp_audio_codec_rx_exit(void)
  631. {
  632. platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver);
  633. }
  634. module_exit(msm_ext_disp_audio_codec_rx_exit);
  635. MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver");
  636. MODULE_LICENSE("GPL v2");