153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2009 Wim Taymans <wim.taymans@collabora.co.uk>
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
953a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
1053a5a1b3Sopenharmony_ci  or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1853a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#include <stdint.h>
2653a5a1b3Sopenharmony_ci#include <sys/types.h>
2753a5a1b3Sopenharmony_ci#include <fcntl.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3053a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3153a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#include "cpu-arm.h"
3453a5a1b3Sopenharmony_ci
3553a5a1b3Sopenharmony_ci#if defined (__arm__) && defined (__linux__)
3653a5a1b3Sopenharmony_ci
3753a5a1b3Sopenharmony_ci#define MAX_BUFFER 4096
3853a5a1b3Sopenharmony_cistatic char *
3953a5a1b3Sopenharmony_ciget_cpuinfo_line(char *cpuinfo, const char *tag) {
4053a5a1b3Sopenharmony_ci    char *line, *end, *colon;
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ci    if (!(line = strstr(cpuinfo, tag)))
4353a5a1b3Sopenharmony_ci        return NULL;
4453a5a1b3Sopenharmony_ci
4553a5a1b3Sopenharmony_ci    if (!(end = strchr(line, '\n')))
4653a5a1b3Sopenharmony_ci        return NULL;
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_ci    if (!(colon = strchr(line, ':')))
4953a5a1b3Sopenharmony_ci        return NULL;
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci    if (++colon >= end)
5253a5a1b3Sopenharmony_ci        return NULL;
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_ci    return pa_xstrndup(colon, end - colon);
5553a5a1b3Sopenharmony_ci}
5653a5a1b3Sopenharmony_ci
5753a5a1b3Sopenharmony_cistatic char *get_cpuinfo(void) {
5853a5a1b3Sopenharmony_ci    char *cpuinfo;
5953a5a1b3Sopenharmony_ci    int n, fd;
6053a5a1b3Sopenharmony_ci
6153a5a1b3Sopenharmony_ci    cpuinfo = pa_xmalloc(MAX_BUFFER);
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_ci    if ((fd = pa_open_cloexec("/proc/cpuinfo", O_RDONLY, 0)) < 0) {
6453a5a1b3Sopenharmony_ci        pa_xfree(cpuinfo);
6553a5a1b3Sopenharmony_ci        return NULL;
6653a5a1b3Sopenharmony_ci    }
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci    if ((n = pa_read(fd, cpuinfo, MAX_BUFFER-1, NULL)) < 0) {
6953a5a1b3Sopenharmony_ci        pa_xfree(cpuinfo);
7053a5a1b3Sopenharmony_ci        pa_close(fd);
7153a5a1b3Sopenharmony_ci        return NULL;
7253a5a1b3Sopenharmony_ci    }
7353a5a1b3Sopenharmony_ci    cpuinfo[n] = 0;
7453a5a1b3Sopenharmony_ci    pa_close(fd);
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    return cpuinfo;
7753a5a1b3Sopenharmony_ci}
7853a5a1b3Sopenharmony_ci#endif /* defined (__arm__) && defined (__linux__) */
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_civoid pa_cpu_get_arm_flags(pa_cpu_arm_flag_t *flags) {
8153a5a1b3Sopenharmony_ci#if defined (__arm__) && defined (__linux__)
8253a5a1b3Sopenharmony_ci    char *cpuinfo, *line;
8353a5a1b3Sopenharmony_ci    int arch, part;
8453a5a1b3Sopenharmony_ci
8553a5a1b3Sopenharmony_ci    /* We need to read the CPU flags from /proc/cpuinfo because there is no user
8653a5a1b3Sopenharmony_ci     * space support to get the CPU features. This only works on linux AFAIK. */
8753a5a1b3Sopenharmony_ci    if (!(cpuinfo = get_cpuinfo())) {
8853a5a1b3Sopenharmony_ci        pa_log("Can't read cpuinfo");
8953a5a1b3Sopenharmony_ci        return;
9053a5a1b3Sopenharmony_ci    }
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_ci    *flags = 0;
9353a5a1b3Sopenharmony_ci
9453a5a1b3Sopenharmony_ci    /* get the CPU architecture */
9553a5a1b3Sopenharmony_ci    if ((line = get_cpuinfo_line(cpuinfo, "CPU architecture"))) {
9653a5a1b3Sopenharmony_ci        arch = strtoul(line, NULL, 0);
9753a5a1b3Sopenharmony_ci        if (arch >= 6)
9853a5a1b3Sopenharmony_ci            *flags |= PA_CPU_ARM_V6;
9953a5a1b3Sopenharmony_ci        if (arch >= 7)
10053a5a1b3Sopenharmony_ci            *flags |= PA_CPU_ARM_V7;
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci        pa_xfree(line);
10353a5a1b3Sopenharmony_ci    }
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci    /* get the CPU features */
10653a5a1b3Sopenharmony_ci    if ((line = get_cpuinfo_line(cpuinfo, "Features"))) {
10753a5a1b3Sopenharmony_ci        const char *state = NULL;
10853a5a1b3Sopenharmony_ci        char *current;
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci        while ((current = pa_split_spaces(line, &state))) {
11153a5a1b3Sopenharmony_ci            if (pa_streq(current, "vfp"))
11253a5a1b3Sopenharmony_ci                *flags |= PA_CPU_ARM_VFP;
11353a5a1b3Sopenharmony_ci            else if (pa_streq(current, "edsp"))
11453a5a1b3Sopenharmony_ci                *flags |= PA_CPU_ARM_EDSP;
11553a5a1b3Sopenharmony_ci            else if (pa_streq(current, "neon"))
11653a5a1b3Sopenharmony_ci                *flags |= PA_CPU_ARM_NEON;
11753a5a1b3Sopenharmony_ci            else if (pa_streq(current, "vfpv3"))
11853a5a1b3Sopenharmony_ci                *flags |= PA_CPU_ARM_VFPV3;
11953a5a1b3Sopenharmony_ci
12053a5a1b3Sopenharmony_ci            pa_xfree(current);
12153a5a1b3Sopenharmony_ci        }
12253a5a1b3Sopenharmony_ci        pa_xfree(line);
12353a5a1b3Sopenharmony_ci    }
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    /* get the CPU part number */
12653a5a1b3Sopenharmony_ci    if ((line = get_cpuinfo_line(cpuinfo, "CPU part"))) {
12753a5a1b3Sopenharmony_ci        part = strtoul(line, NULL, 0);
12853a5a1b3Sopenharmony_ci        if (part == 0xc08)
12953a5a1b3Sopenharmony_ci            *flags |= PA_CPU_ARM_CORTEX_A8;
13053a5a1b3Sopenharmony_ci        pa_xfree(line);
13153a5a1b3Sopenharmony_ci    }
13253a5a1b3Sopenharmony_ci    pa_xfree(cpuinfo);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    pa_log_info("CPU flags: %s%s%s%s%s%s%s",
13553a5a1b3Sopenharmony_ci          (*flags & PA_CPU_ARM_V6) ? "V6 " : "",
13653a5a1b3Sopenharmony_ci          (*flags & PA_CPU_ARM_V7) ? "V7 " : "",
13753a5a1b3Sopenharmony_ci          (*flags & PA_CPU_ARM_VFP) ? "VFP " : "",
13853a5a1b3Sopenharmony_ci          (*flags & PA_CPU_ARM_EDSP) ? "EDSP " : "",
13953a5a1b3Sopenharmony_ci          (*flags & PA_CPU_ARM_NEON) ? "NEON " : "",
14053a5a1b3Sopenharmony_ci          (*flags & PA_CPU_ARM_VFPV3) ? "VFPV3 " : "",
14153a5a1b3Sopenharmony_ci          (*flags & PA_CPU_ARM_CORTEX_A8) ? "Cortex-A8 " : "");
14253a5a1b3Sopenharmony_ci#endif
14353a5a1b3Sopenharmony_ci}
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_cibool pa_cpu_init_arm(pa_cpu_arm_flag_t *flags) {
14653a5a1b3Sopenharmony_ci#if defined (__arm__)
14753a5a1b3Sopenharmony_ci#if defined (__linux__)
14853a5a1b3Sopenharmony_ci    pa_cpu_get_arm_flags(flags);
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci    if (*flags & PA_CPU_ARM_V6)
15153a5a1b3Sopenharmony_ci        pa_volume_func_init_arm(*flags);
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci#ifdef HAVE_NEON
15453a5a1b3Sopenharmony_ci    if (*flags & PA_CPU_ARM_NEON) {
15553a5a1b3Sopenharmony_ci        pa_convert_func_init_neon(*flags);
15653a5a1b3Sopenharmony_ci        pa_mix_func_init_neon(*flags);
15753a5a1b3Sopenharmony_ci        pa_remap_func_init_neon(*flags);
15853a5a1b3Sopenharmony_ci    }
15953a5a1b3Sopenharmony_ci#endif
16053a5a1b3Sopenharmony_ci
16153a5a1b3Sopenharmony_ci    return true;
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ci#else /* defined (__linux__) */
16453a5a1b3Sopenharmony_ci    pa_log("Reading ARM CPU features not yet supported on this OS");
16553a5a1b3Sopenharmony_ci#endif /* defined (__linux__) */
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_ci#else /* defined (__arm__) */
16853a5a1b3Sopenharmony_ci    return false;
16953a5a1b3Sopenharmony_ci#endif /* defined (__arm__) */
17053a5a1b3Sopenharmony_ci}
171