1 /* 2 * Copyright (c) 2006 Konstantin Dimitrov <kosio.dimitrov@gmail.com> 3 * Copyright (c) 2001 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp> 4 * 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 AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <dev/sound/pcm/sound.h> 31 32 #include <dev/sound/pci/spicds.h> 33 34 MALLOC_DEFINE(M_SPICDS, "spicds", "SPI codec"); 35 36 #define SPICDS_NAMELEN 16 37 struct spicds_info { 38 device_t dev; 39 spicds_ctrl ctrl; 40 void *devinfo; 41 int num; /* number of this device */ 42 unsigned int type; /* codec type */ 43 unsigned int cif; /* Controll data Interface Format (0/1) */ 44 unsigned int format; /* data format and master clock frequency */ 45 unsigned int dvc; /* De-emphasis and Volume Control */ 46 unsigned int left, right; 47 char name[SPICDS_NAMELEN]; 48 struct mtx *lock; 49 }; 50 51 static void 52 spicds_wrbit(struct spicds_info *codec, int bit) 53 { 54 unsigned int cs, cdti; 55 if (codec->cif) 56 cs = 1; 57 else 58 cs = 0; 59 if (bit) 60 cdti = 1; 61 else 62 cdti = 0; 63 codec->ctrl(codec->devinfo, cs, 0, cdti); 64 DELAY(1); 65 codec->ctrl(codec->devinfo, cs, 1, cdti); 66 DELAY(1); 67 68 return; 69 } 70 71 static void 72 spicds_wrcd(struct spicds_info *codec, int reg, u_int16_t val) 73 { 74 int mask; 75 76 #if(0) 77 device_printf(codec->dev, "spicds_wrcd(codec, 0x%02x, 0x%02x)\n", reg, val); 78 #endif 79 /* start */ 80 if (codec->cif) 81 codec->ctrl(codec->devinfo, 1, 1, 0); 82 else 83 codec->ctrl(codec->devinfo, 0, 1, 0); 84 DELAY(1); 85 if (codec->type != SPICDS_TYPE_WM8770) { 86 if (codec->type == SPICDS_TYPE_AK4381) { 87 /* AK4381 chip address */ 88 spicds_wrbit(codec, 0); 89 spicds_wrbit(codec, 1); 90 } 91 else if (codec->type == SPICDS_TYPE_AK4396) 92 { 93 /* AK4396 chip address */ 94 spicds_wrbit(codec, 0); 95 spicds_wrbit(codec, 0); 96 } 97 else { 98 /* chip address */ 99 spicds_wrbit(codec, 1); 100 spicds_wrbit(codec, 0); 101 } 102 /* write */ 103 spicds_wrbit(codec, 1); 104 /* register address */ 105 for (mask = 0x10; mask != 0; mask >>= 1) 106 spicds_wrbit(codec, reg & mask); 107 /* data */ 108 for (mask = 0x80; mask != 0; mask >>= 1) 109 spicds_wrbit(codec, val & mask); 110 /* stop */ 111 DELAY(1); 112 } 113 else { 114 /* register address */ 115 for (mask = 0x40; mask != 0; mask >>= 1) 116 spicds_wrbit(codec, reg & mask); 117 /* data */ 118 for (mask = 0x100; mask != 0; mask >>= 1) 119 spicds_wrbit(codec, val & mask); 120 /* stop */ 121 DELAY(1); 122 } 123 if (codec->cif) { 124 codec->ctrl(codec->devinfo, 0, 1, 0); 125 DELAY(1); 126 codec->ctrl(codec->devinfo, 1, 1, 0); 127 } 128 else { 129 codec->ctrl(codec->devinfo, 1, 1, 0); 130 } 131 132 return; 133 } 134 135 struct spicds_info * 136 spicds_create(device_t dev, void *devinfo, int num, spicds_ctrl ctrl) 137 { 138 struct spicds_info *codec; 139 140 #if(0) 141 device_printf(dev, "spicds_create(dev, devinfo, %d, ctrl)\n", num); 142 #endif 143 codec = (struct spicds_info *)malloc(sizeof *codec, M_SPICDS, M_NOWAIT); 144 if (codec == NULL) 145 return NULL; 146 147 snprintf(codec->name, SPICDS_NAMELEN, "%s:spicds%d", device_get_nameunit(dev), num); 148 codec->lock = snd_mtxcreate(codec->name, codec->name); 149 codec->dev = dev; 150 codec->ctrl = ctrl; 151 codec->devinfo = devinfo; 152 codec->num = num; 153 codec->type = SPICDS_TYPE_AK4524; 154 codec->cif = 0; 155 codec->format = AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X; 156 codec->dvc = AK452X_DVC_DEMOFF | AK452X_DVC_ZTM1024 | AK452X_DVC_ZCE; 157 158 return codec; 159 } 160 161 void 162 spicds_destroy(struct spicds_info *codec) 163 { 164 snd_mtxfree(codec->lock); 165 free(codec, M_SPICDS); 166 } 167 168 void 169 spicds_settype(struct spicds_info *codec, unsigned int type) 170 { 171 snd_mtxlock(codec->lock); 172 codec->type = type; 173 snd_mtxunlock(codec->lock); 174 } 175 176 void 177 spicds_setcif(struct spicds_info *codec, unsigned int cif) 178 { 179 snd_mtxlock(codec->lock); 180 codec->cif = cif; 181 snd_mtxunlock(codec->lock); 182 } 183 184 void 185 spicds_setformat(struct spicds_info *codec, unsigned int format) 186 { 187 snd_mtxlock(codec->lock); 188 codec->format = format; 189 snd_mtxunlock(codec->lock); 190 } 191 192 void 193 spicds_setdvc(struct spicds_info *codec, unsigned int dvc) 194 { 195 snd_mtxlock(codec->lock); 196 codec->dvc = dvc; 197 snd_mtxunlock(codec->lock); 198 } 199 200 void 201 spicds_init(struct spicds_info *codec) 202 { 203 #if(0) 204 device_printf(codec->dev, "spicds_init(codec)\n"); 205 #endif 206 snd_mtxlock(codec->lock); 207 if (codec->type == SPICDS_TYPE_AK4524 ||\ 208 codec->type == SPICDS_TYPE_AK4528) { 209 /* power off */ 210 spicds_wrcd(codec, AK4524_POWER, 0); 211 /* set parameter */ 212 spicds_wrcd(codec, AK4524_FORMAT, codec->format); 213 spicds_wrcd(codec, AK4524_DVC, codec->dvc); 214 /* power on */ 215 spicds_wrcd(codec, AK4524_POWER, AK452X_POWER_PWDA | AK452X_POWER_PWAD | AK452X_POWER_PWVR); 216 /* free reset register */ 217 spicds_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD); 218 } 219 if (codec->type == SPICDS_TYPE_WM8770) { 220 /* WM8770 init values are taken from ALSA */ 221 /* These come first to reduce init pop noise */ 222 spicds_wrcd(codec, 0x1b, 0x044); /* ADC Mux (AC'97 source) */ 223 spicds_wrcd(codec, 0x1c, 0x00B); /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */ 224 spicds_wrcd(codec, 0x1d, 0x009); /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */ 225 226 spicds_wrcd(codec, 0x18, 0x000); /* All power-up */ 227 228 spicds_wrcd(codec, 0x16, 0x122); /* I2S, normal polarity, 24bit */ 229 spicds_wrcd(codec, 0x17, 0x022); /* 256fs, slave mode */ 230 231 spicds_wrcd(codec, 0x19, 0x000); /* -12dB ADC/L */ 232 spicds_wrcd(codec, 0x1a, 0x000); /* -12dB ADC/R */ 233 } 234 if (codec->type == SPICDS_TYPE_AK4358) 235 spicds_wrcd(codec, 0x00, 0x07); /* I2S, 24bit, power-up */ 236 if (codec->type == SPICDS_TYPE_AK4381) 237 spicds_wrcd(codec, 0x00, 0x8f); /* I2S, 24bit, power-up */ 238 if (codec->type == SPICDS_TYPE_AK4396) 239 spicds_wrcd(codec, 0x00, 0x07); /* I2S, 24bit, power-up */ 240 snd_mtxunlock(codec->lock); 241 } 242 243 void 244 spicds_reinit(struct spicds_info *codec) 245 { 246 snd_mtxlock(codec->lock); 247 if (codec->type != SPICDS_TYPE_WM8770) { 248 /* reset */ 249 spicds_wrcd(codec, AK4524_RESET, 0); 250 /* set parameter */ 251 spicds_wrcd(codec, AK4524_FORMAT, codec->format); 252 spicds_wrcd(codec, AK4524_DVC, codec->dvc); 253 /* free reset register */ 254 spicds_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD); 255 } 256 else { 257 /* WM8770 reinit */ 258 /* AK4358 reinit */ 259 /* AK4381 reinit */ 260 } 261 snd_mtxunlock(codec->lock); 262 } 263 264 void 265 spicds_set(struct spicds_info *codec, int dir, unsigned int left, unsigned int right) 266 { 267 #if(0) 268 device_printf(codec->dev, "spicds_set(codec, %d, %d, %d)\n", dir, left, right); 269 #endif 270 snd_mtxlock(codec->lock); 271 if (left >= 100) 272 if ((codec->type == SPICDS_TYPE_AK4381) || \ 273 (codec->type == SPICDS_TYPE_AK4396)) 274 left = 255; 275 else 276 left = 127; 277 else 278 switch (codec->type) { 279 case SPICDS_TYPE_WM8770: 280 left = left + 27; 281 break; 282 case SPICDS_TYPE_AK4381 || SPICDS_TYPE_AK4396: 283 left = left * 255 / 100; 284 break; 285 default: 286 left = left * 127 / 100; 287 } 288 if (right >= 100) 289 if ((codec->type == SPICDS_TYPE_AK4381) || \ 290 (codec->type == SPICDS_TYPE_AK4396)) 291 right = 255; 292 else 293 right = 127; 294 else 295 switch (codec->type) { 296 case SPICDS_TYPE_WM8770: 297 right = right + 27; 298 break; 299 case SPICDS_TYPE_AK4381: 300 case SPICDS_TYPE_AK4396: 301 right = right * 255 / 100; 302 break; 303 default: 304 right = right * 127 / 100; 305 } 306 if (dir == PCMDIR_REC && codec->type == SPICDS_TYPE_AK4524) { 307 #if(0) 308 device_printf(codec->dev, "spicds_set(): AK4524(REC) %d/%d\n", left, right); 309 #endif 310 spicds_wrcd(codec, AK4524_LIPGA, left); 311 spicds_wrcd(codec, AK4524_RIPGA, right); 312 } 313 if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4524) { 314 #if(0) 315 device_printf(codec->dev, "spicds_set(): AK4524(PLAY) %d/%d\n", left, right); 316 #endif 317 spicds_wrcd(codec, AK4524_LOATT, left); 318 spicds_wrcd(codec, AK4524_ROATT, right); 319 } 320 if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4528) { 321 #if(0) 322 device_printf(codec->dev, "spicds_set(): AK4528(PLAY) %d/%d\n", left, right); 323 #endif 324 spicds_wrcd(codec, AK4528_LOATT, left); 325 spicds_wrcd(codec, AK4528_ROATT, right); 326 } 327 if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_WM8770) { 328 #if(0) 329 device_printf(codec->dev, "spicds_set(): WM8770(PLAY) %d/%d\n", left, right); 330 #endif 331 spicds_wrcd(codec, WM8770_AOATT_L1, left | WM8770_AOATT_UPDATE); 332 spicds_wrcd(codec, WM8770_AOATT_R1, right | WM8770_AOATT_UPDATE); 333 } 334 if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4358) { 335 #if(0) 336 device_printf(codec->dev, "spicds_set(): AK4358(PLAY) %d/%d\n", left, right); 337 #endif 338 spicds_wrcd(codec, AK4358_LO1ATT, left | AK4358_OATT_ENABLE); 339 spicds_wrcd(codec, AK4358_RO1ATT, right | AK4358_OATT_ENABLE); 340 } 341 if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4381) { 342 #if(0) 343 device_printf(codec->dev, "spicds_set(): AK4381(PLAY) %d/%d\n", left, right); 344 #endif 345 spicds_wrcd(codec, AK4381_LOATT, left); 346 spicds_wrcd(codec, AK4381_ROATT, right); 347 } 348 349 if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4396) { 350 #if(0) 351 device_printf(codec->dev, "spicds_set(): AK4396(PLAY) %d/%d\n", left, right); 352 #endif 353 spicds_wrcd(codec, AK4396_LOATT, left); 354 spicds_wrcd(codec, AK4396_ROATT, right); 355 } 356 357 snd_mtxunlock(codec->lock); 358 } 359 360 MODULE_DEPEND(snd_spicds, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 361 MODULE_VERSION(snd_spicds, 1); 362