1 /* 2 * PCM Interface - misc routines 3 * Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz> 4 * 5 * 6 * This library is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Library General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * 20 */ 21 22 #include <sound/driver.h> 23 #include <linux/time.h> 24 #include <sound/core.h> 25 #include <sound/pcm.h> 26 #define SND_PCM_FORMAT_UNKNOWN (-1) 27 28 /* NOTE: "signed" prefix must be given below since the default char is 29 * unsigned on some architectures! 30 */ 31 struct pcm_format_data { 32 unsigned char width; /* bit width */ 33 unsigned char phys; /* physical bit width */ 34 signed char le; /* 0 = big-endian, 1 = little-endian, -1 = others */ 35 signed char signd; /* 0 = unsigned, 1 = signed, -1 = others */ 36 unsigned char silence[8]; /* silence data to fill */ 37 }; 38 39 static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { 40 [SNDRV_PCM_FORMAT_S8] = { 41 .width = 8, .phys = 8, .le = -1, .signd = 1, 42 .silence = {}, 43 }, 44 [SNDRV_PCM_FORMAT_U8] = { 45 .width = 8, .phys = 8, .le = -1, .signd = 0, 46 .silence = { 0x80 }, 47 }, 48 [SNDRV_PCM_FORMAT_S16_LE] = { 49 .width = 16, .phys = 16, .le = 1, .signd = 1, 50 .silence = {}, 51 }, 52 [SNDRV_PCM_FORMAT_S16_BE] = { 53 .width = 16, .phys = 16, .le = 0, .signd = 1, 54 .silence = {}, 55 }, 56 [SNDRV_PCM_FORMAT_U16_LE] = { 57 .width = 16, .phys = 16, .le = 1, .signd = 0, 58 .silence = { 0x00, 0x80 }, 59 }, 60 [SNDRV_PCM_FORMAT_U16_BE] = { 61 .width = 16, .phys = 16, .le = 0, .signd = 0, 62 .silence = { 0x80, 0x00 }, 63 }, 64 [SNDRV_PCM_FORMAT_S24_LE] = { 65 .width = 24, .phys = 32, .le = 1, .signd = 1, 66 .silence = {}, 67 }, 68 [SNDRV_PCM_FORMAT_S24_BE] = { 69 .width = 24, .phys = 32, .le = 0, .signd = 1, 70 .silence = {}, 71 }, 72 [SNDRV_PCM_FORMAT_U24_LE] = { 73 .width = 24, .phys = 32, .le = 1, .signd = 0, 74 .silence = { 0x00, 0x00, 0x80 }, 75 }, 76 [SNDRV_PCM_FORMAT_U24_BE] = { 77 .width = 24, .phys = 32, .le = 0, .signd = 0, 78 .silence = { 0x80, 0x00, 0x00 }, 79 }, 80 [SNDRV_PCM_FORMAT_S32_LE] = { 81 .width = 32, .phys = 32, .le = 1, .signd = 1, 82 .silence = {}, 83 }, 84 [SNDRV_PCM_FORMAT_S32_BE] = { 85 .width = 32, .phys = 32, .le = 0, .signd = 1, 86 .silence = {}, 87 }, 88 [SNDRV_PCM_FORMAT_U32_LE] = { 89 .width = 32, .phys = 32, .le = 1, .signd = 0, 90 .silence = { 0x00, 0x00, 0x00, 0x80 }, 91 }, 92 [SNDRV_PCM_FORMAT_U32_BE] = { 93 .width = 32, .phys = 32, .le = 0, .signd = 0, 94 .silence = { 0x80, 0x00, 0x00, 0x00 }, 95 }, 96 [SNDRV_PCM_FORMAT_FLOAT_LE] = { 97 .width = 32, .phys = 32, .le = 1, .signd = -1, 98 .silence = {}, 99 }, 100 [SNDRV_PCM_FORMAT_FLOAT_BE] = { 101 .width = 32, .phys = 32, .le = 0, .signd = -1, 102 .silence = {}, 103 }, 104 [SNDRV_PCM_FORMAT_FLOAT64_LE] = { 105 .width = 64, .phys = 64, .le = 1, .signd = -1, 106 .silence = {}, 107 }, 108 [SNDRV_PCM_FORMAT_FLOAT64_BE] = { 109 .width = 64, .phys = 64, .le = 0, .signd = -1, 110 .silence = {}, 111 }, 112 [SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE] = { 113 .width = 32, .phys = 32, .le = 1, .signd = -1, 114 .silence = {}, 115 }, 116 [SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE] = { 117 .width = 32, .phys = 32, .le = 0, .signd = -1, 118 .silence = {}, 119 }, 120 [SNDRV_PCM_FORMAT_MU_LAW] = { 121 .width = 8, .phys = 8, .le = -1, .signd = -1, 122 .silence = { 0x7f }, 123 }, 124 [SNDRV_PCM_FORMAT_A_LAW] = { 125 .width = 8, .phys = 8, .le = -1, .signd = -1, 126 .silence = { 0x55 }, 127 }, 128 [SNDRV_PCM_FORMAT_IMA_ADPCM] = { 129 .width = 4, .phys = 4, .le = -1, .signd = -1, 130 .silence = {}, 131 }, 132 /* FIXME: the following three formats are not defined properly yet */ 133 [SNDRV_PCM_FORMAT_MPEG] = { 134 .le = -1, .signd = -1, 135 }, 136 [SNDRV_PCM_FORMAT_GSM] = { 137 .le = -1, .signd = -1, 138 }, 139 [SNDRV_PCM_FORMAT_SPECIAL] = { 140 .le = -1, .signd = -1, 141 }, 142 [SNDRV_PCM_FORMAT_S24_3LE] = { 143 .width = 24, .phys = 24, .le = 1, .signd = 1, 144 .silence = {}, 145 }, 146 [SNDRV_PCM_FORMAT_S24_3BE] = { 147 .width = 24, .phys = 24, .le = 0, .signd = 1, 148 .silence = {}, 149 }, 150 [SNDRV_PCM_FORMAT_U24_3LE] = { 151 .width = 24, .phys = 24, .le = 1, .signd = 0, 152 .silence = { 0x00, 0x00, 0x80 }, 153 }, 154 [SNDRV_PCM_FORMAT_U24_3BE] = { 155 .width = 24, .phys = 24, .le = 0, .signd = 0, 156 .silence = { 0x80, 0x00, 0x00 }, 157 }, 158 [SNDRV_PCM_FORMAT_S20_3LE] = { 159 .width = 20, .phys = 24, .le = 1, .signd = 1, 160 .silence = {}, 161 }, 162 [SNDRV_PCM_FORMAT_S20_3BE] = { 163 .width = 20, .phys = 24, .le = 0, .signd = 1, 164 .silence = {}, 165 }, 166 [SNDRV_PCM_FORMAT_U20_3LE] = { 167 .width = 20, .phys = 24, .le = 1, .signd = 0, 168 .silence = { 0x00, 0x00, 0x08 }, 169 }, 170 [SNDRV_PCM_FORMAT_U20_3BE] = { 171 .width = 20, .phys = 24, .le = 0, .signd = 0, 172 .silence = { 0x08, 0x00, 0x00 }, 173 }, 174 [SNDRV_PCM_FORMAT_S18_3LE] = { 175 .width = 18, .phys = 24, .le = 1, .signd = 1, 176 .silence = {}, 177 }, 178 [SNDRV_PCM_FORMAT_S18_3BE] = { 179 .width = 18, .phys = 24, .le = 0, .signd = 1, 180 .silence = {}, 181 }, 182 [SNDRV_PCM_FORMAT_U18_3LE] = { 183 .width = 18, .phys = 24, .le = 1, .signd = 0, 184 .silence = { 0x00, 0x00, 0x02 }, 185 }, 186 [SNDRV_PCM_FORMAT_U18_3BE] = { 187 .width = 18, .phys = 24, .le = 0, .signd = 0, 188 .silence = { 0x02, 0x00, 0x00 }, 189 }, 190 }; 191 192 193 /** 194 * snd_pcm_format_signed - Check the PCM format is signed linear 195 * @format: the format to check 196 * 197 * Returns 1 if the given PCM format is signed linear, 0 if unsigned 198 * linear, and a negative error code for non-linear formats. 199 */ 200 int snd_pcm_format_signed(snd_pcm_format_t format) 201 { 202 int val; 203 if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) 204 return -EINVAL; 205 if ((val = pcm_formats[format].signd) < 0) 206 return -EINVAL; 207 return val; 208 } 209 210 /** 211 * snd_pcm_format_unsigned - Check the PCM format is unsigned linear 212 * @format: the format to check 213 * 214 * Returns 1 if the given PCM format is unsigned linear, 0 if signed 215 * linear, and a negative error code for non-linear formats. 216 */ 217 int snd_pcm_format_unsigned(snd_pcm_format_t format) 218 { 219 int val; 220 221 val = snd_pcm_format_signed(format); 222 if (val < 0) 223 return val; 224 return !val; 225 } 226 227 /** 228 * snd_pcm_format_linear - Check the PCM format is linear 229 * @format: the format to check 230 * 231 * Returns 1 if the given PCM format is linear, 0 if not. 232 */ 233 int snd_pcm_format_linear(snd_pcm_format_t format) 234 { 235 return snd_pcm_format_signed(format) >= 0; 236 } 237 238 /** 239 * snd_pcm_format_little_endian - Check the PCM format is little-endian 240 * @format: the format to check 241 * 242 * Returns 1 if the given PCM format is little-endian, 0 if 243 * big-endian, or a negative error code if endian not specified. 244 */ 245 int snd_pcm_format_little_endian(snd_pcm_format_t format) 246 { 247 int val; 248 if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) 249 return -EINVAL; 250 if ((val = pcm_formats[format].le) < 0) 251 return -EINVAL; 252 return val; 253 } 254 255 /** 256 * snd_pcm_format_big_endian - Check the PCM format is big-endian 257 * @format: the format to check 258 * 259 * Returns 1 if the given PCM format is big-endian, 0 if 260 * little-endian, or a negative error code if endian not specified. 261 */ 262 int snd_pcm_format_big_endian(snd_pcm_format_t format) 263 { 264 int val; 265 266 val = snd_pcm_format_little_endian(format); 267 if (val < 0) 268 return val; 269 return !val; 270 } 271 272 /** 273 * snd_pcm_format_width - return the bit-width of the format 274 * @format: the format to check 275 * 276 * Returns the bit-width of the format, or a negative error code 277 * if unknown format. 278 */ 279 int snd_pcm_format_width(snd_pcm_format_t format) 280 { 281 int val; 282 if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) 283 return -EINVAL; 284 if ((val = pcm_formats[format].width) == 0) 285 return -EINVAL; 286 return val; 287 } 288 289 /** 290 * snd_pcm_format_physical_width - return the physical bit-width of the format 291 * @format: the format to check 292 * 293 * Returns the physical bit-width of the format, or a negative error code 294 * if unknown format. 295 */ 296 int snd_pcm_format_physical_width(snd_pcm_format_t format) 297 { 298 int val; 299 if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) 300 return -EINVAL; 301 if ((val = pcm_formats[format].phys) == 0) 302 return -EINVAL; 303 return val; 304 } 305 306 /** 307 * snd_pcm_format_size - return the byte size of samples on the given format 308 * @format: the format to check 309 * 310 * Returns the byte size of the given samples for the format, or a 311 * negative error code if unknown format. 312 */ 313 ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) 314 { 315 int phys_width = snd_pcm_format_physical_width(format); 316 if (phys_width < 0) 317 return -EINVAL; 318 return samples * phys_width / 8; 319 } 320 321 /** 322 * snd_pcm_format_silence_64 - return the silent data in 8 bytes array 323 * @format: the format to check 324 * 325 * Returns the format pattern to fill or NULL if error. 326 */ 327 const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) 328 { 329 if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) 330 return NULL; 331 if (! pcm_formats[format].phys) 332 return NULL; 333 return pcm_formats[format].silence; 334 } 335 336 /** 337 * snd_pcm_format_set_silence - set the silence data on the buffer 338 * @format: the PCM format 339 * @data: the buffer pointer 340 * @samples: the number of samples to set silence 341 * 342 * Sets the silence data on the buffer for the given samples. 343 * 344 * Returns zero if successful, or a negative error code on failure. 345 */ 346 int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) 347 { 348 int width; 349 unsigned char *dst, *pat; 350 351 if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) 352 return -EINVAL; 353 if (samples == 0) 354 return 0; 355 width = pcm_formats[format].phys; /* physical width */ 356 pat = pcm_formats[format].silence; 357 if (! width) 358 return -EINVAL; 359 /* signed or 1 byte data */ 360 if (pcm_formats[format].signd == 1 || width <= 8) { 361 unsigned int bytes = samples * width / 8; 362 memset(data, *pat, bytes); 363 return 0; 364 } 365 /* non-zero samples, fill using a loop */ 366 width /= 8; 367 dst = data; 368 #if 0 369 while (samples--) { 370 memcpy(dst, pat, width); 371 dst += width; 372 } 373 #else 374 /* a bit optimization for constant width */ 375 switch (width) { 376 case 2: 377 while (samples--) { 378 memcpy(dst, pat, 2); 379 dst += 2; 380 } 381 break; 382 case 3: 383 while (samples--) { 384 memcpy(dst, pat, 3); 385 dst += 3; 386 } 387 break; 388 case 4: 389 while (samples--) { 390 memcpy(dst, pat, 4); 391 dst += 4; 392 } 393 break; 394 case 8: 395 while (samples--) { 396 memcpy(dst, pat, 8); 397 dst += 8; 398 } 399 break; 400 } 401 #endif 402 return 0; 403 } 404 405 /* [width][unsigned][bigendian] */ 406 static int linear_formats[4][2][2] = { 407 {{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8}, 408 { SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}}, 409 {{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE}, 410 {SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}}, 411 {{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE}, 412 {SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}}, 413 {{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE}, 414 {SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}} 415 }; 416 417 /** 418 * snd_pcm_build_linear_format - return the suitable linear format for the given condition 419 * @width: the bit-width 420 * @unsignd: 1 if unsigned, 0 if signed. 421 * @big_endian: 1 if big-endian, 0 if little-endian 422 * 423 * Returns the suitable linear format for the given condition. 424 */ 425 snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) 426 { 427 if (width & 7) 428 return SND_PCM_FORMAT_UNKNOWN; 429 width = (width / 8) - 1; 430 if (width < 0 || width >= 4) 431 return SND_PCM_FORMAT_UNKNOWN; 432 return linear_formats[width][!!unsignd][!!big_endian]; 433 } 434 435 /** 436 * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields 437 * @runtime: the runtime instance 438 * 439 * Determines the rate_min and rate_max fields from the rates bits of 440 * the given runtime->hw. 441 * 442 * Returns zero if successful. 443 */ 444 int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime) 445 { 446 static unsigned rates[] = { 447 /* ATTENTION: these values depend on the definition in pcm.h! */ 448 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 449 64000, 88200, 96000, 176400, 192000 450 }; 451 int i; 452 for (i = 0; i < (int)ARRAY_SIZE(rates); i++) { 453 if (runtime->hw.rates & (1 << i)) { 454 runtime->hw.rate_min = rates[i]; 455 break; 456 } 457 } 458 for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) { 459 if (runtime->hw.rates & (1 << i)) { 460 runtime->hw.rate_max = rates[i]; 461 break; 462 } 463 } 464 return 0; 465 } 466