1 /* $NetBSD: msm6258.c,v 1.11 2003/09/07 04:24:07 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 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * OKI MSM6258 ADPCM voice synthesizer codec. 32 */ 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: msm6258.c,v 1.11 2003/09/07 04:24:07 isaki Exp $"); 36 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/malloc.h> 40 #include <sys/select.h> 41 #include <sys/audioio.h> 42 43 #include <dev/audio_if.h> 44 #include <dev/auconv.h> 45 #include <dev/audiovar.h> 46 #include <dev/mulaw.h> 47 #include <dev/ic/msm6258var.h> 48 49 struct msm6258_codecvar { 50 /* ADPCM stream must be converted in order. */ 51 u_char mc_buf[AU_RING_SIZE]; /* XXX */ 52 53 short mc_amp; 54 char mc_estim; 55 }; 56 57 struct msm6258_softc { 58 struct device sc_dev; 59 struct msm6258_codecvar *sc_mc; 60 /* MD vars follow */ 61 }; 62 63 static inline u_char pcm2adpcm_step(struct msm6258_codecvar *, short); 64 static inline short adpcm2pcm_step(struct msm6258_codecvar *, u_char); 65 66 static int adpcm_estimindex[16] = { 67 2, 6, 10, 14, 18, 22, 26, 30, 68 -2, -6, -10, -14, -18, -22, -26, -30 69 }; 70 71 static int adpcm_estim[49] = { 72 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 73 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 74 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 75 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 76 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 77 }; 78 79 static int adpcm_estimstep[16] = { 80 -1, -1, -1, -1, 2, 4, 6, 8, 81 -1, -1, -1, -1, 2, 4, 6, 8 82 }; 83 84 void * 85 msm6258_codec_init (void) 86 { 87 struct msm6258_codecvar *r; 88 89 r = malloc (sizeof(*r), M_DEVBUF, M_NOWAIT); 90 if (r == 0) 91 return 0; 92 r->mc_amp = r->mc_estim = 0; 93 94 return r; 95 } 96 97 int 98 msm6258_codec_open(void *hdl) 99 { 100 struct msm6258_softc *sc = hdl; 101 struct msm6258_codecvar *mc = sc->sc_mc; 102 103 mc->mc_amp = 0; 104 mc->mc_estim = 0; 105 106 return 0; 107 } 108 109 /* 110 * signed 16bit linear PCM -> OkiADPCM 111 */ 112 static inline u_char 113 pcm2adpcm_step(struct msm6258_codecvar *mc, short a) 114 { 115 int estim = (int)mc->mc_estim; 116 int df; 117 short dl, c; 118 unsigned char b; 119 unsigned char s; 120 121 df = a - mc->mc_amp; 122 dl = adpcm_estim[estim]; 123 c = (df / 16) * 8 / dl; 124 if (df < 0) { 125 b = (unsigned char)(-c) / 2; 126 s = 0x08; 127 } else { 128 b = (unsigned char)(c) / 2; 129 s = 0; 130 } 131 if (b > 7) 132 b = 7; 133 s |= b; 134 mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl); 135 estim += adpcm_estimstep[b]; 136 if (estim < 0) 137 estim = 0; 138 else if (estim > 48) 139 estim = 48; 140 141 mc->mc_estim = estim; 142 return s; 143 } 144 145 void 146 msm6258_slinear16_host_to_adpcm(void *hdl, u_char *p, int cc) 147 { 148 struct msm6258_softc *sc = hdl; 149 struct msm6258_codecvar *mc = sc->sc_mc; 150 short *s = (short *)p; 151 int i; 152 u_char f; 153 154 for (i = 0; i < cc; i += 4) { 155 f = pcm2adpcm_step(mc, *s++); 156 f |= pcm2adpcm_step(mc, *s++) << 4; 157 *p++ = f; 158 } 159 } 160 161 void 162 msm6258_slinear16_le_to_adpcm(void *hdl, u_char *p, int cc) 163 { 164 #if BYTE_ORDER == BIG_ENDIAN 165 swap_bytes(hdl, p, cc); 166 #endif 167 msm6258_slinear16_host_to_adpcm(hdl, p, cc); 168 } 169 170 void 171 msm6258_slinear16_be_to_adpcm(void *hdl, u_char *p, int cc) 172 { 173 #if BYTE_ORDER == LITTLE_ENDIAN 174 swap_bytes(hdl, p, cc); 175 #endif 176 msm6258_slinear16_host_to_adpcm(hdl, p, cc); 177 } 178 179 void 180 msm6258_slinear8_to_adpcm(void *hdl, u_char *p, int cc) 181 { 182 struct msm6258_softc *sc = hdl; 183 struct msm6258_codecvar *mc = sc->sc_mc; 184 u_char *s = p; 185 int i; 186 u_char f; 187 188 for (i = 0; i < cc; i += 2) { 189 f = pcm2adpcm_step(mc, (short)(*s++) * 256); 190 f |= pcm2adpcm_step(mc, (short)(*s++) * 256) << 4; 191 *p++ = f; 192 } 193 } 194 195 void 196 msm6258_ulinear8_to_adpcm(void *hdl, u_char *p, int cc) 197 { 198 change_sign8(hdl, p, cc); 199 msm6258_slinear8_to_adpcm(hdl, p, cc); 200 } 201 202 void 203 msm6258_mulaw_to_adpcm(void *hdl, u_char *p, int cc) 204 { 205 mulaw_to_slinear8(hdl, p, cc); 206 msm6258_slinear8_to_adpcm(hdl, p, cc); 207 } 208 209 210 /* 211 * OkiADPCM -> signed 16bit linear PCM 212 */ 213 static inline short 214 adpcm2pcm_step(struct msm6258_codecvar *mc, u_char b) 215 { 216 int estim = (int)mc->mc_estim; 217 218 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b]; 219 estim += adpcm_estimstep[b]; 220 221 if (estim < 0) 222 estim = 0; 223 else if (estim > 48) 224 estim = 48; 225 226 mc->mc_estim = estim; 227 228 return mc->mc_amp; 229 } 230 231 void 232 msm6258_adpcm_to_slinear16_host(void *hdl, u_char *p, int cc) 233 { 234 struct msm6258_softc *sc = hdl; 235 struct msm6258_codecvar *mc = sc->sc_mc; 236 short *d = (short *)p; 237 int i; 238 u_char a; 239 240 /* XXX alignment ? */ 241 memcpy(mc->mc_buf, p, cc / 4); 242 for (i = 0; i < cc / 4;) { 243 a = mc->mc_buf[i++]; 244 245 *d++ = adpcm2pcm_step(mc, a & 0x0f); 246 *d++ = adpcm2pcm_step(mc, (a >> 4) & 0x0f); 247 } 248 } 249 250 void 251 msm6258_adpcm_to_slinear16_le(void *hdl, u_char *p, int cc) 252 { 253 msm6258_adpcm_to_slinear16_host(hdl, p, cc); 254 #if BYTE_ORDER == BIG_ENDIAN 255 swap_bytes(hdl, p, cc); 256 #endif 257 } 258 259 void 260 msm6258_adpcm_to_slinear16_be(void *hdl, u_char *p, int cc) 261 { 262 msm6258_adpcm_to_slinear16_host(hdl, p, cc); 263 #if BYTE_ORDER == LITTLE_ENDIAN 264 swap_bytes(hdl, p, cc); 265 #endif 266 } 267 268 void 269 msm6258_adpcm_to_slinear8(void *hdl, u_char *p, int cc) 270 { 271 struct msm6258_softc *sc = hdl; 272 struct msm6258_codecvar *mc = sc->sc_mc; 273 char *d = (char *)p; 274 int i; 275 u_char a; 276 277 /* cc may be even. XXX alignment ? */ 278 memcpy(mc->mc_buf, p, cc / 2); 279 for (i = 0; i < cc / 2;) { 280 a = mc->mc_buf[i++]; 281 282 *d++ = adpcm2pcm_step(mc, a & 0x0f) / 256; 283 *d++ = adpcm2pcm_step(mc, (a >> 4) & 0x0f) / 256; 284 } 285 } 286 287 void 288 msm6258_adpcm_to_ulinear8(void *hdl, u_char *p, int cc) 289 { 290 msm6258_adpcm_to_slinear8(hdl, p, cc); 291 change_sign8(hdl, p, cc); 292 } 293 294 void 295 msm6258_adpcm_to_mulaw(void *hdl, u_char *p, int cc) 296 { 297 msm6258_adpcm_to_slinear8(hdl, p, cc); 298 slinear8_to_mulaw(hdl, p, cc); 299 } 300 301