blob: 58d2788c95665009b252c9644d73a0f014de7b67
1 | /************************************************************************************* |
2 | * @file spdif_api.c |
3 | * @brief |
4 | * Simple SPDIF test tools |
5 | * Detailed description starts here. |
6 | * |
7 | * @author jian.xu jian.xu@amlogic.com |
8 | * |
9 | * @internal |
10 | * Created 2012-4-19 |
11 | * Revision v1.0 |
12 | * Compiler gcc/g++ |
13 | * Company Amlogic Inc. |
14 | * Copyright Copyright (c) 2011, |
15 | * |
16 | * This source code is released for free distribution under the terms of the |
17 | * GNU General Public License as published by the Free Software Foundation. |
18 | ************************************************************************************* |
19 | */ |
20 | #include <stdio.h> |
21 | #include <unistd.h> |
22 | #include <stdlib.h> |
23 | #include <errno.h> |
24 | #include <sys/types.h> |
25 | #include <sys/stat.h> |
26 | #include <sys/ioctl.h> |
27 | #include <signal.h> |
28 | #include <fcntl.h> |
29 | #include <string.h> |
30 | #include <sys/mman.h> |
31 | #include "spdif_api.h" |
32 | #include <log-print.h> |
33 | #define AUDIO_SPDIF_DEV_NAME "/dev/audio_spdif" |
34 | |
35 | static unsigned hw_rd_offset = 0; |
36 | static unsigned wr_offset = 0; |
37 | static unsigned iec958_buffer_size = 0; |
38 | static int dev_fd = -1; |
39 | static int use_kernel_wr = 0x1; |
40 | static unsigned stream_type = STREAM_DTS; |
41 | static short iec958_buf[6144 / 2]; |
42 | static char *map_buf = (void *)-1L; |
43 | static unsigned first_write = 1; |
44 | #define IEC958_LANTENCY 20 |
45 | int iec958_init() |
46 | { |
47 | int ret = 0; |
48 | iec958_buffer_size = 0; |
49 | hw_rd_offset = 0; |
50 | wr_offset = 0; |
51 | first_write = 1; |
52 | dev_fd = open(AUDIO_SPDIF_DEV_NAME, /*O_RDONLY|O_WRONLY*/O_RDWR); |
53 | if (dev_fd < 0) { |
54 | printf("can not open %s\n", AUDIO_SPDIF_DEV_NAME); |
55 | ret = -1; |
56 | goto exit; |
57 | } |
58 | /* call 958 module init here */ |
59 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_INIT_PREPARE, 1); |
60 | /* get 958 dma buffer size */ |
61 | ioctl(dev_fd, AUDIO_SPDIF_GET_958_BUF_SIZE, &iec958_buffer_size); |
62 | //adec_print("iec958 buffer size %x\n",iec958_buffer_size); |
63 | wr_offset = hw_rd_offset + 4 * 48000 * IEC958_LANTENCY / 1000; //delay |
64 | if (wr_offset >= iec958_buffer_size) { |
65 | wr_offset = iec958_buffer_size; |
66 | } |
67 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_WR_OFFSET, &wr_offset); |
68 | /* mapping the kernel 958 dma buffer to user space to acess */ |
69 | map_buf = mmap(0, iec958_buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED/*MAP_PRIVATE*/, dev_fd, 0); |
70 | if (map_buf == (void *)-1L) { |
71 | printf("mmap failed,error num %d \n", errno); |
72 | ret = -2; |
73 | goto exit1; |
74 | } |
75 | /* enable 958 outout */ |
76 | // ioctl(dev_fd, AUDIO_SPDIF_SET_958_ENABLE,1); |
77 | return 0; |
78 | exit3: |
79 | if (map_buf != (void *)-1L) { |
80 | munmap(map_buf, iec958_buffer_size); |
81 | } |
82 | exit1: |
83 | if (dev_fd >= 0) { |
84 | close(dev_fd); |
85 | } |
86 | exit: |
87 | return ret; |
88 | } |
89 | |
90 | /* |
91 | @param in buf, the input buffer for 61937 package,to be modify |
92 | @param in frame_size,the input data len |
93 | @return , the packaged data length,maybe different than input length |
94 | */ |
95 | /* note ::: now fixed on 1.5M dts stream,maybe different */ |
96 | int iec958_pack_frame(char *buf, int frame_size) |
97 | { |
98 | |
99 | short *left, *right, *pp; |
100 | int i, j; |
101 | if (stream_type == STREAM_AC3) { |
102 | iec958_buf[0] = 0xF872; |
103 | iec958_buf[1] = 0x4E1F; |
104 | iec958_buf[2] = ((/*dd_bsmod*/0 & 7) << 8) | 1; |
105 | iec958_buf[3] = frame_size * 8; //frame_size*8;//Pd as the Length-code |
106 | memcpy((char*)iec958_buf + 8, buf, frame_size); |
107 | memset((char*)iec958_buf + frame_size + 8, 0, 6144 - frame_size - 8); |
108 | pp = (short*)iec958_buf; |
109 | left = (short*)buf; |
110 | right = left + 16; |
111 | for (j = 0; j < 6144; j += 64) { |
112 | for (i = 0; i < 16 ; i++) { |
113 | *left++ = *pp; |
114 | pp++; |
115 | *right++ = *pp; |
116 | pp++; |
117 | } |
118 | left += 16; |
119 | right += 16; |
120 | } |
121 | return 6144; |
122 | } |
123 | /* dts iec60937 package according to DTS type and block num per frame */ |
124 | else if (stream_type == STREAM_DTS) { |
125 | iec958_buf[0] = 0xF872; |
126 | iec958_buf[1] = 0x4E1F; |
127 | iec958_buf[2] = 11;//2 block per frame,DTS type I |
128 | iec958_buf[3] = 2040 * 8; //frame_size*8;//Pd as the Length-code |
129 | memcpy((char*)iec958_buf + 8, buf, frame_size); |
130 | memset((char*)iec958_buf + frame_size + 8, 0, 6144 - frame_size - 8); |
131 | pp = (short*)iec958_buf; |
132 | left = (short*)buf; |
133 | right = left + 16; |
134 | for (j = 0; j < 2048; j += 64) { |
135 | for (i = 0; i < 16 ; i++) { |
136 | *left++ = *pp; |
137 | pp++; |
138 | *right++ = *pp; |
139 | pp++; |
140 | } |
141 | left += 16; |
142 | right += 16; |
143 | } |
144 | return 2048; |
145 | |
146 | } |
147 | /*raw wave file */ |
148 | else { |
149 | memcpy(iec958_buf, buf, frame_size); |
150 | pp = (short*)iec958_buf; |
151 | left = (short*)buf; |
152 | right = left + 16; |
153 | for (j = 0; j < frame_size; j += 64) { |
154 | for (i = 0; i < 16 ; i++) { |
155 | *left++ = *pp; |
156 | pp++; |
157 | *right++ = *pp; |
158 | pp++; |
159 | } |
160 | left += 16; |
161 | right += 16; |
162 | } |
163 | return (frame_size >> 6) << 6; |
164 | } |
165 | return 0; |
166 | } |
167 | #define ALIGN 4096 |
168 | static int iec958_buf_space_size(int dev_fd) |
169 | { |
170 | int space = 0; |
171 | ioctl(dev_fd, AUDIO_SPDIF_GET_958_BUF_RD_OFFSET, &hw_rd_offset); |
172 | if (first_write == 1) { |
173 | if (hw_rd_offset >= wr_offset || (wr_offset - hw_rd_offset) < (4 * 48000 * IEC958_LANTENCY / 1000)) { |
174 | adec_print("reset iec958 hw wr ptr\n"); |
175 | wr_offset = hw_rd_offset + 4 * 48000 * IEC958_LANTENCY / 1000; //delay |
176 | if (wr_offset >= iec958_buffer_size) { |
177 | wr_offset = wr_offset - iec958_buffer_size; |
178 | } |
179 | } |
180 | first_write = 0; |
181 | } |
182 | if (wr_offset > hw_rd_offset) { |
183 | space = iec958_buffer_size + hw_rd_offset - wr_offset; |
184 | } else { |
185 | space = hw_rd_offset - wr_offset; |
186 | } |
187 | return space > ALIGN ? (space - ALIGN) : 0/*&(~4095)*/; |
188 | } |
189 | int iec958_packed_frame_write_958buf(char *buf, int frame_size) |
190 | { |
191 | int tail = 0; |
192 | int ret; |
193 | /* check if i2s enable but 958 not enable */ |
194 | int status_958 = 0; |
195 | int status_i2s = 0; |
196 | ioctl(dev_fd, AUDIO_SPDIF_GET_958_ENABLE_STATUS, &status_958); |
197 | if (status_958 == 0) { |
198 | ioctl(dev_fd, AUDIO_SPDIF_GET_I2S_ENABLE_STATUS, &status_i2s); |
199 | if (status_i2s) { |
200 | status_958 = 1; |
201 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_ENABLE, status_958); |
202 | adec_print("spdif api:enable 958 output\n"); |
203 | } else { |
204 | adec_print("discard data and wait i2s enable\n"); |
205 | return 0;//output are not enable yet,just return and wait |
206 | } |
207 | } |
208 | while (iec958_buf_space_size(dev_fd) < frame_size) { |
209 | // printf("iec958 buffer full,space size %d,write size %d\n",iec958_buf_space_size(dev_fd),frame_size); |
210 | //adec_print("iec958 buffer full,space size %d,write size %d\n",iec958_buf_space_size(dev_fd),frame_size); |
211 | //usleep(5); |
212 | return -1; |
213 | } |
214 | if (wr_offset == iec958_buffer_size) { |
215 | wr_offset = 0; |
216 | } |
217 | if (frame_size + wr_offset > iec958_buffer_size) { |
218 | tail = iec958_buffer_size - wr_offset; |
219 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_WR_OFFSET, &wr_offset); |
220 | //printf("0 tail %d,wr offset %d\n",tail,wr_offset); |
221 | if (!use_kernel_wr) { |
222 | memcpy(map_buf + wr_offset, buf, tail); |
223 | ret = msync(map_buf, iec958_buffer_size, MS_INVALIDATE | MS_SYNC); |
224 | if (ret) { |
225 | printf("msync0 err %d,error id %d addr %p\n", ret, errno, map_buf + wr_offset); |
226 | } |
227 | } else { |
228 | write(dev_fd, buf, tail); |
229 | } |
230 | wr_offset = 0; |
231 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_WR_OFFSET, &wr_offset); |
232 | //printf("1 tail %d,wr offset %d\n",frame_size-tail,wr_offset); |
233 | if (!use_kernel_wr) { |
234 | memcpy(map_buf, buf + tail, frame_size - tail); |
235 | ret = msync(map_buf, iec958_buffer_size, MS_INVALIDATE | MS_SYNC); |
236 | if (ret) { |
237 | printf("msync1 err %d,error id %d addr %p\n", ret, errno, map_buf); |
238 | } |
239 | } else { |
240 | write(dev_fd, buf + tail, frame_size - tail); |
241 | } |
242 | wr_offset = frame_size - tail; |
243 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_WR_OFFSET, &wr_offset); |
244 | |
245 | } else { |
246 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_WR_OFFSET, &wr_offset); |
247 | //printf("2 tail %d,wr offset %d\n",frame_size,wr_offset); |
248 | if (!use_kernel_wr) { |
249 | memcpy(map_buf + wr_offset, buf, frame_size); |
250 | ret = msync(map_buf + wr_offset, frame_size, MS_ASYNC | MS_INVALIDATE); |
251 | if (ret) { |
252 | printf("msync2 err %d,error id %d addr %p\n", ret, errno, map_buf + wr_offset); |
253 | } |
254 | } else { |
255 | write(dev_fd, buf, frame_size); |
256 | } |
257 | wr_offset += frame_size; |
258 | ioctl(dev_fd, AUDIO_SPDIF_SET_958_WR_OFFSET, &wr_offset); |
259 | |
260 | } |
261 | return 0; |
262 | } |
263 | int iec958buf_fill_zero() |
264 | { |
265 | unsigned int zero_filled_cnt = 0, i2s_status = 0, write_ret = 0; |
266 | char zerobuf[2048] = {0}; |
267 | ioctl(dev_fd, AUDIO_SPDIF_GET_I2S_ENABLE_STATUS, &i2s_status); |
268 | while ((zero_filled_cnt < iec958_buffer_size) && i2s_status) { |
269 | write_ret = iec958_packed_frame_write_958buf(zerobuf, 2048); |
270 | if (write_ret) { |
271 | break; |
272 | } |
273 | zero_filled_cnt += 2048; |
274 | ioctl(dev_fd, AUDIO_SPDIF_GET_I2S_ENABLE_STATUS, &i2s_status); |
275 | } |
276 | return 0; |
277 | |
278 | } |
279 | /* |
280 | @return 0:means 958 hw buffer maybe underrun,may need fill zero data |
281 | @return 1:means 958 hw buffer level is fine |
282 | */ |
283 | #define IEC958_LEVEL_THREAD 4096 |
284 | int iec958_check_958buf_level() |
285 | { |
286 | int status_958 = 0; |
287 | int hw_958_level = 0; |
288 | ioctl(dev_fd, AUDIO_SPDIF_GET_958_ENABLE_STATUS, &status_958); |
289 | if (status_958) { |
290 | hw_958_level = iec958_buffer_size - iec958_buf_space_size(dev_fd); |
291 | if (hw_958_level < IEC958_LEVEL_THREAD) { |
292 | return 0; |
293 | } |
294 | } |
295 | return 1; |
296 | } |
297 | int iec958_deinit() |
298 | { |
299 | if (map_buf != (void *)-1L) { |
300 | munmap(map_buf, iec958_buffer_size); |
301 | } |
302 | if (dev_fd >= 0) { |
303 | close(dev_fd); |
304 | } |
305 | return 0; |
306 | } |
307 |