spdif_parser.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /* --------------------------------------------------------
  2. SPDIF parser for dolby digital plus data
  3. Date: 2014-10-30
  4. Author: jimmy.lai@s2-tek.com
  5. ---------------------------------------------------------- */
  6. #include "spdif_parser.h"
  7. #include <linux/kernel.h> // abs(), max()
  8. #include <linux/string.h>
  9. /* ----- Local defines for debug --------------------------------- */
  10. #define SPDIF_DD_PARSER_DEBUG 0
  11. #if SPDIF_DD_PARSER_DEBUG
  12. #define dbg(fmt,args...) printk(fmt, ## args)
  13. #else
  14. #define dbg(fmt,args...)
  15. #endif
  16. static void dump(const unsigned char *ptr, int size)
  17. {
  18. #if SPDIF_DD_PARSER_DEBUG
  19. int i = 0, n = 0;
  20. char str[3 * 0x10 + 8];
  21. for (i = 0; i < size ; i++)
  22. {
  23. if (n >= 0)
  24. {
  25. n += sprintf(&str[n], "%02x ", ptr[i]);
  26. }
  27. if (n >= 3 * 0x10 || i + 1 == size)
  28. {
  29. n = 0;
  30. dbg("%s\n", str);
  31. }
  32. }
  33. #endif
  34. }
  35. /* ----- Local structures and defines ---------------------------- */
  36. typedef struct __SPDIF_PARSER
  37. {
  38. /* source buffer info */
  39. #define IEC61937_DDP_BUFSIZE (6144*2*2)
  40. BOOL bFound;
  41. UINT32 parsed_sz;
  42. UINT32 spdif_header_sz;
  43. UINT8 spdif_header[8];
  44. /* destination buffer info */
  45. #define MAX_CMDBUF_BUFSIZE (4*1024)
  46. #define CMDBUF_NUMBER (16)
  47. UINT32 idx;
  48. UINT32 offset;
  49. UINT32 cmdbufsz;
  50. UINT32 pts;
  51. UINT8 *cmdbuf[CMDBUF_NUMBER];
  52. UINT8 *curbuf;
  53. /* fire call back function */
  54. FireCallBack fire;
  55. void *fire_param;
  56. } SPDIF_PARSER;
  57. /* ----- static functions ---------------------------------------- */
  58. static SPDIF_PARSER gSpdifParser;
  59. static SPDIF_PARSER *get_spdif_parser(void)
  60. {
  61. return &gSpdifParser;
  62. }
  63. static UINT32 spdif_copy(UINT8 *dst, UINT8 *src, UINT32 srcsz)
  64. {
  65. UINT32 *s = (UINT32*)src;
  66. UINT32 i, d;
  67. for (i = 0, d = 0; i < srcsz / 4; i++)
  68. {
  69. dst[d++] = (s[i] >> 24) & 0xff;
  70. dst[d++] = (s[i] >> 16) & 0xff;
  71. }
  72. return d;
  73. }
  74. static UINT8 *find_spdif_header(UINT8 *inbuf, UINT32 inbufsz)
  75. {
  76. UINT32 i;
  77. UINT32 *in = (UINT32*)inbuf;
  78. if((inbufsz/4) == 0)
  79. {
  80. printk("inbufsz %d,skip find spdif header\n", inbufsz);
  81. return NULL;
  82. }
  83. for (i = 0; i < inbufsz / 4 - 1; i += 2)
  84. {
  85. if ((in[i] >> 16) == 0xf872 && (in[i + 1] >> 16) == 0x4e1f)
  86. {
  87. return inbuf + i * 4;
  88. }
  89. }
  90. return NULL;
  91. }
  92. /* ----- global functions ---------------------------------------- */
  93. void hdmi_spdif_parser_handler(UINT8 *inbuf, UINT32 inbufsz, UINT32 timestamp)
  94. {
  95. SPDIF_PARSER *p = get_spdif_parser();
  96. UINT8 *src;
  97. UINT32 leftsz;
  98. src = inbuf;
  99. leftsz = inbufsz;
  100. parse_again:
  101. if (p->bFound == FALSE)
  102. {
  103. src = find_spdif_header(src, leftsz);
  104. if (src)
  105. {
  106. p->bFound = TRUE;
  107. p->offset = 0;
  108. p->spdif_header_sz = 0;
  109. p->cmdbufsz = 0;
  110. p->parsed_sz = 0;
  111. leftsz = inbufsz - (UINT32)(src - inbuf);
  112. dbg("head(%d): spdif header found\n", src - inbuf);
  113. }
  114. else
  115. {
  116. dbg("head(%d): spdif header not found\n", inbufsz);
  117. return;
  118. }
  119. }
  120. /* spdif header is found */
  121. if (p->parsed_sz < 8)
  122. {
  123. /* parse spdif header : pa pb pc pd */
  124. if (p->spdif_header_sz < 8)
  125. {
  126. UINT32 needsz, copysz;
  127. needsz = 16 - p->spdif_header_sz * 2;
  128. copysz = (leftsz < needsz) ? leftsz : needsz;
  129. p->spdif_header_sz += spdif_copy(p->spdif_header + p->spdif_header_sz, src, copysz);
  130. p->parsed_sz = p->spdif_header_sz;
  131. dbg("head(%d): %d (needsz %d leftsz %d) parsed %d\n", src - inbuf, copysz, needsz, leftsz, p->parsed_sz);
  132. src += copysz;
  133. leftsz -= copysz;
  134. if (p->spdif_header_sz == 8)
  135. {
  136. /* check header and data type is dd */
  137. if ((*(UINT32*)p->spdif_header) != 0x1f4e72f8 ||
  138. p->spdif_header[5] != 21)
  139. {
  140. dbg("head(%d): invalid spdif header\n", src - inbuf);
  141. dump(p->spdif_header, 8);
  142. p->bFound = FALSE;
  143. if (leftsz > 0)
  144. {
  145. goto parse_again;
  146. }
  147. else
  148. {
  149. printk("leftsz=%d, copysz=%d, needsz= %d, skip \n" ,leftsz,copysz,needsz);
  150. return;
  151. }
  152. }
  153. /* Note: ddp frame size is indicated in bytes */
  154. p->cmdbufsz = ((p->spdif_header[6] << 8) | p->spdif_header[7]);
  155. p->pts = timestamp;
  156. /* Reserve continuous buffers for big data */
  157. if (p->cmdbufsz + 8 > MAX_CMDBUF_BUFSIZE)
  158. {
  159. UINT32 cnt = (p->cmdbufsz + 8) / MAX_CMDBUF_BUFSIZE;
  160. if (p->idx + cnt >= CMDBUF_NUMBER)
  161. {
  162. p->idx = 0;
  163. p->curbuf = p->cmdbuf[p->idx];
  164. }
  165. }
  166. #if DDPFRMAE_CONTAINS_SPDIF_HEADER
  167. memcpy(p->curbuf, p->spdif_header, 8);
  168. #endif
  169. dbg("head(%d): valid spdif header\n", src - inbuf);
  170. dump(p->spdif_header, 8);
  171. }
  172. }
  173. }
  174. /* parse spdif payload (nonlinear and zero stuffings) */
  175. if (p->parsed_sz >= 8)
  176. {
  177. /* parse nonlinear part */
  178. if (p->offset < p->cmdbufsz)
  179. {
  180. UINT32 needsz, copysz;
  181. needsz = 2 * (p->cmdbufsz - p->offset);
  182. copysz = (leftsz < needsz) ? leftsz : needsz;
  183. #if DDPFRMAE_CONTAINS_SPDIF_HEADER
  184. spdif_copy(p->curbuf + 8 + p->offset, src, copysz);
  185. #else
  186. spdif_copy(p->curbuf + p->offset, src, copysz);
  187. #endif
  188. p->parsed_sz += (copysz / 2);
  189. dbg("copy(%d): %d (needsz %d leftsz %d) parsed %d\n", src - inbuf, copysz, needsz, leftsz, p->parsed_sz);
  190. src += copysz;
  191. leftsz -= copysz;
  192. p->offset += (copysz / 2);
  193. }
  194. /* skip zero stuffing */
  195. if (p->offset == p->cmdbufsz && p->parsed_sz < IEC61937_DDP_BUFSIZE)
  196. {
  197. UINT32 needsz, skipsz;
  198. needsz = 2 * (IEC61937_DDP_BUFSIZE - p->parsed_sz);
  199. skipsz = (leftsz < needsz) ? leftsz : needsz;
  200. p->parsed_sz += (skipsz / 2);
  201. dbg("skip(%d): %d (needsz %d leftsz %d) parsed %d\n", src - inbuf, skipsz, needsz, leftsz, p->parsed_sz);
  202. src += skipsz;
  203. leftsz -= skipsz;
  204. }
  205. }
  206. /* parse done */
  207. if (p->parsed_sz >= IEC61937_DDP_BUFSIZE)
  208. {
  209. /* fire command queue */
  210. dbg("done(%d): %d fire dd frame\n", src - inbuf, p->cmdbufsz);
  211. dump(p->curbuf, 0x10);
  212. #if DDPFRMAE_CONTAINS_SPDIF_HEADER
  213. p->fire(p->fire_param, p->curbuf, p->cmdbufsz + 8, p->pts);
  214. #else
  215. p->fire(p->fire_param, p->curbuf, p->cmdbufsz, p->pts);
  216. #endif
  217. /* update target buffer */
  218. p->idx = (p->idx + 1) % CMDBUF_NUMBER;
  219. if (p->cmdbufsz + 8 > MAX_CMDBUF_BUFSIZE)
  220. {
  221. UINT32 cnt = (p->cmdbufsz + 8) / MAX_CMDBUF_BUFSIZE;
  222. p->idx = (p->idx + cnt) % CMDBUF_NUMBER;
  223. }
  224. p->curbuf = p->cmdbuf[p->idx];
  225. p->offset = 0;
  226. p->cmdbufsz = 0;
  227. p->spdif_header_sz = 0;
  228. p->parsed_sz = 0;
  229. }
  230. if (leftsz > 0)
  231. {
  232. goto parse_again;
  233. }
  234. }
  235. void hdmi_spdif_parser_reset(void)
  236. {
  237. SPDIF_PARSER *p = get_spdif_parser();
  238. p->bFound = FALSE;
  239. p->parsed_sz = 0;
  240. p->spdif_header_sz = 0;
  241. p->idx = 0;
  242. p->offset = 0;
  243. p->cmdbufsz = 0;
  244. p->curbuf = p->cmdbuf[p->idx];
  245. }
  246. void hdmi_spdif_parser_init(UINT8 *cmdbuf, FireCallBack callback, void *cb_param)
  247. {
  248. UINT32 i;
  249. SPDIF_PARSER *p = get_spdif_parser();
  250. memset(p, 0, sizeof(SPDIF_PARSER));
  251. for (i = 0; i < CMDBUF_NUMBER; i++)
  252. {
  253. p->cmdbuf[i] = cmdbuf + (i * MAX_CMDBUF_BUFSIZE);
  254. }
  255. p->fire = callback;
  256. p->fire_param = cb_param;
  257. hdmi_spdif_parser_reset();
  258. }