xref: /third_party/ffmpeg/libavutil/ppc/cpu.c (revision cabdff1a)
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include "config.h"
20
21#ifdef __APPLE__
22#include <sys/sysctl.h>
23#elif defined(__linux__)
24#include <asm/cputable.h>
25#include <linux/auxvec.h>
26#include <fcntl.h>
27#if HAVE_UNISTD_H
28#include <unistd.h>
29#endif
30#elif defined(__OpenBSD__)
31#include <sys/types.h>
32#include <sys/sysctl.h>
33#include <machine/cpu.h>
34#elif defined(__AMIGAOS4__)
35#include <exec/exec.h>
36#include <interfaces/exec.h>
37#include <proto/exec.h>
38#endif /* __APPLE__ */
39
40#include "libavutil/avassert.h"
41#include "libavutil/cpu.h"
42#include "libavutil/cpu_internal.h"
43
44/**
45 * This function MAY rely on signal() or fork() in order to make sure AltiVec
46 * is present.
47 */
48int ff_get_cpu_flags_ppc(void)
49{
50#if HAVE_ALTIVEC
51#ifdef __AMIGAOS4__
52    ULONG result = 0;
53    extern struct ExecIFace *IExec;
54
55    IExec->GetCPUInfoTags(GCIT_VectorUnit, &result, TAG_DONE);
56    if (result == VECTORTYPE_ALTIVEC)
57        return AV_CPU_FLAG_ALTIVEC;
58    return 0;
59#elif defined(__APPLE__) || defined(__OpenBSD__)
60#ifdef __OpenBSD__
61    int sels[2] = {CTL_MACHDEP, CPU_ALTIVEC};
62#else
63    int sels[2] = {CTL_HW, HW_VECTORUNIT};
64#endif
65    int has_vu = 0;
66    size_t len = sizeof(has_vu);
67    int err;
68
69    err = sysctl(sels, 2, &has_vu, &len, NULL, 0);
70
71    if (err == 0)
72        return has_vu ? AV_CPU_FLAG_ALTIVEC : 0;
73    return 0;
74#elif defined(__linux__)
75    // The linux kernel could have the altivec support disabled
76    // even if the cpu has it.
77    int i, ret = 0;
78    int fd = open("/proc/self/auxv", O_RDONLY);
79    unsigned long buf[64] = { 0 };
80    ssize_t count;
81
82    if (fd < 0)
83        return 0;
84
85    while ((count = read(fd, buf, sizeof(buf))) > 0) {
86        for (i = 0; i < count / sizeof(*buf); i += 2) {
87            if (buf[i] == AT_NULL)
88                goto out;
89            if (buf[i] == AT_HWCAP) {
90                if (buf[i + 1] & PPC_FEATURE_HAS_ALTIVEC)
91                    ret = AV_CPU_FLAG_ALTIVEC;
92#ifdef PPC_FEATURE_HAS_VSX
93                if (buf[i + 1] & PPC_FEATURE_HAS_VSX)
94                    ret |= AV_CPU_FLAG_VSX;
95#endif
96                if (ret & AV_CPU_FLAG_VSX)
97                    av_assert0(ret & AV_CPU_FLAG_ALTIVEC);
98            }
99#ifdef AT_HWCAP2 /* not introduced until glibc 2.18 */
100            else if (buf[i] == AT_HWCAP2) {
101#ifdef PPC_FEATURE2_ARCH_2_07
102                if (buf[i + 1] & PPC_FEATURE2_ARCH_2_07)
103                    ret |= AV_CPU_FLAG_POWER8;
104#endif
105            }
106#endif /* AT_HWCAP2 */
107        }
108    }
109
110out:
111    close(fd);
112    return ret;
113#elif CONFIG_RUNTIME_CPUDETECT && defined(__linux__)
114#define PVR_G4_7400  0x000C
115#define PVR_G5_970   0x0039
116#define PVR_G5_970FX 0x003C
117#define PVR_G5_970MP 0x0044
118#define PVR_G5_970GX 0x0045
119#define PVR_POWER6   0x003E
120#define PVR_POWER7   0x003F
121#define PVR_POWER8   0x004B
122#define PVR_CELL_PPU 0x0070
123    int ret = 0;
124    int proc_ver;
125    // Support of mfspr PVR emulation added in Linux 2.6.17.
126    __asm__ volatile("mfspr %0, 287" : "=r" (proc_ver));
127    proc_ver >>= 16;
128    if (proc_ver  & 0x8000 ||
129        proc_ver == PVR_G4_7400  ||
130        proc_ver == PVR_G5_970   ||
131        proc_ver == PVR_G5_970FX ||
132        proc_ver == PVR_G5_970MP ||
133        proc_ver == PVR_G5_970GX ||
134        proc_ver == PVR_POWER6   ||
135        proc_ver == PVR_POWER7   ||
136        proc_ver == PVR_POWER8   ||
137        proc_ver == PVR_CELL_PPU)
138        ret = AV_CPU_FLAG_ALTIVEC;
139    if (proc_ver == PVR_POWER7 ||
140        proc_ver == PVR_POWER8)
141        ret |= AV_CPU_FLAG_VSX;
142    if (proc_ver == PVR_POWER8)
143        ret |= AV_CPU_FLAG_POWER8;
144
145    return ret;
146#else
147    // Since we were compiled for AltiVec, just assume we have it
148    // until someone comes up with a proper way (not involving signal hacks).
149    return AV_CPU_FLAG_ALTIVEC;
150#endif /* __AMIGAOS4__ */
151#endif /* HAVE_ALTIVEC */
152    return 0;
153}
154
155size_t ff_get_cpu_max_align_ppc(void)
156{
157    int flags = av_get_cpu_flags();
158
159    if (flags & (AV_CPU_FLAG_ALTIVEC   |
160                 AV_CPU_FLAG_VSX       |
161                 AV_CPU_FLAG_POWER8))
162        return 16;
163
164    return 8;
165}
166