ac97_codec.c
来自「linux 内核源代码」· C语言 代码 · 共 1,489 行 · 第 1/3 页
C
1,489 行
/* else, write the first set in the mask as the output */ /* clear out current set value first (AC97 supports only 1 input!) */ val = (1 << ac97_rm2oss[codec->codec_read(codec, AC97_RECORD_SELECT) & 0x07]); if (mask != val) mask &= ~val; val = ffs(mask); val = ac97_oss_rm[val-1]; val |= val << 8; /* set both channels */#ifdef DEBUG printk("ac97_codec: setting ac97 recmask to 0x%04x\n", val);#endif codec->codec_write(codec, AC97_RECORD_SELECT, val); return 0;};static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg){ int i, val = 0; if (cmd == SOUND_MIXER_INFO) { mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, codec->name, sizeof(info.id)); strlcpy(info.name, codec->name, sizeof(info.name)); info.modify_counter = codec->modcnt; if (copy_to_user((void __user *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; memset(&info, 0, sizeof(info)); strlcpy(info.id, codec->name, sizeof(info.id)); strlcpy(info.name, codec->name, sizeof(info.name)); if (copy_to_user((void __user *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) return -EINVAL; if (cmd == OSS_GETVERSION) return put_user(SOUND_VERSION, (int __user *)arg); if (_SIOC_DIR(cmd) == _SIOC_READ) { switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* give them the current record source */ if (!codec->recmask_io) { val = 0; } else { val = codec->recmask_io(codec, 1, 0); } break; case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ val = codec->supported_mixers; break; case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ val = codec->record_sources; break; case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ val = codec->stereo_mixers; break; case SOUND_MIXER_CAPS: val = SOUND_CAP_EXCL_INPUT; break; default: /* read a specific mixer */ i = _IOC_NR(cmd); if (!supported_mixer(codec, i)) return -EINVAL; /* do we ever want to touch the hardware? */ /* val = codec->read_mixer(codec, i); */ val = codec->mixer_state[i]; break; } return put_user(val, (int __user *)arg); } if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) { codec->modcnt++; if (get_user(val, (int __user *)arg)) return -EFAULT; switch (_IOC_NR(cmd)) { case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ if (!codec->recmask_io) return -EINVAL; if (!val) return 0; if (!(val &= codec->record_sources)) return -EINVAL; codec->recmask_io(codec, 0, val); return 0; default: /* write a specific mixer */ i = _IOC_NR(cmd); if (!supported_mixer(codec, i)) return -EINVAL; ac97_set_mixer(codec, i, val); return 0; } } return -EINVAL;}/* entry point for /proc/driver/controller_vendor/ac97/%d */int ac97_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data){ int len = 0, cap, extid, val, id1, id2; struct ac97_codec *codec; int is_ac97_20 = 0; if ((codec = data) == NULL) return -ENODEV; id1 = codec->codec_read(codec, AC97_VENDOR_ID1); id2 = codec->codec_read(codec, AC97_VENDOR_ID2); len += sprintf (page+len, "Vendor name : %s\n", codec->name); len += sprintf (page+len, "Vendor id : %04X %04X\n", id1, id2); extid = codec->codec_read(codec, AC97_EXTENDED_ID); extid &= ~((1<<2)|(1<<4)|(1<<5)|(1<<10)|(1<<11)|(1<<12)|(1<<13)); len += sprintf (page+len, "AC97 Version : %s\n", extid ? "2.0 or later" : "1.0"); if (extid) is_ac97_20 = 1; cap = codec->codec_read(codec, AC97_RESET); len += sprintf (page+len, "Capabilities :%s%s%s%s%s%s\n", cap & 0x0001 ? " -dedicated MIC PCM IN channel-" : "", cap & 0x0002 ? " -reserved1-" : "", cap & 0x0004 ? " -bass & treble-" : "", cap & 0x0008 ? " -simulated stereo-" : "", cap & 0x0010 ? " -headphone out-" : "", cap & 0x0020 ? " -loudness-" : ""); val = cap & 0x00c0; len += sprintf (page+len, "DAC resolutions :%s%s%s\n", " -16-bit-", val & 0x0040 ? " -18-bit-" : "", val & 0x0080 ? " -20-bit-" : ""); val = cap & 0x0300; len += sprintf (page+len, "ADC resolutions :%s%s%s\n", " -16-bit-", val & 0x0100 ? " -18-bit-" : "", val & 0x0200 ? " -20-bit-" : ""); len += sprintf (page+len, "3D enhancement : %s\n", ac97_stereo_enhancements[(cap >> 10) & 0x1f]); val = codec->codec_read(codec, AC97_GENERAL_PURPOSE); len += sprintf (page+len, "POP path : %s 3D\n" "Sim. stereo : %s\n" "3D enhancement : %s\n" "Loudness : %s\n" "Mono output : %s\n" "MIC select : %s\n" "ADC/DAC loopback : %s\n", val & 0x8000 ? "post" : "pre", val & 0x4000 ? "on" : "off", val & 0x2000 ? "on" : "off", val & 0x1000 ? "on" : "off", val & 0x0200 ? "MIC" : "MIX", val & 0x0100 ? "MIC2" : "MIC1", val & 0x0080 ? "on" : "off"); extid = codec->codec_read(codec, AC97_EXTENDED_ID); cap = extid; len += sprintf (page+len, "Ext Capabilities :%s%s%s%s%s%s%s\n", cap & 0x0001 ? " -var rate PCM audio-" : "", cap & 0x0002 ? " -2x PCM audio out-" : "", cap & 0x0008 ? " -var rate MIC in-" : "", cap & 0x0040 ? " -PCM center DAC-" : "", cap & 0x0080 ? " -PCM surround DAC-" : "", cap & 0x0100 ? " -PCM LFE DAC-" : "", cap & 0x0200 ? " -slot/DAC mappings-" : ""); if (is_ac97_20) { len += sprintf (page+len, "Front DAC rate : %d\n", codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)); } return len;}/** * codec_id - Turn id1/id2 into a PnP string * @id1: Vendor ID1 * @id2: Vendor ID2 * @buf: CODEC_ID_BUFSZ byte buffer * * Fills buf with a zero terminated PnP ident string for the id1/id2 * pair. For convenience the return is the passed in buffer pointer. */ static char *codec_id(u16 id1, u16 id2, char *buf){ if(id1&0x8080) { snprintf(buf, CODEC_ID_BUFSZ, "0x%04x:0x%04x", id1, id2); } else { buf[0] = (id1 >> 8); buf[1] = (id1 & 0xFF); buf[2] = (id2 >> 8); snprintf(buf+3, CODEC_ID_BUFSZ - 3, "%d", id2&0xFF); } return buf;} /** * ac97_check_modem - Check if the Codec is a modem * @codec: codec to check * * Return true if the device is an AC97 1.0 or AC97 2.0 modem */ static int ac97_check_modem(struct ac97_codec *codec){ /* Check for an AC97 1.0 soft modem (ID1) */ if(codec->codec_read(codec, AC97_RESET) & 2) return 1; /* Check for an AC97 2.x soft modem */ codec->codec_write(codec, AC97_EXTENDED_MODEM_ID, 0L); if(codec->codec_read(codec, AC97_EXTENDED_MODEM_ID) & 1) return 1; return 0;}/** * ac97_alloc_codec - Allocate an AC97 codec * * Returns a new AC97 codec structure. AC97 codecs may become * refcounted soon so this interface is needed. Returns with * one reference taken. */ struct ac97_codec *ac97_alloc_codec(void){ struct ac97_codec *codec = kzalloc(sizeof(struct ac97_codec), GFP_KERNEL); if(!codec) return NULL; spin_lock_init(&codec->lock); INIT_LIST_HEAD(&codec->list); return codec;}EXPORT_SYMBOL(ac97_alloc_codec);/** * ac97_release_codec - Release an AC97 codec * @codec: codec to release * * Release an allocated AC97 codec. This will be refcounted in * time but for the moment is trivial. Calls the unregister * handler if the codec is now defunct. */ void ac97_release_codec(struct ac97_codec *codec){ /* Remove from the list first, we don't want to be "rediscovered" */ mutex_lock(&codec_mutex); list_del(&codec->list); mutex_unlock(&codec_mutex); /* * The driver needs to deal with internal * locking to avoid accidents here. */ if(codec->driver) codec->driver->remove(codec, codec->driver); kfree(codec);}EXPORT_SYMBOL(ac97_release_codec);/** * ac97_probe_codec - Initialize and setup AC97-compatible codec * @codec: (in/out) Kernel info for a single AC97 codec * * Reset the AC97 codec, then initialize the mixer and * the rest of the @codec structure. * * The codec_read and codec_write fields of @codec are * required to be setup and working when this function * is called. All other fields are set by this function. * * codec_wait field of @codec can optionally be provided * when calling this function. If codec_wait is not %NULL, * this function will call codec_wait any time it is * necessary to wait for the audio chip to reach the * codec-ready state. If codec_wait is %NULL, then * the default behavior is to call schedule_timeout. * Currently codec_wait is used to wait for AC97 codec * reset to complete. * * Some codecs will power down when a register reset is * performed. We now check for such codecs. * * Returns 1 (true) on success, or 0 (false) on failure. */ int ac97_probe_codec(struct ac97_codec *codec){ u16 id1, id2; u16 audio; int i; char cidbuf[CODEC_ID_BUFSZ]; u16 f; struct list_head *l; struct ac97_driver *d; /* wait for codec-ready state */ if (codec->codec_wait) codec->codec_wait(codec); else udelay(10); /* will the codec power down if register reset ? */ id1 = codec->codec_read(codec, AC97_VENDOR_ID1); id2 = codec->codec_read(codec, AC97_VENDOR_ID2); codec->name = NULL; codec->codec_ops = &null_ops; for (i = 0; i < ARRAY_SIZE(ac97_codec_ids); i++) { if (ac97_codec_ids[i].id == ((id1 << 16) | id2)) { codec->type = ac97_codec_ids[i].id; codec->name = ac97_codec_ids[i].name; codec->codec_ops = ac97_codec_ids[i].ops; codec->flags = ac97_codec_ids[i].flags; break; } } codec->model = (id1 << 16) | id2; if ((codec->flags & AC97_DEFAULT_POWER_OFF) == 0) { /* reset codec and wait for the ready bit before we continue */ codec->codec_write(codec, AC97_RESET, 0L); if (codec->codec_wait) codec->codec_wait(codec); else udelay(10); } /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should * be read zero. * * FIXME: is the following comment outdated? -jgarzik * Probing of AC97 in this way is not reliable, it is not even SAFE !! */ if ((audio = codec->codec_read(codec, AC97_RESET)) & 0x8000) { printk(KERN_ERR "ac97_codec: %s ac97 codec not present\n", (codec->id & 0x2) ? (codec->id&1 ? "4th" : "Tertiary") : (codec->id&1 ? "Secondary": "Primary")); return 0; } /* probe for Modem Codec */ codec->modem = ac97_check_modem(codec); /* enable SPDIF */ f = codec->codec_read(codec, AC97_EXTENDED_STATUS); if((codec->codec_ops == &null_ops) && (f & 4)) codec->codec_ops = &default_digital_ops; /* A device which thinks its a modem but isnt */ if(codec->flags & AC97_DELUDED_MODEM) codec->modem = 0; if (codec->name == NULL) codec->name = "Unknown"; printk(KERN_INFO "ac97_codec: AC97 %s codec, id: %s (%s)\n", codec->modem ? "Modem" : (audio ? "Audio" : ""), codec_id(id1, id2, cidbuf), codec->name); if(!ac97_init_mixer(codec)) return 0; /* * Attach last so the caller can override the mixer * callbacks. */ mutex_lock(&codec_mutex); list_add(&codec->list, &codecs); list_for_each(l, &codec_drivers) { d = list_entry(l, struct ac97_driver, list); if ((codec->model ^ d->codec_id) & d->codec_mask) continue; if(d->probe(codec, d) == 0) { codec->driver = d; break; } } mutex_unlock(&codec_mutex); return 1;}static int ac97_init_mixer(struct ac97_codec *codec){ u16 cap; int i; cap = codec->codec_read(codec, AC97_RESET); /* mixer masks */ codec->supported_mixers = AC97_SUPPORTED_MASK; codec->stereo_mixers = AC97_STEREO_MASK; codec->record_sources = AC97_RECORD_MASK; if (!(cap & 0x04)) codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); if (!(cap & 0x10)) codec->supported_mixers &= ~SOUND_MASK_ALTPCM; /* detect bit resolution */ codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0x2020); if(codec->codec_read(codec, AC97_MASTER_VOL_STEREO) == 0x2020) codec->bit_resolution = 6; else codec->bit_resolution = 5; /* generic OSS to AC97 wrapper */ codec->read_mixer = ac97_read_mixer; codec->write_mixer = ac97_write_mixer; codec->recmask_io = ac97_recmask_io; codec->mixer_ioctl = ac97_mixer_ioctl; /* initialize mixer channel volumes */ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { struct mixer_defaults *md = &mixer_defaults[i]; if (md->mixer == -1) break; if (!supported_mixer(codec, md->mixer)) continue; ac97_set_mixer(codec, md->mixer, md->value); } /* codec specific initialization for 4-6 channel output or secondary codec stuff */ if (codec->codec_ops->init != NULL) { codec->codec_ops->init(codec); } /* * Volume is MUTE only on this device. We have to initialise * it but its useless beyond that. */ if(codec->flags & AC97_NO_PCM_VOLUME) { codec->supported_mixers &= ~SOUND_MASK_PCM; printk(KERN_WARNING "AC97 codec does not have proper volume support.\n"); } return 1;}#define AC97_SIGMATEL_ANALOG 0x6c /* Analog Special */#define AC97_SIGMATEL_DAC2INVERT 0x6e#define AC97_SIGMATEL_BIAS1 0x70#define AC97_SIGMATEL_BIAS2 0x72#define AC97_SIGMATEL_MULTICHN 0x74 /* Multi-Channel programming */#define AC97_SIGMATEL_CIC1 0x76#define AC97_SIGMATEL_CIC2 0x78static int sigmatel_9708_init(struct ac97_codec * codec){ u16 codec72, codec6c; codec72 = codec->codec_read(codec, AC97_SIGMATEL_BIAS2) & 0x8000; codec6c = codec->codec_read(codec, AC97_SIGMATEL_ANALOG); if ((codec72==0) && (codec6c==0)) { codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1000); codec->codec_write(codec, AC97_SIGMATEL_BIAS1, 0xabba); codec->codec_write(codec, AC97_SIGMATEL_BIAS2, 0x0007); } else if ((codec72==0x8000) && (codec6c==0)) { codec->codec_write(codec, AC97_SIGMATEL_CIC1, 0xabba); codec->codec_write(codec, AC97_SIGMATEL_CIC2, 0x1001); codec->codec_write(codec, AC97_SIGMATEL_DAC2INVERT, 0x0008); } else if ((codec72==0x8000) && (codec6c==0x0080)) { /* nothing */ } codec->codec_write(codec, AC97_SIGMATEL_MULTICHN, 0x0000);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?