summaryrefslogtreecommitdiff
path: root/amadec/spdif_api.c (plain)
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
35static unsigned hw_rd_offset = 0;
36static unsigned wr_offset = 0;
37static unsigned iec958_buffer_size = 0;
38static int dev_fd = -1;
39static int use_kernel_wr = 0x1;
40static unsigned stream_type = STREAM_DTS;
41static short iec958_buf[6144 / 2];
42static char *map_buf = (void *)-1L;
43static unsigned first_write = 1;
44#define IEC958_LANTENCY 20
45int 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;
78exit3:
79 if (map_buf != (void *)-1L) {
80 munmap(map_buf, iec958_buffer_size);
81 }
82exit1:
83 if (dev_fd >= 0) {
84 close(dev_fd);
85 }
86exit:
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 */
96int 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
168static 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}
189int 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}
263int 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
284int 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}
297int 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