msm-transcode-loopback-q6-v2.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223
  1. /* Copyright (c) 2017-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/init.h>
  13. #include <linux/err.h>
  14. #include <linux/module.h>
  15. #include <linux/moduleparam.h>
  16. #include <linux/time.h>
  17. #include <linux/math64.h>
  18. #include <linux/wait.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/slab.h>
  21. #include <sound/core.h>
  22. #include <sound/soc.h>
  23. #include <sound/soc-dapm.h>
  24. #include <sound/pcm.h>
  25. #include <sound/initval.h>
  26. #include <sound/control.h>
  27. #include <sound/pcm_params.h>
  28. #include <sound/timer.h>
  29. #include <sound/tlv.h>
  30. #include <sound/compress_params.h>
  31. #include <sound/compress_offload.h>
  32. #include <sound/compress_driver.h>
  33. #include <dsp/msm_audio_ion.h>
  34. #include <dsp/apr_audio-v2.h>
  35. #include <dsp/q6asm-v2.h>
  36. #include "msm-pcm-routing-v2.h"
  37. #include "msm-qti-pp-config.h"
  38. #define LOOPBACK_SESSION_MAX_NUM_STREAMS 2
  39. /* Max volume corresponding to 24dB */
  40. #define TRANSCODE_LR_VOL_MAX_DB 0xFFFF
  41. #define APP_TYPE_CONFIG_IDX_APP_TYPE 0
  42. #define APP_TYPE_CONFIG_IDX_ACDB_ID 1
  43. #define APP_TYPE_CONFIG_IDX_SAMPLE_RATE 2
  44. #define APP_TYPE_CONFIG_IDX_BE_ID 3
  45. static DEFINE_MUTEX(transcode_loopback_session_lock);
  46. struct trans_loopback_pdata {
  47. struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX];
  48. uint32_t master_gain;
  49. };
  50. struct loopback_stream {
  51. struct snd_compr_stream *cstream;
  52. uint32_t codec_format;
  53. bool start;
  54. };
  55. enum loopback_session_state {
  56. /* One or both streams not opened */
  57. LOOPBACK_SESSION_CLOSE = 0,
  58. /* Loopback streams opened */
  59. LOOPBACK_SESSION_READY,
  60. /* Loopback streams opened and formats configured */
  61. LOOPBACK_SESSION_START,
  62. /* Trigger issued on either of streams when in START state */
  63. LOOPBACK_SESSION_RUN
  64. };
  65. struct msm_transcode_loopback {
  66. struct loopback_stream source;
  67. struct loopback_stream sink;
  68. struct snd_compr_caps source_compr_cap;
  69. struct snd_compr_caps sink_compr_cap;
  70. uint32_t instance;
  71. uint32_t num_streams;
  72. int session_state;
  73. struct mutex lock;
  74. int session_id;
  75. struct audio_client *audio_client;
  76. };
  77. /* Transcode loopback global info struct */
  78. static struct msm_transcode_loopback transcode_info;
  79. static void loopback_event_handler(uint32_t opcode,
  80. uint32_t token, uint32_t *payload, void *priv)
  81. {
  82. struct msm_transcode_loopback *trans =
  83. (struct msm_transcode_loopback *)priv;
  84. struct snd_soc_pcm_runtime *rtd;
  85. struct snd_compr_stream *cstream;
  86. struct audio_client *ac;
  87. int stream_id;
  88. int ret;
  89. if (!trans || !payload) {
  90. pr_err("%s: rtd or payload is NULL\n", __func__);
  91. return;
  92. }
  93. cstream = trans->sink.cstream;
  94. ac = trans->audio_client;
  95. /*
  96. * Token for rest of the compressed commands use to set
  97. * session id, stream id, dir etc.
  98. */
  99. stream_id = q6asm_get_stream_id_from_token(token);
  100. switch (opcode) {
  101. case ASM_STREAM_CMD_ENCDEC_EVENTS:
  102. case ASM_IEC_61937_MEDIA_FMT_EVENT:
  103. pr_debug("%s: Handling stream event : 0X%x\n",
  104. __func__, opcode);
  105. rtd = cstream->private_data;
  106. if (!rtd) {
  107. pr_err("%s: rtd is NULL\n", __func__);
  108. return;
  109. }
  110. ret = msm_adsp_inform_mixer_ctl(rtd, payload);
  111. if (ret) {
  112. pr_err("%s: failed to inform mixer ctrl. err = %d\n",
  113. __func__, ret);
  114. return;
  115. }
  116. break;
  117. case APR_BASIC_RSP_RESULT: {
  118. switch (payload[0]) {
  119. case ASM_SESSION_CMD_RUN_V2:
  120. pr_debug("%s: ASM_SESSION_CMD_RUN_V2:", __func__);
  121. pr_debug("token 0x%x, stream id %d\n", token,
  122. stream_id);
  123. break;
  124. case ASM_STREAM_CMD_CLOSE:
  125. pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__);
  126. pr_debug("token 0x%x, stream id %d\n", token,
  127. stream_id);
  128. break;
  129. default:
  130. break;
  131. }
  132. break;
  133. }
  134. default:
  135. pr_debug("%s: Not Supported Event opcode[0x%x]\n",
  136. __func__, opcode);
  137. break;
  138. }
  139. }
  140. static void populate_codec_list(struct msm_transcode_loopback *trans,
  141. struct snd_compr_stream *cstream)
  142. {
  143. struct snd_compr_caps compr_cap;
  144. pr_debug("%s\n", __func__);
  145. memset(&compr_cap, 0, sizeof(struct snd_compr_caps));
  146. if (cstream->direction == SND_COMPRESS_CAPTURE) {
  147. compr_cap.direction = SND_COMPRESS_CAPTURE;
  148. compr_cap.num_codecs = 3;
  149. compr_cap.codecs[0] = SND_AUDIOCODEC_PCM;
  150. compr_cap.codecs[1] = SND_AUDIOCODEC_AC3;
  151. compr_cap.codecs[2] = SND_AUDIOCODEC_EAC3;
  152. memcpy(&trans->source_compr_cap, &compr_cap,
  153. sizeof(struct snd_compr_caps));
  154. }
  155. if (cstream->direction == SND_COMPRESS_PLAYBACK) {
  156. compr_cap.direction = SND_COMPRESS_PLAYBACK;
  157. compr_cap.num_codecs = 1;
  158. compr_cap.codecs[0] = SND_AUDIOCODEC_PCM;
  159. memcpy(&trans->sink_compr_cap, &compr_cap,
  160. sizeof(struct snd_compr_caps));
  161. }
  162. }
  163. static int msm_transcode_loopback_open(struct snd_compr_stream *cstream)
  164. {
  165. int ret = 0;
  166. struct snd_compr_runtime *runtime;
  167. struct snd_soc_pcm_runtime *rtd;
  168. struct msm_transcode_loopback *trans = &transcode_info;
  169. struct trans_loopback_pdata *pdata;
  170. if (cstream == NULL) {
  171. pr_err("%s: Invalid substream\n", __func__);
  172. return -EINVAL;
  173. }
  174. runtime = cstream->runtime;
  175. rtd = snd_pcm_substream_chip(cstream);
  176. pdata = snd_soc_platform_get_drvdata(rtd->platform);
  177. pdata->cstream[rtd->dai_link->id] = cstream;
  178. mutex_lock(&trans->lock);
  179. if (trans->num_streams > LOOPBACK_SESSION_MAX_NUM_STREAMS) {
  180. pr_err("msm_transcode_open failed..invalid stream\n");
  181. ret = -EINVAL;
  182. goto exit;
  183. }
  184. if (cstream->direction == SND_COMPRESS_CAPTURE) {
  185. if (trans->source.cstream == NULL) {
  186. trans->source.cstream = cstream;
  187. trans->num_streams++;
  188. } else {
  189. pr_err("%s: capture stream already opened\n",
  190. __func__);
  191. ret = -EINVAL;
  192. goto exit;
  193. }
  194. } else if (cstream->direction == SND_COMPRESS_PLAYBACK) {
  195. if (trans->sink.cstream == NULL) {
  196. trans->sink.cstream = cstream;
  197. trans->num_streams++;
  198. } else {
  199. pr_debug("%s: playback stream already opened\n",
  200. __func__);
  201. ret = -EINVAL;
  202. goto exit;
  203. }
  204. msm_adsp_init_mixer_ctl_pp_event_queue(rtd);
  205. }
  206. pr_debug("%s: num stream%d, stream name %s\n", __func__,
  207. trans->num_streams, cstream->name);
  208. populate_codec_list(trans, cstream);
  209. if (trans->num_streams == LOOPBACK_SESSION_MAX_NUM_STREAMS) {
  210. pr_debug("%s: Moving loopback session to READY state %d\n",
  211. __func__, trans->session_state);
  212. trans->session_state = LOOPBACK_SESSION_READY;
  213. }
  214. runtime->private_data = trans;
  215. exit:
  216. mutex_unlock(&trans->lock);
  217. return ret;
  218. }
  219. static void stop_transcoding(struct msm_transcode_loopback *trans)
  220. {
  221. struct snd_soc_pcm_runtime *soc_pcm_rx;
  222. struct snd_soc_pcm_runtime *soc_pcm_tx;
  223. if (trans->audio_client != NULL) {
  224. q6asm_cmd(trans->audio_client, CMD_CLOSE);
  225. if (trans->sink.cstream != NULL) {
  226. soc_pcm_rx = trans->sink.cstream->private_data;
  227. msm_pcm_routing_dereg_phy_stream(
  228. soc_pcm_rx->dai_link->id,
  229. SND_COMPRESS_PLAYBACK);
  230. }
  231. if (trans->source.cstream != NULL) {
  232. soc_pcm_tx = trans->source.cstream->private_data;
  233. msm_pcm_routing_dereg_phy_stream(
  234. soc_pcm_tx->dai_link->id,
  235. SND_COMPRESS_CAPTURE);
  236. }
  237. q6asm_audio_client_free(trans->audio_client);
  238. trans->audio_client = NULL;
  239. }
  240. }
  241. static int msm_transcode_loopback_free(struct snd_compr_stream *cstream)
  242. {
  243. struct snd_compr_runtime *runtime = cstream->runtime;
  244. struct msm_transcode_loopback *trans = runtime->private_data;
  245. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(cstream);
  246. int ret = 0;
  247. mutex_lock(&trans->lock);
  248. pr_debug("%s: Transcode loopback end:%d, streams %d\n", __func__,
  249. cstream->direction, trans->num_streams);
  250. trans->num_streams--;
  251. stop_transcoding(trans);
  252. if (cstream->direction == SND_COMPRESS_PLAYBACK) {
  253. memset(&trans->sink, 0, sizeof(struct loopback_stream));
  254. msm_adsp_clean_mixer_ctl_pp_event_queue(rtd);
  255. } else if (cstream->direction == SND_COMPRESS_CAPTURE) {
  256. memset(&trans->source, 0, sizeof(struct loopback_stream));
  257. }
  258. trans->session_state = LOOPBACK_SESSION_CLOSE;
  259. mutex_unlock(&trans->lock);
  260. return ret;
  261. }
  262. static int msm_transcode_loopback_trigger(struct snd_compr_stream *cstream,
  263. int cmd)
  264. {
  265. struct snd_compr_runtime *runtime = cstream->runtime;
  266. struct msm_transcode_loopback *trans = runtime->private_data;
  267. switch (cmd) {
  268. case SNDRV_PCM_TRIGGER_START:
  269. case SNDRV_PCM_TRIGGER_RESUME:
  270. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  271. if (trans->session_state == LOOPBACK_SESSION_START) {
  272. pr_debug("%s: Issue Loopback session %d RUN\n",
  273. __func__, trans->instance);
  274. q6asm_run_nowait(trans->audio_client, 0, 0, 0);
  275. trans->session_state = LOOPBACK_SESSION_RUN;
  276. }
  277. break;
  278. case SNDRV_PCM_TRIGGER_SUSPEND:
  279. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  280. case SNDRV_PCM_TRIGGER_STOP:
  281. pr_debug("%s: Issue Loopback session %d STOP\n", __func__,
  282. trans->instance);
  283. if (trans->session_state == LOOPBACK_SESSION_RUN)
  284. q6asm_cmd_nowait(trans->audio_client, CMD_PAUSE);
  285. trans->session_state = LOOPBACK_SESSION_START;
  286. break;
  287. default:
  288. break;
  289. }
  290. return 0;
  291. }
  292. static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
  293. struct snd_compr_params *codec_param)
  294. {
  295. struct snd_compr_runtime *runtime = cstream->runtime;
  296. struct msm_transcode_loopback *trans = runtime->private_data;
  297. struct snd_soc_pcm_runtime *soc_pcm_rx;
  298. struct snd_soc_pcm_runtime *soc_pcm_tx;
  299. uint32_t bit_width = 16;
  300. int ret = 0;
  301. if (trans == NULL) {
  302. pr_err("%s: Invalid param\n", __func__);
  303. return -EINVAL;
  304. }
  305. mutex_lock(&trans->lock);
  306. if (cstream->direction == SND_COMPRESS_PLAYBACK) {
  307. if (codec_param->codec.id == SND_AUDIOCODEC_PCM) {
  308. trans->sink.codec_format =
  309. FORMAT_LINEAR_PCM;
  310. switch (codec_param->codec.format) {
  311. case SNDRV_PCM_FORMAT_S32_LE:
  312. bit_width = 32;
  313. break;
  314. case SNDRV_PCM_FORMAT_S24_LE:
  315. bit_width = 24;
  316. break;
  317. case SNDRV_PCM_FORMAT_S24_3LE:
  318. bit_width = 24;
  319. break;
  320. case SNDRV_PCM_FORMAT_S16_LE:
  321. default:
  322. bit_width = 16;
  323. break;
  324. }
  325. } else {
  326. pr_debug("%s: unknown sink codec\n", __func__);
  327. ret = -EINVAL;
  328. goto exit;
  329. }
  330. trans->sink.start = true;
  331. }
  332. if (cstream->direction == SND_COMPRESS_CAPTURE) {
  333. switch (codec_param->codec.id) {
  334. case SND_AUDIOCODEC_PCM:
  335. pr_debug("Source SND_AUDIOCODEC_PCM\n");
  336. trans->source.codec_format =
  337. FORMAT_LINEAR_PCM;
  338. break;
  339. case SND_AUDIOCODEC_AC3:
  340. pr_debug("Source SND_AUDIOCODEC_AC3\n");
  341. trans->source.codec_format =
  342. FORMAT_AC3;
  343. break;
  344. case SND_AUDIOCODEC_EAC3:
  345. pr_debug("Source SND_AUDIOCODEC_EAC3\n");
  346. trans->source.codec_format =
  347. FORMAT_EAC3;
  348. break;
  349. default:
  350. pr_debug("%s: unknown source codec\n", __func__);
  351. ret = -EINVAL;
  352. goto exit;
  353. }
  354. trans->source.start = true;
  355. }
  356. pr_debug("%s: trans->source.start %d trans->sink.start %d trans->source.cstream %pK trans->sink.cstream %pK trans->session_state %d\n",
  357. __func__, trans->source.start, trans->sink.start,
  358. trans->source.cstream, trans->sink.cstream,
  359. trans->session_state);
  360. if ((trans->session_state == LOOPBACK_SESSION_READY) &&
  361. trans->source.start && trans->sink.start) {
  362. pr_debug("%s: Moving loopback session to start state\n",
  363. __func__);
  364. trans->session_state = LOOPBACK_SESSION_START;
  365. }
  366. if (trans->session_state == LOOPBACK_SESSION_START) {
  367. if (trans->audio_client != NULL) {
  368. pr_debug("%s: ASM client already opened, closing\n",
  369. __func__);
  370. stop_transcoding(trans);
  371. }
  372. trans->audio_client = q6asm_audio_client_alloc(
  373. (app_cb)loopback_event_handler, trans);
  374. if (!trans->audio_client) {
  375. pr_err("%s: Could not allocate memory\n", __func__);
  376. ret = -EINVAL;
  377. goto exit;
  378. }
  379. pr_debug("%s: ASM client allocated, callback %pK\n", __func__,
  380. loopback_event_handler);
  381. trans->session_id = trans->audio_client->session;
  382. trans->audio_client->perf_mode = false;
  383. ret = q6asm_open_transcode_loopback(trans->audio_client,
  384. bit_width,
  385. trans->source.codec_format,
  386. trans->sink.codec_format);
  387. if (ret < 0) {
  388. pr_err("%s: Session transcode loopback open failed\n",
  389. __func__);
  390. q6asm_audio_client_free(trans->audio_client);
  391. trans->audio_client = NULL;
  392. goto exit;
  393. }
  394. pr_debug("%s: Starting ADM open for loopback\n", __func__);
  395. soc_pcm_rx = trans->sink.cstream->private_data;
  396. soc_pcm_tx = trans->source.cstream->private_data;
  397. if (trans->source.codec_format != FORMAT_LINEAR_PCM)
  398. msm_pcm_routing_reg_phy_compr_stream(
  399. soc_pcm_tx->dai_link->id,
  400. trans->audio_client->perf_mode,
  401. trans->session_id,
  402. SNDRV_PCM_STREAM_CAPTURE,
  403. COMPRESSED_PASSTHROUGH_GEN);
  404. else
  405. msm_pcm_routing_reg_phy_stream(
  406. soc_pcm_tx->dai_link->id,
  407. trans->audio_client->perf_mode,
  408. trans->session_id,
  409. SNDRV_PCM_STREAM_CAPTURE);
  410. /* Opening Rx ADM in LOW_LATENCY mode by default */
  411. msm_pcm_routing_reg_phy_stream(
  412. soc_pcm_rx->dai_link->id,
  413. true,
  414. trans->session_id,
  415. SNDRV_PCM_STREAM_PLAYBACK);
  416. pr_debug("%s: Successfully opened ADM sessions\n", __func__);
  417. }
  418. exit:
  419. mutex_unlock(&trans->lock);
  420. return ret;
  421. }
  422. static int msm_transcode_loopback_get_caps(struct snd_compr_stream *cstream,
  423. struct snd_compr_caps *arg)
  424. {
  425. struct snd_compr_runtime *runtime;
  426. struct msm_transcode_loopback *trans;
  427. if (!arg || !cstream) {
  428. pr_err("%s: Invalid arguments\n", __func__);
  429. return -EINVAL;
  430. }
  431. runtime = cstream->runtime;
  432. trans = runtime->private_data;
  433. pr_debug("%s\n", __func__);
  434. if (cstream->direction == SND_COMPRESS_CAPTURE)
  435. memcpy(arg, &trans->source_compr_cap,
  436. sizeof(struct snd_compr_caps));
  437. else
  438. memcpy(arg, &trans->sink_compr_cap,
  439. sizeof(struct snd_compr_caps));
  440. return 0;
  441. }
  442. static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol,
  443. struct snd_ctl_elem_value *ucontrol)
  444. {
  445. struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
  446. unsigned long fe_id = kcontrol->private_value;
  447. struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
  448. snd_soc_component_get_drvdata(comp);
  449. struct snd_compr_stream *cstream = NULL;
  450. struct msm_transcode_loopback *prtd;
  451. int ret = 0;
  452. struct msm_adsp_event_data *event_data = NULL;
  453. if (fe_id >= MSM_FRONTEND_DAI_MAX) {
  454. pr_err("%s Received invalid fe_id %lu\n",
  455. __func__, fe_id);
  456. ret = -EINVAL;
  457. goto done;
  458. }
  459. cstream = pdata->cstream[fe_id];
  460. if (cstream == NULL) {
  461. pr_err("%s cstream is null.\n", __func__);
  462. ret = -EINVAL;
  463. goto done;
  464. }
  465. prtd = cstream->runtime->private_data;
  466. if (!prtd) {
  467. pr_err("%s: prtd is null.\n", __func__);
  468. ret = -EINVAL;
  469. goto done;
  470. }
  471. if (prtd->audio_client == NULL) {
  472. pr_err("%s: audio_client is null.\n", __func__);
  473. ret = -EINVAL;
  474. goto done;
  475. }
  476. event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
  477. if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
  478. (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
  479. pr_err("%s: invalid event_type=%d",
  480. __func__, event_data->event_type);
  481. ret = -EINVAL;
  482. goto done;
  483. }
  484. if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
  485. - sizeof(struct msm_adsp_event_data)) {
  486. pr_err("%s param length=%d exceeds limit",
  487. __func__, event_data->payload_len);
  488. ret = -EINVAL;
  489. goto done;
  490. }
  491. ret = q6asm_send_stream_cmd(prtd->audio_client, event_data);
  492. if (ret < 0)
  493. pr_err("%s: failed to send stream event cmd, err = %d\n",
  494. __func__, ret);
  495. done:
  496. return ret;
  497. }
  498. static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol,
  499. struct snd_ctl_elem_value *ucontrol)
  500. {
  501. struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
  502. unsigned long fe_id = kcontrol->private_value;
  503. struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
  504. snd_soc_component_get_drvdata(comp);
  505. struct snd_compr_stream *cstream = NULL;
  506. struct msm_transcode_loopback *prtd;
  507. int fd;
  508. int ret = 0;
  509. if (fe_id >= MSM_FRONTEND_DAI_MAX) {
  510. pr_err("%s Received out of bounds invalid fe_id %lu\n",
  511. __func__, fe_id);
  512. ret = -EINVAL;
  513. goto done;
  514. }
  515. cstream = pdata->cstream[fe_id];
  516. if (cstream == NULL) {
  517. pr_err("%s cstream is null\n", __func__);
  518. ret = -EINVAL;
  519. goto done;
  520. }
  521. prtd = cstream->runtime->private_data;
  522. if (!prtd) {
  523. pr_err("%s: prtd is null\n", __func__);
  524. ret = -EINVAL;
  525. goto done;
  526. }
  527. if (prtd->audio_client == NULL) {
  528. pr_err("%s: audio_client is null\n", __func__);
  529. ret = -EINVAL;
  530. goto done;
  531. }
  532. memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd));
  533. ret = q6asm_send_ion_fd(prtd->audio_client, fd);
  534. if (ret < 0)
  535. pr_err("%s: failed to register ion fd\n", __func__);
  536. done:
  537. return ret;
  538. }
  539. static int msm_transcode_rtic_event_ack_put(struct snd_kcontrol *kcontrol,
  540. struct snd_ctl_elem_value *ucontrol)
  541. {
  542. struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
  543. unsigned long fe_id = kcontrol->private_value;
  544. struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
  545. snd_soc_component_get_drvdata(comp);
  546. struct snd_compr_stream *cstream = NULL;
  547. struct msm_transcode_loopback *prtd;
  548. int ret = 0;
  549. int param_length = 0;
  550. if (fe_id >= MSM_FRONTEND_DAI_MAX) {
  551. pr_err("%s Received invalid fe_id %lu\n",
  552. __func__, fe_id);
  553. ret = -EINVAL;
  554. goto done;
  555. }
  556. cstream = pdata->cstream[fe_id];
  557. if (cstream == NULL) {
  558. pr_err("%s cstream is null\n", __func__);
  559. ret = -EINVAL;
  560. goto done;
  561. }
  562. prtd = cstream->runtime->private_data;
  563. if (!prtd) {
  564. pr_err("%s: prtd is null\n", __func__);
  565. ret = -EINVAL;
  566. goto done;
  567. }
  568. if (prtd->audio_client == NULL) {
  569. pr_err("%s: audio_client is null\n", __func__);
  570. ret = -EINVAL;
  571. goto done;
  572. }
  573. memcpy(&param_length, ucontrol->value.bytes.data,
  574. sizeof(param_length));
  575. if ((param_length + sizeof(param_length))
  576. >= sizeof(ucontrol->value.bytes.data)) {
  577. pr_err("%s param length=%d exceeds limit",
  578. __func__, param_length);
  579. ret = -EINVAL;
  580. goto done;
  581. }
  582. ret = q6asm_send_rtic_event_ack(prtd->audio_client,
  583. ucontrol->value.bytes.data + sizeof(param_length),
  584. param_length);
  585. if (ret < 0)
  586. pr_err("%s: failed to send rtic event ack, err = %d\n",
  587. __func__, ret);
  588. done:
  589. return ret;
  590. }
  591. static int msm_transcode_playback_app_type_cfg_put(
  592. struct snd_kcontrol *kcontrol,
  593. struct snd_ctl_elem_value *ucontrol)
  594. {
  595. u64 fe_id = kcontrol->private_value;
  596. int session_type = SESSION_TYPE_RX;
  597. int be_id = ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID];
  598. struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
  599. int ret = 0;
  600. cfg_data.app_type = ucontrol->value.integer.value[
  601. APP_TYPE_CONFIG_IDX_APP_TYPE];
  602. cfg_data.acdb_dev_id = ucontrol->value.integer.value[
  603. APP_TYPE_CONFIG_IDX_ACDB_ID];
  604. if (ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] != 0)
  605. cfg_data.sample_rate = ucontrol->value.integer.value[
  606. APP_TYPE_CONFIG_IDX_SAMPLE_RATE];
  607. pr_debug("%s: fe_id %llu session_type %d be_id %d app_type %d acdb_dev_id %d sample_rate- %d\n",
  608. __func__, fe_id, session_type, be_id,
  609. cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
  610. ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
  611. be_id, &cfg_data);
  612. if (ret < 0)
  613. pr_err("%s: msm_transcode_playback_stream_app_type_cfg set failed returned %d\n",
  614. __func__, ret);
  615. return ret;
  616. }
  617. static int msm_transcode_playback_app_type_cfg_get(
  618. struct snd_kcontrol *kcontrol,
  619. struct snd_ctl_elem_value *ucontrol)
  620. {
  621. u64 fe_id = kcontrol->private_value;
  622. int session_type = SESSION_TYPE_RX;
  623. int be_id = 0;
  624. struct msm_pcm_stream_app_type_cfg cfg_data = {0};
  625. int ret = 0;
  626. ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
  627. &be_id, &cfg_data);
  628. if (ret < 0) {
  629. pr_err("%s: msm_transcode_playback_stream_app_type_cfg get failed returned %d\n",
  630. __func__, ret);
  631. goto done;
  632. }
  633. ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_APP_TYPE] =
  634. cfg_data.app_type;
  635. ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_ACDB_ID] =
  636. cfg_data.acdb_dev_id;
  637. ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_SAMPLE_RATE] =
  638. cfg_data.sample_rate;
  639. ucontrol->value.integer.value[APP_TYPE_CONFIG_IDX_BE_ID] = be_id;
  640. pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
  641. __func__, fe_id, session_type, be_id,
  642. cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
  643. done:
  644. return ret;
  645. }
  646. static int msm_transcode_set_volume(struct snd_compr_stream *cstream,
  647. uint32_t master_gain)
  648. {
  649. int rc = 0;
  650. struct msm_transcode_loopback *prtd;
  651. struct snd_soc_pcm_runtime *rtd;
  652. pr_debug("%s: master_gain %d\n", __func__, master_gain);
  653. if (!cstream || !cstream->runtime) {
  654. pr_err("%s: session not active\n", __func__);
  655. return -EINVAL;
  656. }
  657. rtd = cstream->private_data;
  658. prtd = cstream->runtime->private_data;
  659. if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) {
  660. pr_err("%s: invalid rtd, prtd or audio client", __func__);
  661. return -EINVAL;
  662. }
  663. rc = q6asm_set_volume(prtd->audio_client, master_gain);
  664. if (rc < 0)
  665. pr_err("%s: Send vol gain command failed rc=%d\n",
  666. __func__, rc);
  667. return rc;
  668. }
  669. static int msm_transcode_volume_put(struct snd_kcontrol *kcontrol,
  670. struct snd_ctl_elem_value *ucontrol)
  671. {
  672. struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
  673. unsigned long fe_id = kcontrol->private_value;
  674. struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
  675. snd_soc_component_get_drvdata(comp);
  676. struct snd_compr_stream *cstream = NULL;
  677. uint32_t ret = 0;
  678. if (fe_id >= MSM_FRONTEND_DAI_MAX) {
  679. pr_err("%s Received out of bounds fe_id %lu\n",
  680. __func__, fe_id);
  681. return -EINVAL;
  682. }
  683. cstream = pdata->cstream[fe_id];
  684. pdata->master_gain = ucontrol->value.integer.value[0];
  685. pr_debug("%s: fe_id %lu master_gain %d\n",
  686. __func__, fe_id, pdata->master_gain);
  687. if (cstream)
  688. ret = msm_transcode_set_volume(cstream, pdata->master_gain);
  689. return ret;
  690. }
  691. static int msm_transcode_volume_get(struct snd_kcontrol *kcontrol,
  692. struct snd_ctl_elem_value *ucontrol)
  693. {
  694. struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
  695. unsigned long fe_id = kcontrol->private_value;
  696. struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
  697. snd_soc_component_get_drvdata(comp);
  698. if (fe_id >= MSM_FRONTEND_DAI_MAX) {
  699. pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id);
  700. return -EINVAL;
  701. }
  702. pr_debug("%s: fe_id %lu\n", __func__, fe_id);
  703. ucontrol->value.integer.value[0] = pdata->master_gain;
  704. return 0;
  705. }
  706. static int msm_transcode_stream_cmd_control(
  707. struct snd_soc_pcm_runtime *rtd)
  708. {
  709. const char *mixer_ctl_name = DSP_STREAM_CMD;
  710. const char *deviceNo = "NN";
  711. char *mixer_str = NULL;
  712. int ctl_len = 0, ret = 0;
  713. struct snd_kcontrol_new fe_loopback_stream_cmd_config_control[1] = {
  714. {
  715. .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  716. .name = "?",
  717. .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  718. .info = msm_adsp_stream_cmd_info,
  719. .put = msm_transcode_stream_cmd_put,
  720. .private_value = 0,
  721. }
  722. };
  723. if (!rtd) {
  724. pr_err("%s NULL rtd\n", __func__);
  725. ret = -EINVAL;
  726. goto done;
  727. }
  728. ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
  729. mixer_str = kzalloc(ctl_len, GFP_KERNEL);
  730. if (!mixer_str) {
  731. ret = -ENOMEM;
  732. goto done;
  733. }
  734. snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
  735. fe_loopback_stream_cmd_config_control[0].name = mixer_str;
  736. fe_loopback_stream_cmd_config_control[0].private_value =
  737. rtd->dai_link->id;
  738. pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
  739. ret = snd_soc_add_platform_controls(rtd->platform,
  740. fe_loopback_stream_cmd_config_control,
  741. ARRAY_SIZE(fe_loopback_stream_cmd_config_control));
  742. if (ret < 0)
  743. pr_err("%s: failed to add ctl %s. err = %d\n",
  744. __func__, mixer_str, ret);
  745. kfree(mixer_str);
  746. done:
  747. return ret;
  748. }
  749. static int msm_transcode_stream_callback_control(
  750. struct snd_soc_pcm_runtime *rtd)
  751. {
  752. const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
  753. const char *deviceNo = "NN";
  754. char *mixer_str = NULL;
  755. int ctl_len = 0, ret = 0;
  756. struct snd_kcontrol *kctl;
  757. struct snd_kcontrol_new fe_loopback_callback_config_control[1] = {
  758. {
  759. .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  760. .name = "?",
  761. .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  762. .info = msm_adsp_stream_callback_info,
  763. .get = msm_adsp_stream_callback_get,
  764. .private_value = 0,
  765. }
  766. };
  767. if (!rtd) {
  768. pr_err("%s: rtd is NULL\n", __func__);
  769. ret = -EINVAL;
  770. goto done;
  771. }
  772. ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
  773. mixer_str = kzalloc(ctl_len, GFP_KERNEL);
  774. if (!mixer_str) {
  775. ret = -ENOMEM;
  776. goto done;
  777. }
  778. snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
  779. fe_loopback_callback_config_control[0].name = mixer_str;
  780. fe_loopback_callback_config_control[0].private_value =
  781. rtd->dai_link->id;
  782. pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
  783. ret = snd_soc_add_platform_controls(rtd->platform,
  784. fe_loopback_callback_config_control,
  785. ARRAY_SIZE(fe_loopback_callback_config_control));
  786. if (ret < 0) {
  787. pr_err("%s: failed to add ctl %s. err = %d\n",
  788. __func__, mixer_str, ret);
  789. ret = -EINVAL;
  790. goto free_mixer_str;
  791. }
  792. kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
  793. if (!kctl) {
  794. pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
  795. ret = -EINVAL;
  796. goto free_mixer_str;
  797. }
  798. kctl->private_data = NULL;
  799. free_mixer_str:
  800. kfree(mixer_str);
  801. done:
  802. return ret;
  803. }
  804. static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
  805. {
  806. const char *mixer_ctl_name = "Playback ION FD";
  807. const char *deviceNo = "NN";
  808. char *mixer_str = NULL;
  809. int ctl_len = 0, ret = 0;
  810. struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
  811. {
  812. .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  813. .name = "?",
  814. .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  815. .info = msm_adsp_stream_cmd_info,
  816. .put = msm_transcode_ion_fd_map_put,
  817. .private_value = 0,
  818. }
  819. };
  820. if (!rtd) {
  821. pr_err("%s NULL rtd\n", __func__);
  822. ret = -EINVAL;
  823. goto done;
  824. }
  825. ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
  826. mixer_str = kzalloc(ctl_len, GFP_KERNEL);
  827. if (!mixer_str) {
  828. ret = -ENOMEM;
  829. goto done;
  830. }
  831. snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
  832. fe_ion_fd_config_control[0].name = mixer_str;
  833. fe_ion_fd_config_control[0].private_value = rtd->dai_link->id;
  834. pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
  835. ret = snd_soc_add_platform_controls(rtd->platform,
  836. fe_ion_fd_config_control,
  837. ARRAY_SIZE(fe_ion_fd_config_control));
  838. if (ret < 0)
  839. pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
  840. kfree(mixer_str);
  841. done:
  842. return ret;
  843. }
  844. static int msm_transcode_add_event_ack_cmd_control(
  845. struct snd_soc_pcm_runtime *rtd)
  846. {
  847. const char *mixer_ctl_name = "Playback Event Ack";
  848. const char *deviceNo = "NN";
  849. char *mixer_str = NULL;
  850. int ctl_len = 0, ret = 0;
  851. struct snd_kcontrol_new fe_event_ack_config_control[1] = {
  852. {
  853. .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  854. .name = "?",
  855. .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  856. .info = msm_adsp_stream_cmd_info,
  857. .put = msm_transcode_rtic_event_ack_put,
  858. .private_value = 0,
  859. }
  860. };
  861. if (!rtd) {
  862. pr_err("%s NULL rtd\n", __func__);
  863. ret = -EINVAL;
  864. goto done;
  865. }
  866. ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
  867. mixer_str = kzalloc(ctl_len, GFP_KERNEL);
  868. if (!mixer_str) {
  869. ret = -ENOMEM;
  870. goto done;
  871. }
  872. snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
  873. fe_event_ack_config_control[0].name = mixer_str;
  874. fe_event_ack_config_control[0].private_value = rtd->dai_link->id;
  875. pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
  876. ret = snd_soc_add_platform_controls(rtd->platform,
  877. fe_event_ack_config_control,
  878. ARRAY_SIZE(fe_event_ack_config_control));
  879. if (ret < 0)
  880. pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
  881. kfree(mixer_str);
  882. done:
  883. return ret;
  884. }
  885. static int msm_transcode_app_type_cfg_info(struct snd_kcontrol *kcontrol,
  886. struct snd_ctl_elem_info *uinfo)
  887. {
  888. uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  889. uinfo->count = 5;
  890. uinfo->value.integer.min = 0;
  891. uinfo->value.integer.max = 0xFFFFFFFF;
  892. return 0;
  893. }
  894. static int msm_transcode_add_app_type_cfg_control(
  895. struct snd_soc_pcm_runtime *rtd)
  896. {
  897. char mixer_str[32];
  898. struct snd_kcontrol_new fe_app_type_cfg_control[1] = {
  899. {
  900. .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  901. .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
  902. .info = msm_transcode_app_type_cfg_info,
  903. .put = msm_transcode_playback_app_type_cfg_put,
  904. .get = msm_transcode_playback_app_type_cfg_get,
  905. .private_value = 0,
  906. }
  907. };
  908. if (!rtd) {
  909. pr_err("%s NULL rtd\n", __func__);
  910. return -EINVAL;
  911. }
  912. if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
  913. snprintf(mixer_str, sizeof(mixer_str),
  914. "Audio Stream %d App Type Cfg",
  915. rtd->pcm->device);
  916. fe_app_type_cfg_control[0].name = mixer_str;
  917. fe_app_type_cfg_control[0].private_value = rtd->dai_link->id;
  918. fe_app_type_cfg_control[0].put =
  919. msm_transcode_playback_app_type_cfg_put;
  920. fe_app_type_cfg_control[0].get =
  921. msm_transcode_playback_app_type_cfg_get;
  922. pr_debug("Registering new mixer ctl %s", mixer_str);
  923. snd_soc_add_platform_controls(rtd->platform,
  924. fe_app_type_cfg_control,
  925. ARRAY_SIZE(fe_app_type_cfg_control));
  926. }
  927. return 0;
  928. }
  929. static int msm_transcode_volume_info(struct snd_kcontrol *kcontrol,
  930. struct snd_ctl_elem_info *uinfo)
  931. {
  932. uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  933. uinfo->count = 1;
  934. uinfo->value.integer.min = 0;
  935. uinfo->value.integer.max = TRANSCODE_LR_VOL_MAX_DB;
  936. return 0;
  937. }
  938. static int msm_transcode_add_volume_control(struct snd_soc_pcm_runtime *rtd)
  939. {
  940. struct snd_kcontrol_new fe_volume_control[1] = {
  941. {
  942. .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  943. .name = "Transcode Loopback Rx Volume",
  944. .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
  945. SNDRV_CTL_ELEM_ACCESS_READWRITE,
  946. .info = msm_transcode_volume_info,
  947. .get = msm_transcode_volume_get,
  948. .put = msm_transcode_volume_put,
  949. .private_value = 0,
  950. }
  951. };
  952. if (!rtd) {
  953. pr_err("%s NULL rtd\n", __func__);
  954. return -EINVAL;
  955. }
  956. if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
  957. fe_volume_control[0].private_value = rtd->dai_link->id;
  958. pr_debug("Registering new mixer ctl %s",
  959. fe_volume_control[0].name);
  960. snd_soc_add_platform_controls(rtd->platform, fe_volume_control,
  961. ARRAY_SIZE(fe_volume_control));
  962. }
  963. return 0;
  964. }
  965. static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd)
  966. {
  967. int rc;
  968. rc = msm_transcode_stream_cmd_control(rtd);
  969. if (rc)
  970. pr_err("%s: ADSP Stream Cmd Control open failed\n", __func__);
  971. rc = msm_transcode_stream_callback_control(rtd);
  972. if (rc)
  973. pr_err("%s: ADSP Stream callback Control open failed\n",
  974. __func__);
  975. rc = msm_transcode_add_ion_fd_cmd_control(rtd);
  976. if (rc)
  977. pr_err("%s: Could not add transcode ion fd Control\n",
  978. __func__);
  979. rc = msm_transcode_add_event_ack_cmd_control(rtd);
  980. if (rc)
  981. pr_err("%s: Could not add transcode event ack Control\n",
  982. __func__);
  983. rc = msm_transcode_add_app_type_cfg_control(rtd);
  984. if (rc)
  985. pr_err("%s: Could not add Compr App Type Cfg Control\n",
  986. __func__);
  987. rc = msm_transcode_add_volume_control(rtd);
  988. if (rc)
  989. pr_err("%s: Could not add transcode volume Control\n",
  990. __func__);
  991. return 0;
  992. }
  993. static struct snd_compr_ops msm_transcode_loopback_ops = {
  994. .open = msm_transcode_loopback_open,
  995. .free = msm_transcode_loopback_free,
  996. .trigger = msm_transcode_loopback_trigger,
  997. .set_params = msm_transcode_loopback_set_params,
  998. .get_caps = msm_transcode_loopback_get_caps,
  999. };
  1000. static int msm_transcode_loopback_probe(struct snd_soc_platform *platform)
  1001. {
  1002. struct trans_loopback_pdata *pdata = NULL;
  1003. pr_debug("%s\n", __func__);
  1004. pdata = (struct trans_loopback_pdata *)
  1005. kzalloc(sizeof(struct trans_loopback_pdata),
  1006. GFP_KERNEL);
  1007. if (!pdata)
  1008. return -ENOMEM;
  1009. snd_soc_platform_set_drvdata(platform, pdata);
  1010. return 0;
  1011. }
  1012. static int msm_transcode_loopback_remove(struct snd_soc_platform *platform)
  1013. {
  1014. struct trans_loopback_pdata *pdata = NULL;
  1015. pdata = (struct trans_loopback_pdata *)
  1016. snd_soc_platform_get_drvdata(platform);
  1017. kfree(pdata);
  1018. return 0;
  1019. }
  1020. static struct snd_soc_platform_driver msm_soc_platform = {
  1021. .probe = msm_transcode_loopback_probe,
  1022. .compr_ops = &msm_transcode_loopback_ops,
  1023. .pcm_new = msm_transcode_loopback_new,
  1024. .remove = msm_transcode_loopback_remove,
  1025. };
  1026. static int msm_transcode_dev_probe(struct platform_device *pdev)
  1027. {
  1028. pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
  1029. if (pdev->dev.of_node)
  1030. dev_set_name(&pdev->dev, "%s", "msm-transcode-loopback");
  1031. return snd_soc_register_platform(&pdev->dev,
  1032. &msm_soc_platform);
  1033. }
  1034. static int msm_transcode_remove(struct platform_device *pdev)
  1035. {
  1036. snd_soc_unregister_platform(&pdev->dev);
  1037. return 0;
  1038. }
  1039. static const struct of_device_id msm_transcode_loopback_dt_match[] = {
  1040. {.compatible = "qcom,msm-transcode-loopback"},
  1041. {}
  1042. };
  1043. MODULE_DEVICE_TABLE(of, msm_transcode_loopback_dt_match);
  1044. static struct platform_driver msm_transcode_loopback_driver = {
  1045. .driver = {
  1046. .name = "msm-transcode-loopback",
  1047. .owner = THIS_MODULE,
  1048. .of_match_table = msm_transcode_loopback_dt_match,
  1049. },
  1050. .probe = msm_transcode_dev_probe,
  1051. .remove = msm_transcode_remove,
  1052. };
  1053. int __init msm_transcode_loopback_init(void)
  1054. {
  1055. memset(&transcode_info, 0, sizeof(struct msm_transcode_loopback));
  1056. mutex_init(&transcode_info.lock);
  1057. return platform_driver_register(&msm_transcode_loopback_driver);
  1058. }
  1059. void msm_transcode_loopback_exit(void)
  1060. {
  1061. mutex_destroy(&transcode_info.lock);
  1062. platform_driver_unregister(&msm_transcode_loopback_driver);
  1063. }
  1064. MODULE_DESCRIPTION("Transcode loopback platform driver");
  1065. MODULE_LICENSE("GPL v2");