spdif_parser.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. //printk("inbufsz=%d, p->parse_sz=%d p->offset =%d\n", inbufsz, p->parsed_sz, p->offset);
  121. /* spdif header is found */
  122. if (p->parsed_sz < 8)
  123. {
  124. /* parse spdif header : pa pb pc pd */
  125. if (p->spdif_header_sz < 8)
  126. {
  127. UINT32 needsz, copysz;
  128. needsz = 16 - p->spdif_header_sz * 2;
  129. copysz = (leftsz < needsz) ? leftsz : needsz;
  130. p->spdif_header_sz += spdif_copy(p->spdif_header + p->spdif_header_sz, src, copysz);
  131. p->parsed_sz = p->spdif_header_sz;
  132. dbg("head(%d): %d (needsz %d leftsz %d) parsed %d\n", src - inbuf, copysz, needsz, leftsz, p->parsed_sz);
  133. src += copysz;
  134. leftsz -= copysz;
  135. if (p->spdif_header_sz == 8)
  136. {
  137. /* check header and data type is dd */
  138. if ((*(UINT32*)p->spdif_header) != 0x1f4e72f8 ||
  139. p->spdif_header[5] != 21)
  140. {
  141. dbg("head(%d): invalid spdif header\n", src - inbuf);
  142. dump(p->spdif_header, 8);
  143. p->bFound = FALSE;
  144. if (leftsz > 0)
  145. {
  146. goto parse_again;
  147. }
  148. else
  149. {
  150. printk("leftsz=%d, copysz=%d, needsz= %d, skip \n" ,leftsz,copysz,needsz);
  151. return;
  152. }
  153. }
  154. /* Note: ddp frame size is indicated in bytes */
  155. p->cmdbufsz = ((p->spdif_header[6] << 8) | p->spdif_header[7]);
  156. p->pts = timestamp;
  157. /* Reserve continuous buffers for big data */
  158. if (p->cmdbufsz + 8 > MAX_CMDBUF_BUFSIZE)
  159. {
  160. UINT32 cnt = (p->cmdbufsz + 8) / MAX_CMDBUF_BUFSIZE;
  161. if (p->idx + cnt >= CMDBUF_NUMBER)
  162. {
  163. p->idx = 0;
  164. p->curbuf = p->cmdbuf[p->idx];
  165. }
  166. }
  167. #if DDPFRMAE_CONTAINS_SPDIF_HEADER
  168. memcpy(p->curbuf, p->spdif_header, 8);
  169. #endif
  170. dbg("head(%d): valid spdif header\n", src - inbuf);
  171. dump(p->spdif_header, 8);
  172. }
  173. }
  174. }
  175. /* parse spdif payload (nonlinear and zero stuffings) */
  176. if (p->parsed_sz >= 8)
  177. {
  178. /* parse nonlinear part */
  179. if (p->offset < p->cmdbufsz)
  180. {
  181. UINT32 needsz, copysz;
  182. needsz = 2 * (p->cmdbufsz - p->offset);
  183. copysz = (leftsz < needsz) ? leftsz : needsz;
  184. #if DDPFRMAE_CONTAINS_SPDIF_HEADER
  185. spdif_copy(p->curbuf + 8 + p->offset, src, copysz);
  186. #else
  187. spdif_copy(p->curbuf + p->offset, src, copysz);
  188. #endif
  189. p->parsed_sz += (copysz / 2);
  190. dbg("copy(%d): %d (needsz %d leftsz %d) parsed %d\n", src - inbuf, copysz, needsz, leftsz, p->parsed_sz);
  191. src += copysz;
  192. leftsz -= copysz;
  193. p->offset += (copysz / 2);
  194. }
  195. /* skip zero stuffing */
  196. if (p->offset == p->cmdbufsz && p->parsed_sz < IEC61937_DDP_BUFSIZE)
  197. {
  198. UINT32 needsz, skipsz;
  199. needsz = 2 * (IEC61937_DDP_BUFSIZE - p->parsed_sz);
  200. skipsz = (leftsz < needsz) ? leftsz : needsz;
  201. p->parsed_sz += (skipsz / 2);
  202. dbg("skip(%d): %d (needsz %d leftsz %d) parsed %d\n", src - inbuf, skipsz, needsz, leftsz, p->parsed_sz);
  203. src += skipsz;
  204. leftsz -= skipsz;
  205. }
  206. }
  207. /* parse done */
  208. if (p->parsed_sz >= IEC61937_DDP_BUFSIZE)
  209. {
  210. /* fire command queue */
  211. dbg("done(%d): %d fire dd frame\n", src - inbuf, p->cmdbufsz);
  212. dump(p->curbuf, 0x10);
  213. #if DDPFRMAE_CONTAINS_SPDIF_HEADER
  214. p->fire(p->fire_param, p->curbuf, p->cmdbufsz + 8, p->pts);
  215. #else
  216. p->fire(p->fire_param, p->curbuf, p->cmdbufsz, p->pts);
  217. #endif
  218. /* update target buffer */
  219. p->idx = (p->idx + 1) % CMDBUF_NUMBER;
  220. if (p->cmdbufsz + 8 > MAX_CMDBUF_BUFSIZE)
  221. {
  222. UINT32 cnt = (p->cmdbufsz + 8) / MAX_CMDBUF_BUFSIZE;
  223. p->idx = (p->idx + cnt) % CMDBUF_NUMBER;
  224. }
  225. p->curbuf = p->cmdbuf[p->idx];
  226. p->offset = 0;
  227. p->cmdbufsz = 0;
  228. p->spdif_header_sz = 0;
  229. p->parsed_sz = 0;
  230. }
  231. if (leftsz > 0)
  232. {
  233. goto parse_again;
  234. }
  235. }
  236. void hdmi_spdif_parser_reset(void)
  237. {
  238. SPDIF_PARSER *p = get_spdif_parser();
  239. p->bFound = FALSE;
  240. p->parsed_sz = 0;
  241. p->spdif_header_sz = 0;
  242. p->idx = 0;
  243. p->offset = 0;
  244. p->cmdbufsz = 0;
  245. p->curbuf = p->cmdbuf[p->idx];
  246. }
  247. void hdmi_spdif_parser_init(UINT8 *cmdbuf, FireCallBack callback, void *cb_param)
  248. {
  249. UINT32 i;
  250. SPDIF_PARSER *p = get_spdif_parser();
  251. memset(p, 0, sizeof(SPDIF_PARSER));
  252. for (i = 0; i < CMDBUF_NUMBER; i++)
  253. {
  254. p->cmdbuf[i] = cmdbuf + (i * MAX_CMDBUF_BUFSIZE);
  255. }
  256. p->fire = callback;
  257. p->fire_param = cb_param;
  258. hdmi_spdif_parser_reset();
  259. }