A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Copyright (C) 2010 Thomas Martitz, Andree Buschmann
11*
12* This program is free software; you can redistribute it and/or
13* modify it under the terms of the GNU General Public License
14* as published by the Free Software Foundation; either version 2
15* of the License, or (at your option) any later version.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#include "plugin.h"
23
24#if PLUGIN_BUFFER_SIZE <= 0x8000
25#define BUF_SIZE (1<<12) /* 16 KB = (1<<12)*sizeof(int) */
26#elif PLUGIN_BUFFER_SIZE <= 0x10000
27#define BUF_SIZE (1<<13) /* 32 KB = (1<<13)*sizeof(int) */
28#elif PLUGIN_BUFFER_SIZE <= 0x20000
29#define BUF_SIZE (1<<14) /* 64 KB = (1<<14)*sizeof(int) */
30#else
31#define BUF_SIZE (1<<15) /* 128 KB = (1<<15)*sizeof(int) */
32#endif
33
34#define LOOP_REPEAT_DRAM 256
35#define MAX_REPEAT_DRAM 512
36static int loop_repeat_dram = LOOP_REPEAT_DRAM;
37static volatile int buf_dram[BUF_SIZE] MEM_ALIGN_ATTR;
38
39#if defined(PLUGIN_USE_IRAM)
40
41#if PLUGIN_BUFFER_SIZE <= 0x8000
42#define IBUF_SIZE (1<<12) /* 16 KB = (1<<12)*sizeof(int) */
43#else
44#define IBUF_SIZE (1<<13) /* 32 KB = (1<<13)*sizeof(int) */
45#endif
46
47#define LOOP_REPEAT_IRAM 256
48#define MAX_REPEAT_IRAM 512
49static int loop_repeat_iram = LOOP_REPEAT_DRAM;
50static volatile int buf_iram[IBUF_SIZE] IBSS_ATTR MEM_ALIGN_ATTR;
51#endif
52
53/* (Byte per loop * loops)>>20 * ticks per s * 10 / ticks = dMB per s */
54#define dMB_PER_SEC(buf_size, cnt, delta) ((((buf_size*sizeof(int)*cnt)>>20)*HZ*10)/delta)
55
56static void memset_test(volatile int *buf, int buf_size, int loop_cnt)
57{
58 size_t buf_bytes = buf_size*sizeof(buf[0]);
59 for(int i = 0; i < loop_cnt; i++)
60 {
61 memset((void*)buf, 0xff, buf_bytes);
62 }
63}
64
65static void memcpy_test(volatile int *buf, int buf_size, int loop_cnt)
66{
67 /* half-size memcpy since memory regions must not overlap */
68 void* half_buf = (void*)(&buf[buf_size/2]);
69 size_t half_buf_bytes = buf_size * sizeof(buf[0]) / 2;
70
71 /* double loop count to compensate for half size memcpy */
72 for(int i = 0; i < loop_cnt*2; i++)
73 {
74 memcpy((void*)&buf[0], half_buf, half_buf_bytes);
75 }
76}
77
78static void write_test(volatile int *buf, int buf_size, int loop_cnt)
79{
80#if defined(CPU_ARM)
81 asm volatile (
82 "mov r6, %[loops] \n"
83 ".outer_loop_write: \n"
84 "mov r4, %[buf_p] \n"
85 "mov r5, %[size] \n"
86 ".inner_loop_write: \n"
87 "ldmia r4!, {r0-r3} \n"
88 "subs r5, r5, #8 \n"
89 "ldmia r4!, {r0-r3} \n"
90 "bgt .inner_loop_write \n"
91 "subs r6, r6, #1 \n"
92 "bgt .outer_loop_write \n"
93 :
94 : [loops] "r" (loop_cnt), [size] "r" (buf_size), [buf_p] "r" (buf)
95 : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory", "cc"
96 );
97#else
98 for(int i = 0; i < loop_cnt; i++)
99 {
100 for(int j = 0; j < buf_size; j+=4)
101 {
102 buf[j ] = j;
103 buf[j+1] = j+1;
104 buf[j+2] = j+2;
105 buf[j+3] = j+3;
106 }
107 }
108#endif
109}
110
111static void read_test(volatile int *buf, int buf_size, int loop_cnt)
112{
113#if defined(CPU_ARM)
114 asm volatile (
115 "mov r0, #0 \n"
116 "mov r1, #1 \n"
117 "mov r2, #2 \n"
118 "mov r3, #3 \n"
119 "mov r6, %[loops] \n"
120 ".outer_loop_read: \n"
121 "mov r4, %[buf_p] \n"
122 "mov r5, %[size] \n"
123 ".inner_loop_read: \n"
124 "stmia r4!, {r0-r3} \n"
125 "stmia r4!, {r0-r3} \n"
126 "subs r5, r5, #8 \n"
127 "bgt .inner_loop_read \n"
128 "subs r6, r6, #1 \n"
129 "bgt .outer_loop_read \n"
130 :
131 : [loops] "r" (loop_cnt), [size] "r" (buf_size), [buf_p] "r" (buf)
132 : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "memory", "cc"
133 );
134#else
135 int x, i, j;
136 for(i = 0; i < loop_cnt; i++)
137 {
138 for(j = 0; j < buf_size - 4; j+=4)
139 {
140 x = buf[j ];
141 x = buf[j+2];
142 x = buf[j+3];
143 x = buf[j+4];
144 }
145 }
146 (void)x;
147#endif
148}
149
150enum test_type {
151 READ,
152 WRITE,
153 MEMSET,
154 MEMCPY,
155};
156
157static const char tests[][7] = {
158 [READ] = "read ",
159 [WRITE] = "write ",
160 [MEMSET] = "memset",
161 [MEMCPY] = "memcpy",
162};
163
164static int line;
165#define TEST_MEM_PRINTF(...) rb->screens[0]->putsf(0, line++, __VA_ARGS__)
166
167static int test(volatile int *buf, int buf_size, int loop_cnt,
168 enum test_type type)
169{
170 int delta, dMB;
171 int last_tick = *rb->current_tick;
172 int ret = 0;
173
174 switch(type)
175 {
176 case READ: read_test(buf, buf_size, loop_cnt); break;
177 case WRITE: write_test(buf, buf_size, loop_cnt); break;
178 case MEMSET: memset_test(buf, buf_size, loop_cnt); break;
179 case MEMCPY: memcpy_test(buf, buf_size, loop_cnt); break;
180 }
181
182 delta = *rb->current_tick - last_tick;
183
184 if (delta <= 20)
185 {
186 /* The loop_cnt will be increased for the next measurement set until
187 * each measurement at least takes 10 ticks. This is to ensure a
188 * minimum accuracy. */
189 ret = 1;
190 }
191
192 delta = delta>0 ? delta : delta+1;
193 dMB = dMB_PER_SEC(buf_size, loop_cnt, delta);
194 TEST_MEM_PRINTF("%s: %3d.%d MB/s (%3d ms)",
195 tests[type], dMB/10, dMB%10, delta*10);
196
197 return ret;
198}
199
200enum plugin_status plugin_start(const void* parameter)
201{
202 (void)parameter;
203 bool done = false;
204#ifdef HAVE_ADJUSTABLE_CPU_FREQ
205 bool boost = false;
206#endif
207 int count = 0;
208
209 rb->lcd_setfont(FONT_SYSFIXED);
210
211 rb->screens[0]->clear_display();
212 TEST_MEM_PRINTF("patience, may take some seconds...");
213 rb->screens[0]->update();
214
215 while (!done)
216 {
217 line = 0;
218 int ret;
219 rb->screens[0]->clear_display();
220#ifdef HAVE_ADJUSTABLE_CPU_FREQ
221 TEST_MEM_PRINTF("%s", boost?"boosted":"unboosted");
222 TEST_MEM_PRINTF("clock: %3d.%d MHz", (*rb->cpu_frequency)/1000000, (*rb->cpu_frequency)%1000000);
223#endif
224 TEST_MEM_PRINTF("loop#: %d", ++count);
225
226 TEST_MEM_PRINTF("DRAM cnt: %d size: %d MB", loop_repeat_dram,
227 (loop_repeat_dram*BUF_SIZE*sizeof(buf_dram[0]))>>20);
228 ret = 0;
229 ret |= test(buf_dram, BUF_SIZE, loop_repeat_dram, READ);
230 ret |= test(buf_dram, BUF_SIZE, loop_repeat_dram, WRITE);
231 ret |= test(buf_dram, BUF_SIZE, loop_repeat_dram, MEMSET);
232 ret |= test(buf_dram, BUF_SIZE, loop_repeat_dram, MEMCPY);
233 if (ret != 0 && loop_repeat_dram < MAX_REPEAT_DRAM) loop_repeat_dram *= 2;
234#if defined(PLUGIN_USE_IRAM)
235 TEST_MEM_PRINTF("IRAM cnt: %d size: %d MB", loop_repeat_iram,
236 (loop_repeat_iram*BUF_SIZE*sizeof(buf_iram[0]))>>20);
237 ret = 0;
238 ret |= test(buf_iram, BUF_SIZE, loop_repeat_iram, READ);
239 ret |= test(buf_iram, BUF_SIZE, loop_repeat_iram, WRITE);
240 ret |= test(buf_iram, BUF_SIZE, loop_repeat_iram, MEMSET);
241 ret |= test(buf_iram, BUF_SIZE, loop_repeat_iram, MEMCPY);
242 if (ret != 0 && loop_repeat_iram < MAX_REPEAT_IRAM) loop_repeat_iram *= 2;
243#endif
244
245 rb->screens[0]->update();
246
247 switch (rb->get_action(CONTEXT_STD, HZ/5))
248 {
249#ifdef HAVE_ADJUSTABLE_CPU_FREQ
250 case ACTION_STD_PREV:
251 if (!boost)
252 {
253 rb->cpu_boost(true);
254 boost = true;
255 }
256 break;
257
258 case ACTION_STD_NEXT:
259 if (boost)
260 {
261 rb->cpu_boost(false);
262 boost = false;
263 }
264 break;
265#endif
266 case ACTION_STD_CANCEL:
267 done = true;
268 break;
269 }
270 }
271
272 return PLUGIN_OK;
273}