summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2002-05-24 12:22:14 +0000
committerBjörn Stenberg <bjorn@haxx.se>2002-05-24 12:22:14 +0000
commit1ac4600e5bf3ff937d404622158111b576651829 (patch)
tree0d2d106b3f9a44aef8a6900a4142be0cafb241cc
parentffb6a9db8e1aeef43306e5f0c83c1547bd82546b (diff)
downloadrockbox-1ac4600e5bf3ff937d404622158111b576651829.tar.gz
rockbox-1ac4600e5bf3ff937d404622158111b576651829.zip
First version
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@695 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/mpeg.c494
-rw-r--r--firmware/mpeg.h31
2 files changed, 525 insertions, 0 deletions
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
new file mode 100644
index 0000000000..5bf4fe0fd8
--- /dev/null
+++ b/firmware/mpeg.c
@@ -0,0 +1,494 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdbool.h>
20#include "i2c.h"
21#include "mas.h"
22#include "dac.h"
23#include "sh7034.h"
24#include "system.h"
25#include "debug.h"
26#include "kernel.h"
27#include "thread.h"
28#include "panic.h"
29#include "file.h"
30
31#define MPEG_STACK_SIZE 0x2000
32#define MPEG_BUFSIZE 0x100000
33#define MPEG_CHUNKSIZE 0x20000
34#define MPEG_LOW_WATER 0x30000
35
36#define MPEG_PLAY 1
37#define MPEG_STOP 2
38#define MPEG_PAUSE 3
39#define MPEG_RESUME 4
40#define MPEG_NEED_DATA 100
41
42static unsigned int bass_table[] =
43{
44 0,
45 0x800, /* 1dB */
46 0x10000, /* 2dB */
47 0x17c00, /* 3dB */
48 0x1f800, /* 4dB */
49 0x27000, /* 5dB */
50 0x2e400, /* 6dB */
51 0x35800, /* 7dB */
52 0x3c000, /* 8dB */
53 0x42800, /* 9dB */
54 0x48800, /* 10dB */
55 0x4e400, /* 11dB */
56 0x53800, /* 12dB */
57 0x58800, /* 13dB */
58 0x5d400, /* 14dB */
59 0x61800 /* 15dB */
60};
61
62static unsigned int treble_table[] =
63{
64 0,
65 0x5400, /* 1dB */
66 0xac00, /* 2dB */
67 0x10400, /* 3dB */
68 0x16000, /* 4dB */
69 0x1c000, /* 5dB */
70 0x22400, /* 6dB */
71 0x28400, /* 7dB */
72 0x2ec00, /* 8dB */
73 0x35400, /* 9dB */
74 0x3c000, /* 10dB */
75 0x42c00, /* 11dB */
76 0x49c00, /* 12dB */
77 0x51800, /* 13dB */
78 0x58400, /* 14dB */
79 0x5f800 /* 15dB */
80};
81
82static unsigned char fliptable[] =
83{
84 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
85 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
86 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
87 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
88 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
89 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
90 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
91 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
92 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
93 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
94 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
95 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
96 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
97 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
98 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
99 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
100 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
101 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
102 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
103 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
104 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
105 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
106 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
107 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
108 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
109 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
110 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
111 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
112 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
113 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
114 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
115 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
116};
117
118static struct event_queue mpeg_queue;
119static int mpeg_stack[MPEG_STACK_SIZE/sizeof(int)];
120
121static unsigned char mp3buf[ MPEG_BUFSIZE ];
122static int mp3buf_write;
123static int mp3buf_read;
124
125static int last_dma_chunk_size;
126
127static bool dma_on; /* The DMA is active */
128static bool playing; /* We are playing an MP3 stream */
129static bool filling; /* We are filling the buffer with data from disk */
130
131static int mpeg_file = -1;
132
133static void mas_poll_start(int interval_in_ms)
134{
135 unsigned int count;
136
137 count = FREQ / 1000 / 8 * interval_in_ms;
138
139 if(count > 0xffff)
140 {
141 panicf("Error! The MAS poll interval is too long (%d ms)\n",
142 interval_in_ms);
143 return;
144 }
145
146 /* We are using timer 1 */
147
148 TSTR &= ~0x02; /* Stop the timer */
149 TSNC &= ~0x02; /* No synchronization */
150 TMDR &= ~0x02; /* Operate normally */
151
152 TCNT1 = 0; /* Start counting at 0 */
153 GRA1 = count;
154 TCR1 = 0x23; /* Clear at GRA match, sysclock/8 */
155
156 /* Enable interrupt on level 2 */
157 IPRC = (IPRC & ~0x000f) | 0x0002;
158
159 TSR1 &= ~0x02;
160 TIER1 = 0xf9; /* Enable GRA match interrupt */
161
162 TSTR |= 0x02; /* Start timer 2 */
163}
164
165static void init_dma(void)
166{
167 SAR3 = (unsigned int) mp3buf + mp3buf_read;
168 DAR3 = 0x5FFFEC3;
169 CHCR3 &= ~0x0002; /* Clear interrupt */
170 CHCR3 = 0x1504; /* Single address destination, TXI0, IE=1 */
171 last_dma_chunk_size = MIN(65536, mp3buf_write - mp3buf_read);
172 DTCR3 = last_dma_chunk_size & 0xffff;
173 DMAOR = 0x0001; /* Enable DMA */
174 CHCR3 |= 0x0001; /* Enable DMA IRQ */
175}
176
177static void start_dma(void)
178{
179 SCR0 |= 0x80;
180 dma_on = true;
181}
182
183static void stop_dma(void)
184{
185 SCR0 &= 0x7f;
186 dma_on = false;
187}
188
189static void dma_tick(void)
190{
191 /* Start DMA if it isn't running */
192 if(playing && !dma_on)
193 {
194 if(PBDR & 0x4000)
195 {
196 if(!(SCR0 & 0x80))
197 start_dma();
198 }
199 }
200}
201
202static void bitswap(unsigned char *data, int length)
203{
204 int i;
205 for(i = 0;i < length;i++)
206 {
207 data[i] = fliptable[data[i]];
208 }
209}
210
211static void reset_mp3_buffer(void)
212{
213 mp3buf_read = 0;
214 mp3buf_write = 0;
215}
216
217#pragma interrupt
218void IMIA1(void)
219{
220 dma_tick();
221 TSR1 &= ~0x01;
222}
223
224static void mpeg_thread(void)
225{
226 struct event ev;
227 int len;
228 int free_space_left;
229 int amount_to_read;
230 bool play_pending;
231
232 play_pending = false;
233 playing = false;
234
235 while(1)
236 {
237 DEBUGF("S\n");
238 queue_wait(&mpeg_queue, &ev);
239 switch(ev.id)
240 {
241 case MPEG_PLAY:
242 /* Stop the current stream */
243 play_pending = false;
244 playing = false;
245 stop_dma();
246
247 reset_mp3_buffer();
248
249 mpeg_file = open((char*)ev.data, O_RDONLY);
250 if(mpeg_file < 0)
251 {
252 DEBUGF("Couldn't open %s\n",ev.data);
253 break;
254 }
255
256 /* Make it read more data */
257 filling = true;
258 queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
259
260 /* Tell the file loading code that we want to start playing
261 as soon as we have some data */
262 play_pending = true;
263 break;
264
265 case MPEG_STOP:
266 /* Stop the current stream */
267 playing = false;
268 stop_dma();
269 break;
270
271 case MPEG_PAUSE:
272 /* Stop the current stream */
273 playing = false;
274 stop_dma();
275 break;
276
277 case MPEG_RESUME:
278 /* Stop the current stream */
279 playing = true;
280 start_dma();
281 break;
282
283 case MPEG_NEED_DATA:
284 free_space_left = mp3buf_read - mp3buf_write;
285
286 /* We interpret 0 as "empty buffer" */
287 if(free_space_left <= 0)
288 free_space_left = MPEG_BUFSIZE + free_space_left;
289
290 if(free_space_left <= MPEG_CHUNKSIZE)
291 {
292 DEBUGF("0\n");
293 filling = false;
294 break;;
295 }
296
297 amount_to_read = MIN(MPEG_CHUNKSIZE, free_space_left);
298 amount_to_read = MIN(MPEG_BUFSIZE - mp3buf_write, amount_to_read);
299
300 /* Read in a few seconds worth of MP3 data. We don't want to
301 read too large chunks because the bitswapping will take
302 too much time. We must keep the DMA happy and also give
303 the other threads a chance to run. */
304 DEBUGF("R\n");
305 len = read(mpeg_file, mp3buf+mp3buf_write, amount_to_read);
306 if(len)
307 {
308 DEBUGF("B\n");
309 bitswap(mp3buf + mp3buf_write, len);
310
311 mp3buf_write += len;
312 if(mp3buf_write >= MPEG_BUFSIZE)
313 {
314 mp3buf_write = 0;
315 DEBUGF("W\n");
316 }
317
318 /* Tell ourselves that we want more data */
319 queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
320
321 /* And while we're at it, see if we have startet playing
322 yet. If not, do it. */
323 if(play_pending)
324 {
325 play_pending = false;
326 playing = true;
327
328 init_dma();
329 start_dma();
330 }
331 }
332 else
333 {
334 close(mpeg_file);
335
336 /* Make sure that the write pointer is at a word
337 boundary */
338 mp3buf_write &= 0xfffffffe;
339
340#if 1
341 /* No more data to play */
342 DEBUGF("Finished playing\n");
343 playing = false;
344 filling = false;
345#else
346 next_track();
347 if(new_file() < 0)
348 {
349 /* No more data to play */
350 DEBUGF("Finished playing\n");
351 playing = false;
352 filling = false;
353 }
354 else
355 {
356 /* Tell ourselves that we want more data */
357 queue_post(&mpeg_queue, MPEG_NEED_DATA, 0);
358 }
359#endif
360 }
361 break;
362 }
363 }
364}
365
366static void setup_sci0(void)
367{
368 /* PB15 is I/O, PB14 is IRQ6, PB12 is SCK0 */
369 PBCR1 = (PBCR1 & 0x0cff) | 0x1200;
370
371 /* Set PB12 to output */
372 PBIOR |= 0x1000;
373
374 /* Disable serial port */
375 SCR0 = 0x00;
376
377 /* Synchronous, no prescale */
378 SMR0 = 0x80;
379
380 /* Set baudrate 1Mbit/s */
381 BRR0 = 0x03;
382
383 /* use SCK as serial clock output */
384 SCR0 = 0x01;
385
386 /* Clear FER and PER */
387 SSR0 &= 0xe7;
388
389 /* Set interrupt ITU2 and SCI0 priority to 0 */
390 IPRD &= 0x0ff0;
391
392 /* set IRQ6 and IRQ7 to edge detect */
393 ICR |= 0x03;
394
395 /* set PB15 and PB14 to inputs */
396 PBIOR &= 0x7fff;
397 PBIOR &= 0xbfff;
398
399 /* set IRQ6 prio 8 and IRQ7 prio 0 */
400 IPRB = ( IPRB & 0xff00 ) | 0x0080;
401
402 /* Enable End of DMA interrupt at prio 8 */
403 IPRC = (IPRC & 0xf0ff) | 0x0800;
404
405 /* Enable Tx (only!) */
406 SCR0 |= 0x20;
407}
408
409void mpeg_init(void)
410{
411 int rc;
412
413 setup_sci0();
414
415#ifdef DEBUG
416 {
417 unsigned char buf[32];
418 unsigned char str[32];
419 int i;
420
421 rc = mas_readmem(MAS_BANK_D1,0xff6,(unsigned long*)buf,2);
422 if (rc)
423 panicf("Error - mas_readmem(0xff6) returned %d\n", rc);
424
425 i = buf[0] | buf[1] << 8;
426 DEBUGF("MAS version: %x\n", i);
427 i = buf[4] | buf[5] << 8;
428 DEBUGF("MAS revision: %x\n", i);
429
430 rc = mas_readmem(MAS_BANK_D1,0xff9,(unsigned long*)buf,7);
431 if (rc)
432 panicf("Error - mas_readmem(0xff9) returned %d\n", rc);
433
434 for (i = 0;i < 7;i++) {
435 str[i*2+1] = buf[i*4];
436 str[i*2] = buf[i*4+1];
437 }
438 str[i*2] = 0;
439 DEBUGF("Description: %s\n", str);
440 }
441#endif
442
443 rc = mas_writereg(0x3b, 0x20);
444 if (rc < 0)
445 panicf("Error - mas_writereg(0x3b) returned %d\n", rc);
446
447 rc = mas_run(1);
448 if (rc < 0)
449 panicf("Error - mas_run(1) returned %d\n", rc);
450
451 queue_init(&mpeg_queue);
452 create_thread(mpeg_thread, mpeg_stack, sizeof(mpeg_stack));
453 mas_poll_start(2);
454
455 mas_writereg(MAS_REG_KPRESCALE, 0xe9400);
456}
457
458void mpeg_play(char* trackname)
459{
460 queue_post(&mpeg_queue, MPEG_PLAY, trackname);
461}
462
463void mpeg_stop(void)
464{
465 queue_post(&mpeg_queue, MPEG_STOP, NULL);
466}
467
468void mpeg_pause(void)
469{
470 queue_post(&mpeg_queue, MPEG_PAUSE, NULL);
471}
472
473void mpeg_resume(void)
474{
475 queue_post(&mpeg_queue, MPEG_RESUME, NULL);
476}
477
478void mpeg_volume(int percent)
479{
480 int volume = 0x2c * percent / 100;
481 dac_volume(volume);
482}
483
484void mpeg_bass(int percent)
485{
486 int bass = 15 * percent / 100;
487 mas_writereg(MAS_REG_KBASS, bass_table[bass]);
488}
489
490void mpeg_treble(int percent)
491{
492 int treble = 15 * percent / 100;
493 mas_writereg(MAS_REG_KTREBLE, treble_table[treble]);
494}
diff --git a/firmware/mpeg.h b/firmware/mpeg.h
new file mode 100644
index 0000000000..493e5f55a2
--- /dev/null
+++ b/firmware/mpeg.h
@@ -0,0 +1,31 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef _MPEG_H_
20#define _MPEG_H_
21
22void mpeg_init(void);
23void mpeg_play(char* trackname);
24void mpeg_stop(void);
25void mpeg_pause(void);
26void mpeg_resume(void);
27void mpeg_volume(int percent);
28void mpeg_bass(int percent);
29void mpeg_treble(int percent);
30
31#endif