1 /*        $NetBSD: msm6258.c,v 1.26 2019/05/08 13:40:18 isaki Exp $   */
2 
3 /*
4  * Copyright (c) 2001 Tetsuya Isaki. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*
29  * OKI MSM6258 ADPCM voice synthesizer codec.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: msm6258.c,v 1.26 2019/05/08 13:40:18 isaki Exp $");
34 
35 #include <sys/systm.h>
36 #include <sys/device.h>
37 #include <sys/kmem.h>
38 #include <sys/select.h>
39 #include <sys/audioio.h>
40 
41 #include <dev/audio/audio_if.h>
42 #include <dev/ic/msm6258var.h>
43 
44 static inline uint8_t         pcm2adpcm_step(struct msm6258_codecvar *, int16_t);
45 static inline int16_t         adpcm2pcm_step(struct msm6258_codecvar *, uint8_t);
46 
47 static const int adpcm_estimindex[16] = {
48            2,  6,  10,  14,  18,  22,  26,  30,
49           -2, -6, -10, -14, -18, -22, -26, -30
50 };
51 
52 static const int adpcm_estim[49] = {
53            16,  17,  19,  21,  23,  25,  28,  31,  34,  37,
54            41,  45,  50,  55,  60,  66,  73,  80,  88,  97,
55           107, 118, 130, 143, 157, 173, 190, 209, 230, 253,
56           279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
57           724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552
58 };
59 
60 static const int adpcm_estimstep[16] = {
61           -1, -1, -1, -1, 2, 4, 6, 8,
62           -1, -1, -1, -1, 2, 4, 6, 8
63 };
64 
65 /*
66  * signed 16bit linear PCM -> OkiADPCM
67  */
68 static inline uint8_t
pcm2adpcm_step(struct msm6258_codecvar * mc,int16_t a)69 pcm2adpcm_step(struct msm6258_codecvar *mc, int16_t a)
70 {
71           int estim = (int)mc->mc_estim;
72           int df;
73           short dl, c;
74           uint8_t b;
75           uint8_t s;
76 
77           df = a - mc->mc_amp;
78           dl = adpcm_estim[estim];
79           c = (df / 16) * 8 / dl;
80           if (df < 0) {
81                     b = (unsigned char)(-c) / 2;
82                     s = 0x08;
83           } else {
84                     b = (unsigned char)(c) / 2;
85                     s = 0;
86           }
87           if (b > 7)
88                     b = 7;
89           s |= b;
90           mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl);
91           estim += adpcm_estimstep[b];
92           if (estim < 0)
93                     estim = 0;
94           else if (estim > 48)
95                     estim = 48;
96 
97           mc->mc_estim = estim;
98           return s;
99 }
100 
101 void
msm6258_internal_to_adpcm(audio_filter_arg_t * arg)102 msm6258_internal_to_adpcm(audio_filter_arg_t *arg)
103 {
104           struct msm6258_codecvar *mc;
105           const aint_t *src;
106           uint8_t *dst;
107           u_int sample_count;
108           u_int i;
109 
110           KASSERT((arg->count & 1) == 0);
111 
112           mc = arg->context;
113           src = arg->src;
114           dst = arg->dst;
115           sample_count = arg->count * arg->srcfmt->channels;
116           for (i = 0; i < sample_count / 2; i++) {
117                     aint_t s;
118                     uint8_t f;
119 
120                     s = *src++;
121                     s >>= AUDIO_INTERNAL_BITS - 16;
122                     f = pcm2adpcm_step(mc, s);
123 
124                     s = *src++;
125                     s >>= AUDIO_INTERNAL_BITS - 16;
126                     f |= pcm2adpcm_step(mc, s) << 4;
127 
128                     *dst++ = (uint8_t)f;
129           }
130 }
131 
132 
133 /*
134  * OkiADPCM -> signed 16bit linear PCM
135  */
136 static inline int16_t
adpcm2pcm_step(struct msm6258_codecvar * mc,uint8_t b)137 adpcm2pcm_step(struct msm6258_codecvar *mc, uint8_t b)
138 {
139           int estim = (int)mc->mc_estim;
140 
141           mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b];
142           estim += adpcm_estimstep[b];
143 
144           if (estim < 0)
145                     estim = 0;
146           else if (estim > 48)
147                     estim = 48;
148 
149           mc->mc_estim = estim;
150 
151           return mc->mc_amp;
152 }
153 
154 void
msm6258_adpcm_to_internal(audio_filter_arg_t * arg)155 msm6258_adpcm_to_internal(audio_filter_arg_t *arg)
156 {
157           struct msm6258_codecvar *mc;
158           const uint8_t *src;
159           aint_t *dst;
160           u_int sample_count;
161           u_int i;
162 
163           KASSERT((arg->count & 1) == 0);
164 
165           mc = arg->context;
166           src = arg->src;
167           dst = arg->dst;
168           sample_count = arg->count * arg->srcfmt->channels;
169           for (i = 0; i < sample_count / 2; i++) {
170                     uint8_t a = *src++;
171                     aint_t s;
172 
173                     s = adpcm2pcm_step(mc, a & 0x0f);
174                     s <<= AUDIO_INTERNAL_BITS - 16;
175                     *dst++ = s;
176 
177                     s = adpcm2pcm_step(mc, a >> 4);
178                     s <<= AUDIO_INTERNAL_BITS - 16;
179                     *dst++ = s;
180           }
181 }
182