1 /* $NetBSD: msm6258.c,v 1.17 2011/11/23 23:07:32 jmcneill 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.17 2011/11/23 23:07:32 jmcneill 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_if.h> 42 #include <dev/auconv.h> 43 #include <dev/audiovar.h> 44 #include <dev/mulaw.h> 45 #include <dev/ic/msm6258var.h> 46 47 struct msm6258_codecvar { 48 stream_filter_t base; 49 short mc_amp; 50 char mc_estim; 51 }; 52 53 static stream_filter_t *msm6258_factory 54 (struct audio_softc *, 55 int (*)(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int)); 56 static void msm6258_dtor(struct stream_filter *); 57 static inline uint8_t pcm2adpcm_step(struct msm6258_codecvar *, int16_t); 58 static inline int16_t adpcm2pcm_step(struct msm6258_codecvar *, uint8_t); 59 60 static const int adpcm_estimindex[16] = { 61 2, 6, 10, 14, 18, 22, 26, 30, 62 -2, -6, -10, -14, -18, -22, -26, -30 63 }; 64 65 static const int adpcm_estim[49] = { 66 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 67 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 68 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 69 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 70 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552 71 }; 72 73 static const int adpcm_estimstep[16] = { 74 -1, -1, -1, -1, 2, 4, 6, 8, 75 -1, -1, -1, -1, 2, 4, 6, 8 76 }; 77 78 static stream_filter_t * 79 msm6258_factory(struct audio_softc *asc, 80 int (*fetch_to)(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int)) 81 { 82 struct msm6258_codecvar *this; 83 84 this = kmem_alloc(sizeof(struct msm6258_codecvar), KM_SLEEP); 85 this->base.base.fetch_to = fetch_to; 86 this->base.dtor = msm6258_dtor; 87 this->base.set_fetcher = stream_filter_set_fetcher; 88 this->base.set_inputbuffer = stream_filter_set_inputbuffer; 89 return &this->base; 90 } 91 92 static void 93 msm6258_dtor(struct stream_filter *this) 94 { 95 if (this != NULL) 96 kmem_free(this, sizeof(struct msm6258_codecvar)); 97 } 98 99 /* 100 * signed 16bit linear PCM -> OkiADPCM 101 */ 102 static inline uint8_t 103 pcm2adpcm_step(struct msm6258_codecvar *mc, int16_t a) 104 { 105 int estim = (int)mc->mc_estim; 106 int df; 107 short dl, c; 108 uint8_t b; 109 uint8_t s; 110 111 df = a - mc->mc_amp; 112 dl = adpcm_estim[estim]; 113 c = (df / 16) * 8 / dl; 114 if (df < 0) { 115 b = (unsigned char)(-c) / 2; 116 s = 0x08; 117 } else { 118 b = (unsigned char)(c) / 2; 119 s = 0; 120 } 121 if (b > 7) 122 b = 7; 123 s |= b; 124 mc->mc_amp += (short)(adpcm_estimindex[(int)s] * dl); 125 estim += adpcm_estimstep[b]; 126 if (estim < 0) 127 estim = 0; 128 else if (estim > 48) 129 estim = 48; 130 131 mc->mc_estim = estim; 132 return s; 133 } 134 135 #define DEFINE_FILTER(name) \ 136 static int \ 137 name##_fetch_to(struct audio_softc *, stream_fetcher_t *, audio_stream_t *, int); \ 138 stream_filter_t * \ 139 name(struct audio_softc *sc, const audio_params_t *from, \ 140 const audio_params_t *to) \ 141 { \ 142 return msm6258_factory(sc, name##_fetch_to); \ 143 } \ 144 static int \ 145 name##_fetch_to(struct audio_softc *asc, stream_fetcher_t *self, audio_stream_t *dst, int max_used) 146 147 DEFINE_FILTER(msm6258_slinear16_to_adpcm) 148 { 149 stream_filter_t *this; 150 struct msm6258_codecvar *mc; 151 uint8_t *d; 152 const uint8_t *s; 153 int m, err, enc_src; 154 155 this = (stream_filter_t *)self; 156 mc = (struct msm6258_codecvar *)self; 157 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used * 4))) 158 return err; 159 m = dst->end - dst->start; 160 m = min(m, max_used); 161 d = dst->inp; 162 s = this->src->outp; 163 enc_src = this->src->param.encoding; 164 if (enc_src == AUDIO_ENCODING_SLINEAR_LE) { 165 while (dst->used < m && this->src->used >= 4) { 166 uint8_t f; 167 int16_t ss; 168 #if BYTE_ORDER == LITTLE_ENDIAN 169 ss = *(const int16_t*)s; 170 s = audio_stream_add_outp(this->src, s, 2); 171 f = pcm2adpcm_step(mc, ss); 172 ss = *(const int16_t*)s; 173 #else 174 ss = (s[1] << 8) | s[0]; 175 s = audio_stream_add_outp(this->src, s, 2); 176 f = pcm2adpcm_step(mc, ss); 177 ss = (s[1] << 8) | s[0]; 178 #endif 179 f |= pcm2adpcm_step(mc, ss) << 4; 180 *d = f; 181 d = audio_stream_add_inp(dst, d, 1); 182 s = audio_stream_add_outp(this->src, s, 2); 183 } 184 } else { 185 while (dst->used < m && this->src->used >= 4) { 186 uint8_t f; 187 int16_t ss; 188 #if BYTE_ORDER == BIG_ENDIAN 189 ss = *(const int16_t*)s; 190 s = audio_stream_add_outp(this->src, s, 2); 191 f = pcm2adpcm_step(mc, ss); 192 ss = *(const int16_t*)s; 193 #else 194 ss = (s[0] << 8) | s[1]; 195 s = audio_stream_add_outp(this->src, s, 2); 196 f = pcm2adpcm_step(mc, ss); 197 ss = (s[0] << 8) | s[1]; 198 #endif 199 f |= pcm2adpcm_step(mc, ss) << 4; 200 *d = f; 201 d = audio_stream_add_inp(dst, d, 1); 202 s = audio_stream_add_outp(this->src, s, 2); 203 } 204 } 205 dst->inp = d; 206 this->src->outp = s; 207 return 0; 208 } 209 210 DEFINE_FILTER(msm6258_linear8_to_adpcm) 211 { 212 stream_filter_t *this; 213 struct msm6258_codecvar *mc; 214 uint8_t *d; 215 const uint8_t *s; 216 int m, err, enc_src; 217 218 this = (stream_filter_t *)self; 219 mc = (struct msm6258_codecvar *)self; 220 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used * 2))) 221 return err; 222 m = dst->end - dst->start; 223 m = min(m, max_used); 224 d = dst->inp; 225 s = this->src->outp; 226 enc_src = this->src->param.encoding; 227 if (enc_src == AUDIO_ENCODING_SLINEAR_LE) { 228 while (dst->used < m && this->src->used >= 4) { 229 uint8_t f; 230 int16_t ss; 231 ss = ((int16_t)s[0]) * 256; 232 s = audio_stream_add_outp(this->src, s, 1); 233 f = pcm2adpcm_step(mc, ss); 234 ss = ((int16_t)s[0]) * 256; 235 f |= pcm2adpcm_step(mc, ss) << 4; 236 *d = f; 237 d = audio_stream_add_inp(dst, d, 1); 238 s = audio_stream_add_outp(this->src, s, 1); 239 } 240 } else { 241 while (dst->used < m && this->src->used >= 4) { 242 uint8_t f; 243 int16_t ss; 244 ss = ((int16_t)(s[0] ^ 0x80)) * 256; 245 s = audio_stream_add_outp(this->src, s, 1); 246 f = pcm2adpcm_step(mc, ss); 247 ss = ((int16_t)(s[0] ^ 0x80)) * 256; 248 f |= pcm2adpcm_step(mc, ss) << 4; 249 *d = f; 250 d = audio_stream_add_inp(dst, d, 1); 251 s = audio_stream_add_outp(this->src, s, 1); 252 } 253 } 254 dst->inp = d; 255 this->src->outp = s; 256 return 0; 257 } 258 259 /* 260 * OkiADPCM -> signed 16bit linear PCM 261 */ 262 static inline int16_t 263 adpcm2pcm_step(struct msm6258_codecvar *mc, uint8_t b) 264 { 265 int estim = (int)mc->mc_estim; 266 267 mc->mc_amp += adpcm_estim[estim] * adpcm_estimindex[b]; 268 estim += adpcm_estimstep[b]; 269 270 if (estim < 0) 271 estim = 0; 272 else if (estim > 48) 273 estim = 48; 274 275 mc->mc_estim = estim; 276 277 return mc->mc_amp; 278 } 279 280 DEFINE_FILTER(msm6258_adpcm_to_slinear16) 281 { 282 stream_filter_t *this; 283 struct msm6258_codecvar *mc; 284 uint8_t *d; 285 const uint8_t *s; 286 int m, err, enc_dst; 287 288 this = (stream_filter_t *)self; 289 mc = (struct msm6258_codecvar *)self; 290 max_used = (max_used + 3) & ~3; /* round up multiple of 4 */ 291 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used / 4))) 292 return err; 293 m = (dst->end - dst->start) & ~3; 294 m = min(m, max_used); 295 d = dst->inp; 296 s = this->src->outp; 297 enc_dst = dst->param.encoding; 298 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) { 299 while (dst->used < m && this->src->used >= 1) { 300 uint8_t a; 301 int16_t s1, s2; 302 a = s[0]; 303 s1 = adpcm2pcm_step(mc, a & 0x0f); 304 s2 = adpcm2pcm_step(mc, a >> 4); 305 #if BYTE_ORDER == LITTLE_ENDIAN 306 *(int16_t*)d = s1; 307 d = audio_stream_add_inp(dst, d, 2); 308 *(int16_t*)d = s2; 309 #else 310 d[0] = s1; 311 d[1] = s1 >> 8; 312 d = audio_stream_add_inp(dst, d, 2); 313 d[0] = s2; 314 d[1] = s2 >> 8; 315 #endif 316 d = audio_stream_add_inp(dst, d, 2); 317 s = audio_stream_add_outp(this->src, s, 1); 318 } 319 } else { 320 while (dst->used < m && this->src->used >= 1) { 321 uint8_t a; 322 int16_t s1, s2; 323 a = s[0]; 324 s1 = adpcm2pcm_step(mc, a & 0x0f); 325 s2 = adpcm2pcm_step(mc, a >> 4); 326 #if BYTE_ORDER == BIG_ENDIAN 327 *(int16_t*)d = s1; 328 d = audio_stream_add_inp(dst, d, 2); 329 *(int16_t*)d = s2; 330 #else 331 d[1] = s1; 332 d[0] = s1 >> 8; 333 d = audio_stream_add_inp(dst, d, 2); 334 d[1] = s2; 335 d[0] = s2 >> 8; 336 #endif 337 d = audio_stream_add_inp(dst, d, 2); 338 s = audio_stream_add_outp(this->src, s, 1); 339 } 340 } 341 dst->inp = d; 342 this->src->outp = s; 343 return 0; 344 } 345 346 DEFINE_FILTER(msm6258_adpcm_to_linear8) 347 { 348 stream_filter_t *this; 349 struct msm6258_codecvar *mc; 350 uint8_t *d; 351 const uint8_t *s; 352 int m, err, enc_dst; 353 354 this = (stream_filter_t *)self; 355 mc = (struct msm6258_codecvar *)self; 356 max_used = (max_used + 1) & ~1; /* round up multiple of 4 */ 357 if ((err = this->prev->fetch_to(asc, this->prev, this->src, max_used / 2))) 358 return err; 359 m = (dst->end - dst->start) & ~1; 360 m = min(m, max_used); 361 d = dst->inp; 362 s = this->src->outp; 363 enc_dst = dst->param.encoding; 364 if (enc_dst == AUDIO_ENCODING_SLINEAR_LE) { 365 while (dst->used < m && this->src->used >= 1) { 366 uint8_t a; 367 int16_t s1, s2; 368 a = s[0]; 369 s1 = adpcm2pcm_step(mc, a & 0x0f); 370 s2 = adpcm2pcm_step(mc, a >> 4); 371 d[0] = s1 / 266; 372 d = audio_stream_add_inp(dst, d, 1); 373 d[0] = s2 / 266; 374 d = audio_stream_add_inp(dst, d, 1); 375 s = audio_stream_add_outp(this->src, s, 1); 376 } 377 } else { 378 while (dst->used < m && this->src->used >= 1) { 379 uint8_t a; 380 int16_t s1, s2; 381 a = s[0]; 382 s1 = adpcm2pcm_step(mc, a & 0x0f); 383 s2 = adpcm2pcm_step(mc, a >> 4); 384 d[0] = (s1 / 266) ^ 0x80; 385 d = audio_stream_add_inp(dst, d, 1); 386 d[0] = (s2 / 266) ^ 0x80; 387 d = audio_stream_add_inp(dst, d, 1); 388 s = audio_stream_add_outp(this->src, s, 1); 389 } 390 } 391 dst->inp = d; 392 this->src->outp = s; 393 return 0; 394 } 395