oxfw-stream.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. /*
  2. * oxfw_stream.c - a part of driver for OXFW970/971 based devices
  3. *
  4. * Copyright (c) 2014 Takashi Sakamoto
  5. *
  6. * Licensed under the terms of the GNU General Public License, version 2.
  7. */
  8. #include "oxfw.h"
  9. #include <linux/delay.h>
  10. #define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
  11. #define CALLBACK_TIMEOUT 200
  12. /*
  13. * According to datasheet of Oxford Semiconductor:
  14. * OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O
  15. * OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O
  16. */
  17. static const unsigned int oxfw_rate_table[] = {
  18. [0] = 32000,
  19. [1] = 44100,
  20. [2] = 48000,
  21. [3] = 88200,
  22. [4] = 96000,
  23. [5] = 192000,
  24. };
  25. /*
  26. * See Table 5.7 – Sampling frequency for Multi-bit Audio
  27. * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
  28. */
  29. static const unsigned int avc_stream_rate_table[] = {
  30. [0] = 0x02,
  31. [1] = 0x03,
  32. [2] = 0x04,
  33. [3] = 0x0a,
  34. [4] = 0x05,
  35. [5] = 0x07,
  36. };
  37. static int set_rate(struct snd_oxfw *oxfw, unsigned int rate)
  38. {
  39. int err;
  40. err = avc_general_set_sig_fmt(oxfw->unit, rate,
  41. AVC_GENERAL_PLUG_DIR_IN, 0);
  42. if (err < 0)
  43. goto end;
  44. if (oxfw->has_output)
  45. err = avc_general_set_sig_fmt(oxfw->unit, rate,
  46. AVC_GENERAL_PLUG_DIR_OUT, 0);
  47. end:
  48. return err;
  49. }
  50. static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
  51. unsigned int rate, unsigned int pcm_channels)
  52. {
  53. u8 **formats;
  54. struct snd_oxfw_stream_formation formation;
  55. enum avc_general_plug_dir dir;
  56. unsigned int len;
  57. int i, err;
  58. if (s == &oxfw->tx_stream) {
  59. formats = oxfw->tx_stream_formats;
  60. dir = AVC_GENERAL_PLUG_DIR_OUT;
  61. } else {
  62. formats = oxfw->rx_stream_formats;
  63. dir = AVC_GENERAL_PLUG_DIR_IN;
  64. }
  65. /* Seek stream format for requirements. */
  66. for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
  67. err = snd_oxfw_stream_parse_format(formats[i], &formation);
  68. if (err < 0)
  69. return err;
  70. if ((formation.rate == rate) && (formation.pcm == pcm_channels))
  71. break;
  72. }
  73. if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
  74. return -EINVAL;
  75. /* If assumed, just change rate. */
  76. if (oxfw->assumed)
  77. return set_rate(oxfw, rate);
  78. /* Calculate format length. */
  79. len = 5 + formats[i][4] * 2;
  80. err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len);
  81. if (err < 0)
  82. return err;
  83. /* Some requests just after changing format causes freezing. */
  84. msleep(100);
  85. return 0;
  86. }
  87. static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
  88. {
  89. amdtp_stream_pcm_abort(stream);
  90. amdtp_stream_stop(stream);
  91. if (stream == &oxfw->tx_stream)
  92. cmp_connection_break(&oxfw->out_conn);
  93. else
  94. cmp_connection_break(&oxfw->in_conn);
  95. }
  96. static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
  97. unsigned int rate, unsigned int pcm_channels)
  98. {
  99. u8 **formats;
  100. struct cmp_connection *conn;
  101. struct snd_oxfw_stream_formation formation;
  102. unsigned int i, midi_ports;
  103. int err;
  104. if (stream == &oxfw->rx_stream) {
  105. formats = oxfw->rx_stream_formats;
  106. conn = &oxfw->in_conn;
  107. } else {
  108. formats = oxfw->tx_stream_formats;
  109. conn = &oxfw->out_conn;
  110. }
  111. /* Get stream format */
  112. for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
  113. if (formats[i] == NULL)
  114. break;
  115. err = snd_oxfw_stream_parse_format(formats[i], &formation);
  116. if (err < 0)
  117. goto end;
  118. if (rate != formation.rate)
  119. continue;
  120. if (pcm_channels == 0 || pcm_channels == formation.pcm)
  121. break;
  122. }
  123. if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
  124. err = -EINVAL;
  125. goto end;
  126. }
  127. pcm_channels = formation.pcm;
  128. midi_ports = DIV_ROUND_UP(formation.midi, 8);
  129. /* The stream should have one pcm channels at least */
  130. if (pcm_channels == 0) {
  131. err = -EINVAL;
  132. goto end;
  133. }
  134. amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
  135. err = cmp_connection_establish(conn,
  136. amdtp_stream_get_max_payload(stream));
  137. if (err < 0)
  138. goto end;
  139. err = amdtp_stream_start(stream,
  140. conn->resources.channel,
  141. conn->speed);
  142. if (err < 0) {
  143. cmp_connection_break(conn);
  144. goto end;
  145. }
  146. /* Wait first packet */
  147. if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
  148. stop_stream(oxfw, stream);
  149. err = -ETIMEDOUT;
  150. }
  151. end:
  152. return err;
  153. }
  154. static int check_connection_used_by_others(struct snd_oxfw *oxfw,
  155. struct amdtp_stream *stream)
  156. {
  157. struct cmp_connection *conn;
  158. bool used;
  159. int err;
  160. if (stream == &oxfw->tx_stream)
  161. conn = &oxfw->out_conn;
  162. else
  163. conn = &oxfw->in_conn;
  164. err = cmp_connection_check_used(conn, &used);
  165. if ((err >= 0) && used && !amdtp_stream_running(stream)) {
  166. dev_err(&oxfw->unit->device,
  167. "Connection established by others: %cPCR[%d]\n",
  168. (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
  169. conn->pcr_index);
  170. err = -EBUSY;
  171. }
  172. return err;
  173. }
  174. int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
  175. struct amdtp_stream *stream)
  176. {
  177. struct cmp_connection *conn;
  178. enum cmp_direction c_dir;
  179. enum amdtp_stream_direction s_dir;
  180. int err;
  181. if (stream == &oxfw->tx_stream) {
  182. conn = &oxfw->out_conn;
  183. c_dir = CMP_OUTPUT;
  184. s_dir = AMDTP_IN_STREAM;
  185. } else {
  186. conn = &oxfw->in_conn;
  187. c_dir = CMP_INPUT;
  188. s_dir = AMDTP_OUT_STREAM;
  189. }
  190. err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
  191. if (err < 0)
  192. goto end;
  193. err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
  194. if (err < 0) {
  195. amdtp_stream_destroy(stream);
  196. cmp_connection_destroy(conn);
  197. goto end;
  198. }
  199. /* OXFW starts to transmit packets with non-zero dbc. */
  200. if (stream == &oxfw->tx_stream)
  201. oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
  202. end:
  203. return err;
  204. }
  205. int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
  206. struct amdtp_stream *stream,
  207. unsigned int rate, unsigned int pcm_channels)
  208. {
  209. struct amdtp_stream *opposite;
  210. struct snd_oxfw_stream_formation formation;
  211. enum avc_general_plug_dir dir;
  212. unsigned int substreams, opposite_substreams;
  213. int err = 0;
  214. if (stream == &oxfw->tx_stream) {
  215. substreams = oxfw->capture_substreams;
  216. opposite = &oxfw->rx_stream;
  217. opposite_substreams = oxfw->playback_substreams;
  218. dir = AVC_GENERAL_PLUG_DIR_OUT;
  219. } else {
  220. substreams = oxfw->playback_substreams;
  221. opposite_substreams = oxfw->capture_substreams;
  222. if (oxfw->has_output)
  223. opposite = &oxfw->rx_stream;
  224. else
  225. opposite = NULL;
  226. dir = AVC_GENERAL_PLUG_DIR_IN;
  227. }
  228. if (substreams == 0)
  229. goto end;
  230. /*
  231. * Considering JACK/FFADO streaming:
  232. * TODO: This can be removed hwdep functionality becomes popular.
  233. */
  234. err = check_connection_used_by_others(oxfw, stream);
  235. if (err < 0)
  236. goto end;
  237. /* packet queueing error */
  238. if (amdtp_streaming_error(stream))
  239. stop_stream(oxfw, stream);
  240. err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
  241. if (err < 0)
  242. goto end;
  243. if (rate == 0)
  244. rate = formation.rate;
  245. if (pcm_channels == 0)
  246. pcm_channels = formation.pcm;
  247. if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
  248. if (opposite != NULL) {
  249. err = check_connection_used_by_others(oxfw, opposite);
  250. if (err < 0)
  251. goto end;
  252. stop_stream(oxfw, opposite);
  253. }
  254. stop_stream(oxfw, stream);
  255. err = set_stream_format(oxfw, stream, rate, pcm_channels);
  256. if (err < 0) {
  257. dev_err(&oxfw->unit->device,
  258. "fail to set stream format: %d\n", err);
  259. goto end;
  260. }
  261. /* Start opposite stream if needed. */
  262. if (opposite && !amdtp_stream_running(opposite) &&
  263. (opposite_substreams > 0)) {
  264. err = start_stream(oxfw, opposite, rate, 0);
  265. if (err < 0) {
  266. dev_err(&oxfw->unit->device,
  267. "fail to restart stream: %d\n", err);
  268. goto end;
  269. }
  270. }
  271. }
  272. /* Start requested stream. */
  273. if (!amdtp_stream_running(stream)) {
  274. err = start_stream(oxfw, stream, rate, pcm_channels);
  275. if (err < 0)
  276. dev_err(&oxfw->unit->device,
  277. "fail to start stream: %d\n", err);
  278. }
  279. end:
  280. return err;
  281. }
  282. void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
  283. struct amdtp_stream *stream)
  284. {
  285. if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
  286. ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
  287. return;
  288. stop_stream(oxfw, stream);
  289. }
  290. /*
  291. * This function should be called before starting the stream or after stopping
  292. * the streams.
  293. */
  294. void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
  295. struct amdtp_stream *stream)
  296. {
  297. struct cmp_connection *conn;
  298. if (stream == &oxfw->tx_stream)
  299. conn = &oxfw->out_conn;
  300. else
  301. conn = &oxfw->in_conn;
  302. amdtp_stream_destroy(stream);
  303. cmp_connection_destroy(conn);
  304. }
  305. void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
  306. struct amdtp_stream *stream)
  307. {
  308. struct cmp_connection *conn;
  309. if (stream == &oxfw->tx_stream)
  310. conn = &oxfw->out_conn;
  311. else
  312. conn = &oxfw->in_conn;
  313. if (cmp_connection_update(conn) < 0)
  314. stop_stream(oxfw, stream);
  315. else
  316. amdtp_stream_update(stream);
  317. }
  318. int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
  319. enum avc_general_plug_dir dir,
  320. struct snd_oxfw_stream_formation *formation)
  321. {
  322. u8 *format;
  323. unsigned int len;
  324. int err;
  325. len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
  326. format = kmalloc(len, GFP_KERNEL);
  327. if (format == NULL)
  328. return -ENOMEM;
  329. err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len);
  330. if (err < 0)
  331. goto end;
  332. if (len < 3) {
  333. err = -EIO;
  334. goto end;
  335. }
  336. err = snd_oxfw_stream_parse_format(format, formation);
  337. end:
  338. kfree(format);
  339. return err;
  340. }
  341. /*
  342. * See Table 6.16 - AM824 Stream Format
  343. * Figure 6.19 - format_information field for AM824 Compound
  344. * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA)
  345. * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
  346. */
  347. int snd_oxfw_stream_parse_format(u8 *format,
  348. struct snd_oxfw_stream_formation *formation)
  349. {
  350. unsigned int i, e, channels, type;
  351. memset(formation, 0, sizeof(struct snd_oxfw_stream_formation));
  352. /*
  353. * this module can support a hierarchy combination that:
  354. * Root: Audio and Music (0x90)
  355. * Level 1: AM824 Compound (0x40)
  356. */
  357. if ((format[0] != 0x90) || (format[1] != 0x40))
  358. return -ENOSYS;
  359. /* check the sampling rate */
  360. for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) {
  361. if (format[2] == avc_stream_rate_table[i])
  362. break;
  363. }
  364. if (i == ARRAY_SIZE(avc_stream_rate_table))
  365. return -ENOSYS;
  366. formation->rate = oxfw_rate_table[i];
  367. for (e = 0; e < format[4]; e++) {
  368. channels = format[5 + e * 2];
  369. type = format[6 + e * 2];
  370. switch (type) {
  371. /* IEC 60958 Conformant, currently handled as MBLA */
  372. case 0x00:
  373. /* Multi Bit Linear Audio (Raw) */
  374. case 0x06:
  375. formation->pcm += channels;
  376. break;
  377. /* MIDI Conformant */
  378. case 0x0d:
  379. formation->midi = channels;
  380. break;
  381. /* IEC 61937-3 to 7 */
  382. case 0x01:
  383. case 0x02:
  384. case 0x03:
  385. case 0x04:
  386. case 0x05:
  387. /* Multi Bit Linear Audio */
  388. case 0x07: /* DVD-Audio */
  389. case 0x0c: /* High Precision */
  390. /* One Bit Audio */
  391. case 0x08: /* (Plain) Raw */
  392. case 0x09: /* (Plain) SACD */
  393. case 0x0a: /* (Encoded) Raw */
  394. case 0x0b: /* (Encoded) SACD */
  395. /* SMPTE Time-Code conformant */
  396. case 0x0e:
  397. /* Sample Count */
  398. case 0x0f:
  399. /* Anciliary Data */
  400. case 0x10:
  401. /* Synchronization Stream (Stereo Raw audio) */
  402. case 0x40:
  403. /* Don't care */
  404. case 0xff:
  405. default:
  406. return -ENOSYS; /* not supported */
  407. }
  408. }
  409. if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
  410. formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
  411. return -ENOSYS;
  412. return 0;
  413. }
  414. static int
  415. assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir,
  416. unsigned int pid, u8 *buf, unsigned int *len,
  417. u8 **formats)
  418. {
  419. struct snd_oxfw_stream_formation formation;
  420. unsigned int i, eid;
  421. int err;
  422. /* get format at current sampling rate */
  423. err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len);
  424. if (err < 0) {
  425. dev_err(&oxfw->unit->device,
  426. "fail to get current stream format for isoc %s plug %d:%d\n",
  427. (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
  428. pid, err);
  429. goto end;
  430. }
  431. /* parse and set stream format */
  432. eid = 0;
  433. err = snd_oxfw_stream_parse_format(buf, &formation);
  434. if (err < 0)
  435. goto end;
  436. formats[eid] = kmalloc(*len, GFP_KERNEL);
  437. if (formats[eid] == NULL) {
  438. err = -ENOMEM;
  439. goto end;
  440. }
  441. memcpy(formats[eid], buf, *len);
  442. /* apply the format for each available sampling rate */
  443. for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) {
  444. if (formation.rate == oxfw_rate_table[i])
  445. continue;
  446. err = avc_general_inquiry_sig_fmt(oxfw->unit,
  447. oxfw_rate_table[i],
  448. dir, pid);
  449. if (err < 0)
  450. continue;
  451. eid++;
  452. formats[eid] = kmalloc(*len, GFP_KERNEL);
  453. if (formats[eid] == NULL) {
  454. err = -ENOMEM;
  455. goto end;
  456. }
  457. memcpy(formats[eid], buf, *len);
  458. formats[eid][2] = avc_stream_rate_table[i];
  459. }
  460. err = 0;
  461. oxfw->assumed = true;
  462. end:
  463. return err;
  464. }
  465. static int fill_stream_formats(struct snd_oxfw *oxfw,
  466. enum avc_general_plug_dir dir,
  467. unsigned short pid)
  468. {
  469. u8 *buf, **formats;
  470. unsigned int len, eid = 0;
  471. struct snd_oxfw_stream_formation dummy;
  472. int err;
  473. buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL);
  474. if (buf == NULL)
  475. return -ENOMEM;
  476. if (dir == AVC_GENERAL_PLUG_DIR_OUT)
  477. formats = oxfw->tx_stream_formats;
  478. else
  479. formats = oxfw->rx_stream_formats;
  480. /* get first entry */
  481. len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
  482. err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0);
  483. if (err == -ENOSYS) {
  484. /* LIST subfunction is not implemented */
  485. len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
  486. err = assume_stream_formats(oxfw, dir, pid, buf, &len,
  487. formats);
  488. goto end;
  489. } else if (err < 0) {
  490. dev_err(&oxfw->unit->device,
  491. "fail to get stream format %d for isoc %s plug %d:%d\n",
  492. eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out",
  493. pid, err);
  494. goto end;
  495. }
  496. /* LIST subfunction is implemented */
  497. while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) {
  498. /* The format is too short. */
  499. if (len < 3) {
  500. err = -EIO;
  501. break;
  502. }
  503. /* parse and set stream format */
  504. err = snd_oxfw_stream_parse_format(buf, &dummy);
  505. if (err < 0)
  506. break;
  507. formats[eid] = kmalloc(len, GFP_KERNEL);
  508. if (formats[eid] == NULL) {
  509. err = -ENOMEM;
  510. break;
  511. }
  512. memcpy(formats[eid], buf, len);
  513. /* get next entry */
  514. len = AVC_GENERIC_FRAME_MAXIMUM_BYTES;
  515. err = avc_stream_get_format_list(oxfw->unit, dir, 0,
  516. buf, &len, ++eid);
  517. /* No entries remained. */
  518. if (err == -EINVAL) {
  519. err = 0;
  520. break;
  521. } else if (err < 0) {
  522. dev_err(&oxfw->unit->device,
  523. "fail to get stream format %d for isoc %s plug %d:%d\n",
  524. eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" :
  525. "out",
  526. pid, err);
  527. break;
  528. }
  529. }
  530. end:
  531. kfree(buf);
  532. return err;
  533. }
  534. int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
  535. {
  536. u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
  537. int err;
  538. /* the number of plugs for isoc in/out, ext in/out */
  539. err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs);
  540. if (err < 0) {
  541. dev_err(&oxfw->unit->device,
  542. "fail to get info for isoc/external in/out plugs: %d\n",
  543. err);
  544. goto end;
  545. } else if ((plugs[0] == 0) && (plugs[1] == 0)) {
  546. err = -ENOSYS;
  547. goto end;
  548. }
  549. /* use oPCR[0] if exists */
  550. if (plugs[1] > 0) {
  551. err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
  552. if (err < 0)
  553. goto end;
  554. oxfw->has_output = true;
  555. }
  556. /* use iPCR[0] if exists */
  557. if (plugs[0] > 0)
  558. err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
  559. end:
  560. return err;
  561. }
  562. void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw)
  563. {
  564. oxfw->dev_lock_changed = true;
  565. wake_up(&oxfw->hwdep_wait);
  566. }
  567. int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw)
  568. {
  569. int err;
  570. spin_lock_irq(&oxfw->lock);
  571. /* user land lock this */
  572. if (oxfw->dev_lock_count < 0) {
  573. err = -EBUSY;
  574. goto end;
  575. }
  576. /* this is the first time */
  577. if (oxfw->dev_lock_count++ == 0)
  578. snd_oxfw_stream_lock_changed(oxfw);
  579. err = 0;
  580. end:
  581. spin_unlock_irq(&oxfw->lock);
  582. return err;
  583. }
  584. void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw)
  585. {
  586. spin_lock_irq(&oxfw->lock);
  587. if (WARN_ON(oxfw->dev_lock_count <= 0))
  588. goto end;
  589. if (--oxfw->dev_lock_count == 0)
  590. snd_oxfw_stream_lock_changed(oxfw);
  591. end:
  592. spin_unlock_irq(&oxfw->lock);
  593. }